【问题标题】:How much memory can I declare as tile_static?我可以将多少内存声明为 tile_static?
【发布时间】:2013-11-18 13:49:57
【问题描述】:

我目前正在平铺我的 c++ AMP 代码。对于每个图块,我有 4096 字节的数据经常被读取,所以我想将其声明为 tile_static。将其分成多个图块是不切实际的,因为每个线程都需要访问所有数据。我的瓦片由 128 个线程组成,因此它们应该在 Nvidia/AMD GPU 上占用 2-4 个扭曲。

我刚刚阅读了以下文章,这似乎表明我只能在每个 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 上 卡,tile_static 内存有 32 个银行(n = 32),它们是这样组织的 连续的 32 位字 (m = 32) 映射到连续的内存库。 请注意,n 可能因硬件而异,m 通常是 32. 在本文的其余部分,我将假设 m 为 32。

这是否意味着我可以为每个 warp 或每个线程声明最多 1024 位?所有 tile_static 变量是否在 warp 之间共享,或者每个 warp 都有自己的副本?

这些问题中有多少是与硬件相关的,如果是,我如何在运行时找出限制?

我已阅读a c++ AMP book cover to cover,虽然我很感谢作者向我介绍了这个主题,但它似乎并没有解决这个问题(或者如果它解决了,我不明白)。

网上有大量关于如何使用 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 语言扩展本身更依赖于硬件/实现......

【问题讨论】:

  • 帖子在哪里说你每个扭曲只能得到 1024 位?
  • @AdeMiller 我添加了一个引用并突出显示了让我认为是这种情况的部分。
  • 我在下面添加了一些说明。 bank 的大小与它允许您访问的内存大小不同。
  • 干杯,这让很多更有意义!

标签: c++ gpu c++-amp


【解决方案1】:

首先,我认为您的意思是 2 x 1024 字节而不是位。瓦片静态内存是按瓦片声明的,而不是按线程或每个扭曲声明的。 warp 实际上只是一种调度结构,用于组织一起执行的线程组,通常以 32 或 64 个为一组,具体取决于架构。为了使调度程序的工作更轻松,您应该使用包含多个线程的切片,这些线程的数量正好是 warp 大小的倍数。

在详细讨论这个问题之前,回顾一下 GPU 的作用是有帮助的 执行构成内核的线程。 GPU由几个 处理器。 AMD 将它们称为计算单元,而 NVIDIA 则称为 他们流式多处理器。每个 CU 调度工作在块或 线束称为经线。当一条经线被阻挡时, CU调度程序可以通过切换到另一个warp来隐藏延迟 而不是等待当前的扭曲。 CU 能够使用这种方法 隐藏与内存访问相关的延迟,前提是 有足够的经纱可用。

本书未对此进行详细介绍的原因之一是因为 C++ AMP 设计为与硬件无关,并且在 DirectX 之上运行。因此,如果您在设计应用程序时考虑到特定的 GPU 细节,它可能会变得不那么可移植。此外,由于在某些情况下 C++ AMP 是在 DX11 之上实现的,因此根本无法获得特定于硬件的信息。 warp 大小、tile_static 内存大小和缓存大小都是这样的例子。与任何书籍一样,我们也有空间限制和出版期限。

但是,您可以对 warp 大小和 tile 内存做出合理的假设。在现代 GPU 上,假设 warp 大小为 32 或 64,并且 tile 静态内存约为 10 KB。如果您真的想针对特定处理器调整代码,则可以使用制造商规范和/或显示适当详细信息的工具。

在 Tesla 上,共享内存是 16KB。在 Fermi 上,共享内存实际上是 64KB,并且可以 配置为具有 16KB 硬件的 48KB 软件管理数据缓存 数据缓存,或其他方式(16KB SW,48KB HW 缓存)。

对于tile_static 数组来说,10 KB 的内存可能看起来并不多,但实际上还有其他压力也会决定 tile 的大小;注册压力之一。您还应该记住,一些非常大的图块通常会导致占用率低,从而导致代码效率低下。

我同意整个记忆库术语令人困惑。 32 位是指内存条的大小,而不是总内存的大小。您可以将银行视为访问机制,而不是总存储。正如上面提到的,每个 32 位存储体都映射到连续的 32 位内存地址。因为每个周期您可以访问一个存储库,所以访问内存的最有效方法是从每个存储库中读取一项。或者,让所有线程读取相同的项目(广播)。本书在性能/优化一章中对此进行了一些讨论。

【讨论】:

    【解决方案2】:

    大约 32kb。如果你达到了限制,当你尝试编译时你会得到一个错误。

    如果你没有收到错误,那你就没事。您应该能够通过声明一个庞大的tile_static 数组来自己测试它,并且您应该收到一条愤怒的消息,告诉您tile_static 的限制是什么。

    【讨论】:

      猜你喜欢
      • 2020-05-24
      • 2014-03-02
      • 1970-01-01
      • 1970-01-01
      • 2015-03-08
      • 1970-01-01
      • 2011-07-27
      • 2020-03-12
      • 1970-01-01
      相关资源
      最近更新 更多