我需要找到有关Unified Shader Array如何访问GPU内存以了解如何有效使用它的信息。我的显卡的架构图像并没有清楚地显示出来。
我需要使用C ++ Amp将大图像加载到GPU内存中并将其分成小块(如4x4像素)。应使用不同的线程计算每个部分。我不知道线程如何共享对图像的访问。
有没有办法以这样的方式做到这一点,即线程在访问图像时没有互相阻塞?也许他们有自己的记忆,可以独家访问?
或许对统一内存的访问速度如此之快以至于我不应该关心它(但我不相信它)?这非常重要,因为我需要为每个图像计算大约10k个子集。
答案 0 :(得分:1)
对于C ++ AMP,您希望在开始卷积计算之前将平铺中每个线程使用的数据加载到tile_static
内存中。因为每个线程访问也被其他线程读取的像素,所以这允许您对来自(慢)全局存储器的每个像素进行单次读取,并将其缓存在(快速)瓦片静态存储器中,以便所有后续读取更快。 / p>
您可以看到example of tiling for convolution here。 DetectEdgeTiled
方法加载它需要的所有数据和调用idx.barrier.wait()
,以确保所有线程都已完成将数据写入tile静态内存。然后它利用tile_static
内存执行边缘检测代码。样本中还有许多其他这种模式的例子。请注意,DetectEdgeTiled
中的加载代码很复杂,因为它必须考虑当前图块中写入的像素边缘周围的附加像素,并且基本上是展开的循环,因此它的长度。
我不确定你是否以正确的方式思考问题。这里有两个级别的分区。为了计算每个像素的新值,执行此工作的线程读取周围像素的块。此外,线程的块(瓦片)将更大的像素数据块加载到tile_static
存储器中。然后,图块上的每个线程都会计算块内一个像素的结果。
void ApplyEdgeDetectionTiledHelper(const array<ArgbPackedPixel, 2>& srcFrame,
array<ArgbPackedPixel, 2>& destFrame)
{
tiled_extent<tileSize, tileSize> computeDomain = GetTiledExtent(srcFrame.extent);
parallel_for_each(computeDomain.tile<tileSize, tileSize>(), [=, &srcFrame, &destFrame, &orgFrame](tiled_index<tileSize, tileSize> idx) restrict(amp)
{
DetectEdgeTiled(idx, srcFrame, destFrame, orgFrame);
});
}
void DetectEdgeTiled(
tiled_index<tileSize, tileSize> idx,
const array<ArgbPackedPixel, 2>& srcFrame,
array<ArgbPackedPixel, 2>& destFrame) restrict(amp)
{
const UINT shift = imageBorderWidth / 2;
const UINT startHeight = 0;
const UINT startWidth = 0;
const UINT endHeight = srcFrame.extent[0];
const UINT endWidth = srcFrame.extent[1];
tile_static RgbPixel localSrc[tileSize + imageBorderWidth ]
[tileSize + imageBorderWidth];
const UINT global_idxY = idx.global[0];
const UINT global_idxX = idx.global[1];
const UINT local_idxY = idx.local[0];
const UINT local_idxX = idx.local[1];
const UINT local_idx_tsY = local_idxY + shift;
const UINT local_idx_tsX = local_idxX + shift;
// Copy image data to tile_static memory. The if clauses are required to deal with threads that own a
// pixel close to the edge of the tile and need to copy additional halo data.
// This pixel
index<2> gNew = index<2>(global_idxY, global_idxX);
localSrc[local_idx_tsY][local_idx_tsX] = UnpackPixel(srcFrame[gNew]);
// Left edge
if (local_idxX < shift)
{
index<2> gNew = index<2>(global_idxY, global_idxX - shift);
localSrc[local_idx_tsY][local_idx_tsX-shift] = UnpackPixel(srcFrame[gNew]);
}
// Right edge
// Top edge
// Bottom edge
// Top Left corner
// Bottom Left corner
// Bottom Right corner
// Top Right corner
// Synchronize all threads so that none of them start calculation before
// all data is copied onto the current tile.
idx.barrier.wait();
// Make sure that the thread is not referring to a border pixel
// for which the filter cannot be applied.
if ((global_idxY >= startHeight + 1 && global_idxY <= endHeight - 1) &&
(global_idxX >= startWidth + 1 && global_idxX <= endWidth - 1))
{
RgbPixel result = Convolution(localSrc, index<2>(local_idx_tsY, local_idx_tsX));
destFrame[index<2>(global_idxY, global_idxX)] = result;
}
}
此代码取自CodePlex,我删除了许多真正的实现,以使其更清晰。
WRT @ sharpneli的回答你可以在C ++ AMP中使用texture<>
来获得与OpenCL图像相同的结果。在CodePlex上还有一个例子。
答案 1 :(得分:0)
在这种特殊情况下,您不必担心。只需使用OpenCL图像。 GPU非常擅长于简单地读取图像(由于纹理化)。但是,此方法需要将结果写入单独的图像,因为您无法在单个内核中读取和写入相同的图像。如果您可以将计算作为单次传递执行(无需迭代),则应使用此方法。
另一种方法是将其作为普通内存缓冲区访问,将波前(同步运行的线程组)中的部分加载到本地内存(此内存非常快),执行计算并将完整的最终结果写回统一计算后的记忆。如果需要在计算时读取和写入同一图像的值,则应使用此方法。如果你没有内存限制,你仍然可以从纹理中读取原始值,然后在本地内存中迭代并将最终结果写入单独的图像中。
统一内存中的读取只有在不是const * restrict并且多个线程读取相同位置时才会很慢。通常,如果后续线程ID读取后续位置,则速度相当快。但是,如果您的线程都写入和读取统一内存,那么它将会很慢。