我目前正在设置我的c ++ AMP代码。对于每个tile,我有4096个字节的数据,这些数据是经常读取的,所以我想把它声明为tile_static。将其划分为多个区块是不切实际的,因为每个线程都需要访问所有数据。我的磁贴由128个线程组成,因此它们应该在Nvidia / AMD GPU上占用2-4个warp。
我刚刚阅读了以下文章,这似乎表明我只能在每个warp的tile_static中使用1024位: http://blogs.msdn.com/b/nativeconcurrency/archive/2012/08/14/avoid-bank-conflicts-on-tile-static-memory-with-c-amp.aspx
在一些现代GPU上,tile_static存储器由大小相等的“n”组成 存储库可以同时访问,也可以连续访问 “m”位字被映射到连续的存储体。最正确 tile_static存储器(即n和m)的组织是硬件 依赖。例如,在Nvidia GTX 580卡或ATI HD 5870上 card,tile_static memory有 32个银行(n = 32)这样组织 连续的32位字(m = 32)映射到连续的存储库。 请注意,n可能因硬件而异,通常为m 32.在帖子的其余部分,我假设m是32。
这是否意味着我可以为每个warp或每个线程声明最多1024位? warp之间是否共享了tile_static变量,或者每个warp都有自己的副本?
这些问题中有多少与硬件有关,如果是,我如何在运行时找出限制?
我已经阅读了a c++ AMP book cover to cover,虽然我感谢作者向我介绍了这个问题,但它似乎没有解决这个问题(或者如果它确实如此,我不理解它)。 / p>
网上有大量关于如何使用tile_static内存的信息(这是一个好的开始:http://www.danielmoth.com/Blog/tilestatic-Tilebarrier-And-Tiled-Matrix-Multiplication-With-C-AMP.aspx)但似乎没有人谈论我们可以声明多少,使得无法实际实现任何内容这个东西!最后一个链接给出了以下示例:
01: void MatrixMultiplyTiled(vector<float>& vC,
const vector<float>& vA,
const vector<float>& vB, int M, int N, int W)
02: {
03: static const int TS = 16;
04: array_view<const float,2> a(M, W, vA);
05: array_view<const float,2> b(W, N, vB);
06: array_view<float,2> c(M,N,vC); c.discard_data();
07: parallel_for_each(c.extent.tile< TS, TS >(),
08: [=] (tiled_index< TS, TS> t_idx) restrict(amp)
09: {
10: int row = t_idx.local[0]; int col = t_idx.local[1];
11: float sum = 0.0f;
12: for (int i = 0; i < W; i += TS) {
13: tile_static float locA[TS][TS], locB[TS][TS];
14: locA[row][col] = a(t_idx.global[0], col + i);
15: locB[row][col] = b(row + i, t_idx.global[1]);
16: t_idx.barrier.wait();
17: for (int k = 0; k < TS; k++)
18: sum += locA[row][k] * locB[k][col];
19: t_idx.barrier.wait();
20: }
21: c[t_idx.global] = sum;
22: });
23: }
请注意,第13行声明了2x 1024位,这让我希望我的4096位不要求太多......如果有一般c ++ amp或GPU编程经验的人可以帮我解决这个问题会很棒 - 我想这些问题比AMP语言扩展本身更依赖于硬件/实现......
答案 0 :(得分:3)
首先,我认为你的意思是2 x 1024字节而不是位。每个tile声明了tile静态内存,而不是每个线程或每个warp声明。 warp实际上只是一个调度构造,用于组织一起执行的线程组,通常以32或64的组为单位,具体取决于体系结构。为了使调度程序的生活更轻松,您应该使用包含许多线程的tile,这些线程是warp大小的精确倍数。
在详细讨论之前,重新审视GPU是如何帮助的 执行构成内核的线程。 GPU由几个组成 处理器。 AMD将其称为计算单位,而NVIDIA则称其为计算单位 他们流式多处理器。每个CU计划都以块或块的形式工作 被称为warp的线程束。当经线被阻挡时, CU调度程序可以通过切换到另一个warp来隐藏延迟 而不是等待当前的扭曲。 CU可以使用这种方法 隐藏与内存访问相关的延迟,前提是这样 有足够的经线。
本书中没有详细介绍这一点的原因之一是因为C ++ AMP被设计为与硬件无关并且在DirectX之上运行。因此,如果您设计具有特定GPU细节的应用程序,它可能会变得不那么便携。此外,由于在某些情况下C ++ AMP是在DX11之上实现的,因此无法获得硬件特定信息。 warp大小,tile_static
内存大小和高速缓存大小都是这样的例子。与任何一本书一样,我们也有空间限制和出版截止日期。
但是,您可以对warp大小和tile内存做出合理的假设。在现代GPU上,假设warp大小为32或64,并且tile静态内存大约为10s KB。如果您真的想要调整特定处理器的代码,那么您可以使用制造商规格和/或显示相应详细信息的工具。
在特斯拉,共享内存是 16KB。在Fermi上,共享内存实际上是64KB,也可以 配置为48KB软件管理数据缓存,具有16KB硬件 数据缓存,或者相反(16KB SW,48KB硬件缓存)。
对于tile_static
数组而言,10 KB的KB似乎不是很多内存,但实际上还有其他压力也决定了磁贴的大小;注册表压力一个。您还应该记住,一些非常大的磁贴通常会导致占用率低,从而导致代码效率低下。
我同意整个记忆库术语令人困惑。 32位是指存储体的大小,而不是总存储器的大小。您可以将银行视为访问机制,而不是总存储。如上所述,每个32位存储区映射到连续的32位存储器地址。因为每个周期有一个银行访问权限,访问内存的最有效方法是从每个银行读取一个项目。或者,让所有线程读取相同的项目(广播)。本书在性能/优化章节中对此进行了一些讨论。
答案 1 :(得分:0)
关于 32kb 。如果达到限制,则在尝试编译时会出现错误。
如果您没有收到错误,那么您就可以了。你应该能够通过声明一个巨大的tile_static
数组来自己测试它,你应该得到一个愤怒的消息,告诉你tile_static
限制是什么。