【问题标题】:Several arithmetic operations parallelized in C++Amp在 C++Amp 中并行化的几个算术运算
【发布时间】:2014-01-22 13:18:18
【问题描述】:

我正在尝试使用 C++Amp 并行化卷积滤波器。我希望以下功能开始工作(我不知道如何正确执行):

float* pixel_color[] = new float [16]; 

concurrency::array_view<float, 2> pixels(4, 4, pixel_array), taps(4, 4, myTap4Kernel_array); 
concurrency::array_view<float, 1> pixel(16, pixel_color); // I don't know which data structure to use here

parallel_for_each(
      pixels.extent, [=](concurrency::index<2> idx) restrict(amp)
  {
      int row=idx[0];
      int col=idx[1];

      pixels(row, col) = taps(row, col) * pixels(row, col); 
      pixel[0] += pixels(row, col); 
     });
pixel_color.synchronize(); 

pixels_.at<Pixel>(j, i) = pixel_color 

}

主要问题是我不知道如何正确使用像素结构(并发数据结构在这里使用,因为我不需要所有 16 个元素)。而且我不知道我是否可以通过这种方式安全地添加值。 以下代码不起作用,它没有向像素 [0] 添加适当的值。 我也想定义

concurrency::array_view<float, 2> pixels(4, 4, pixel_array), taps(4, 4, myTap4Kernel_array); 

在方法之外(例如在头文件中)并在 costructor 或其他函数中对其进行初始化(因为这是一个瓶颈,在 CPU 和 GPU 之间复制数据需要大量时间)。有人知道怎么做这个吗?

【问题讨论】:

  • 我不清楚你想在这里做什么。通常,您不会编写仅处理 4x4 数据数组的 C++ AMP 内核。我假设这是一个计算每个像素周围 4x4 卷积掩码结果的函数?您当前的代码不起作用,因为您在 GPU 上运行的 16 个线程中的每一个都在尝试写入像素 [0]。
  • 没错,我想计算每个像素周围4x4蒙版的结果。我的想法是让每个线程计算 16 个像素中的每一个的颜色,然后对所有结果求和,以便接收像素的结果颜色。我想对图像中的每个像素都做同样的事情。

标签: c++ multithreading parallel-processing gpgpu c++-amp


【解决方案1】:

您的方法不对,但是在 GPU 上对数组进行就地操作很棘手,因为您无法保证不同元素的更新顺序。

这是一个非常相似的例子。 ApplyColorSimplifierTiledHelper 方法包含一个 AMP 受限的 parallel_for_each,它为 2D 数组中的每个索引调用 SimplifyIndexTiledSimplifyIndexTiled 根据srcFrame 中相应像素周围的像素值计算destFrame 中每个像素的新值。这解决了代码中存在的竞争条件问题。

此代码来自Codeplex site for the C++ AMP book。 Cartoonizer 案例研究包括使用 C++ AMP 实现的此类图像处理问题的几个示例;阵列、纹理、平铺/非平铺和多 GPU。 C++ AMP book 详细讨论了实现。

void ApplyColorSimplifierTiledHelper(const array<ArgbPackedPixel, 2>& srcFrame,
    array<ArgbPackedPixel, 2>& destFrame, UINT neighborWindow)
{
    const float_3 W(ImageUtils::W);

    assert(neighborWindow <= FrameProcessorAmp::MaxNeighborWindow);

    tiled_extent<FrameProcessorAmp::TileSize, FrameProcessorAmp::TileSize>     
        computeDomain = GetTiledExtent(srcFrame.extent);
    parallel_for_each(computeDomain, [=, &srcFrame, &destFrame]
        (tiled_index<FrameProcessorAmp::TileSize, FrameProcessorAmp::TileSize> idx) 
        restrict(amp)
    {
        SimplifyIndexTiled(srcFrame, destFrame, idx, neighborWindow, W);
    });
}

void SimplifyIndex(const array<ArgbPackedPixel, 2>& srcFrame, array<ArgbPackedPixel,
                   2>& destFrame, index<2> idx, 
                   UINT neighborWindow, const float_3& W) restrict(amp)
{
    const int shift = neighborWindow / 2;
    float sum = 0;
    float_3 partialSum;
    const float standardDeviation = 0.025f;
    const float k = -0.5f / (standardDeviation * standardDeviation);

    const int idxY = idx[0] + shift;         // Corrected index for border offset.
    const int idxX = idx[1] + shift;
    const int y_start = idxY - shift;
    const int y_end = idxY + shift;
    const int x_start = idxX - shift;
    const int x_end = idxX + shift;

    RgbPixel orgClr = UnpackPixel(srcFrame(idxY, idxX));

    for (int y = y_start; y <= y_end; ++y)
        for (int x = x_start; x <= x_end; ++x)
        {
            if (x != idxX || y != idxY) // don't apply filter to the requested index, only to the neighbors
            {
                RgbPixel clr = UnpackPixel(srcFrame(y, x));
                float distance = ImageUtils::GetDistance(orgClr, clr, W);
                float value = concurrency::fast_math::pow(float(M_E), k * distance * distance);
                sum += value;
                partialSum.r += clr.r * value;
                partialSum.g += clr.g * value;
                partialSum.b += clr.b * value;
            }
        }

    RgbPixel newClr;
    newClr.r = static_cast<UINT>(clamp(partialSum.r / sum, 0.0f, 255.0f));
    newClr.g = static_cast<UINT>(clamp(partialSum.g / sum, 0.0f, 255.0f));
    newClr.b = static_cast<UINT>(clamp(partialSum.b / sum, 0.0f, 255.0f));
    destFrame(idxY, idxX) = PackPixel(newClr);
}

代码使用ArgbPackedPixel,这是一种将 8 位 RGB 值打包到 unsigned long 中的机制,因为 C++ AMP 不支持 char。如果您的问题小到可以放入纹理中,那么您可能需要考虑使用它而不是数组,因为打包/解包是在 GPU 上的硬件中实现的,因此实际上是“免费的”,您必须为此付费与额外的计算。 CodePlex 上也有这个实现的例子。

typedef unsigned long ArgbPackedPixel;

struct RgbPixel 
{
    unsigned int r;
    unsigned int g;
    unsigned int b;
};

const int fixedAlpha = 0xFF;

inline ArgbPackedPixel PackPixel(const RgbPixel& rgb) restrict(amp) 
{
    return (rgb.b | (rgb.g << 8) | (rgb.r << 16) | (fixedAlpha << 24));
}


inline RgbPixel UnpackPixel(const ArgbPackedPixel& packedArgb) restrict(amp) 
{
    RgbPixel rgb;
    rgb.b = packedArgb & 0xFF;
    rgb.g = (packedArgb & 0xFF00) >> 8;
    rgb.r = (packedArgb & 0xFF0000) >> 16;
    return rgb;
}

【讨论】:

  • 我正在对图像进行操作,所以也许我提到的纹理结构会对我有所帮助,但我还不知道该怎么做。我更喜欢使用浮点数而不是整数,因为它可以提供更好的分辨率,而且我正在处理单色图像。
  • 如果您只需要一个浮点数来表示每个像素的灰度,那么您几乎可以将 ArgbPackedPixel 替换为浮点数。
  • 这回答了你的问题吗?
  • 其实我已经实现了不需要按特定顺序完成的操作。我的代码计算一个像素的新值,然后将该值存储在输出缓冲区中。如果你愿意,我可以粘贴代码。
猜你喜欢
  • 2017-04-15
  • 2014-09-12
  • 1970-01-01
  • 2012-09-12
  • 2012-10-10
  • 1970-01-01
  • 1970-01-01
  • 2020-09-17
  • 2013-02-19
相关资源
最近更新 更多