在NVIDIA CUDA中访问整数值时,良好的内存使用习惯

时间:2016-01-01 15:01:17

标签: memory cuda

我对CUDA编程完全陌生,我想确保我理解一些基本的内存相关原则,因为我对我的一些想法感到有些困惑。

我正在进行模拟,使用数十亿次使用0到50范围内的随机数。

在cuRand填充一个巨大的数组后,随机的0.0到1.0浮点数,我运行一个内核,将所有这些浮点数据转换为所需的整数范围。从我学到的东西,我感觉通过仅使用6位将这些值存储在一个无符号整数上是更好的,因为全局内存的带宽非常低。所以我做到了。

现在我必须存储大约20000个只读的是/否值,这些值将随机访问,让我们根据进入模拟的随机值说出相同的概率。

首先我想到了共享内存。使用一位看起来很棒,直到我意识到一个银行中的信息越多,冲突就越多。所以解决方案似乎是使用无符号短(2Byte-> 40KB total)来表示一个是/否信息,使用最大可用内存,从而最大限度地减少不同线程读取同一个存储区的概率。

另一个想法是使用常量内存和L1缓存。在这里,根据我的学习,这种方法与共享内存完全相反。现在需要通过更多线程读取相同的位置,因此在一个4B库上放置32个值现在是最佳的。

因此,基于总体概率,我应该在共享内存和缓存之间做出决定,共享位置可能更好,因为有很多是/否值,每个块只有几十或几百个线程,不会有很多银行冲突。

但我对这个问题的一般理解是对的吗?与内存相比,处理器真的那么快,优化内存访问是至关重要的,并且在通过<<,>>,| =,& =等操作提取数据时对额外指令的想法并不重要吗?

对于一面文字,我有百万种方法可以让模拟工作,但只有几种方法可以使它成为正确的方法。我不想一次又一次地重复一些愚蠢的错误只是因为我理解得很糟糕。

1 个答案:

答案 0 :(得分:3)

任何CUDA程序员的前两个优化优先级是:

  1. 启动足够的线程(即暴露足够的并行度),使机器有足够的工作来隐藏延迟

  2. 有效利用记忆。

  3. 上面的第二项将根据您使用的GPU内存类型提供各种指导,例如:

    1. 争取合并访问全局内存
    2. 利用缓存和共享内存来优化数据重用
    3. 对于共享内存,争取非银行冲突访问
    4. 对于常量内存,争取统一访问(在给定的访问周期中,从相同位置读取warp中的所有线程)
    5. 考虑使用纹理缓存和其他特殊的GPU功能。
    6. 你的问题集中在记忆上:

        

      处理器与内存相比真的那么快,优化内存访问是至关重要的,并且在通过&lt;&lt;,&gt;&gt;,| =,&amp; =等操作提取数据时对额外指令的想法并不重要?< / p>

      优化内存访问对于拥有在GPU上快速运行的代码通常非常重要。我提供的上述大多数建议/指南并未关注您应该按int(或其他一些字数量)打包多个数据项的想法,但对于内存而言,这样做并不是不可能的 - 绑定代码。 GPU上的许多程序最终都受内存限制(即它们对内存的使用最终是性能限制器,而不是它们对GPU上计算资源的使用)。

      开发人员正在考虑为每个单词数量打包多个数据项的域的示例是在深度学习空间中,特别是在GPU上的卷积神经网络。在某些情况下,开发人员发现他们可以通过以下方式使他们的培训代码更快地运行:

      1. 将数据集存储为GPU内存中的打包half(16位浮点数)
      2. 以压缩格式
      3. 加载数据集
      4. 解压缩(并转换)每个包装的2 half件数量为2 float数量
      5. float数量
      6. 执行计算
      7. float结果转换为half并将其打包
      8. 存储打包数量
      9. 重复下一个培训周期的过程
      10. (有关half数据类型的其他背景知识,请参阅this answer。)

        关键是,有效使用内存 是使代码在GPU上快速运行的高优先级,这可能包括在某些情况下使用打包数量。

        您的案例是否合理可能需要进行基准测试和比较,以确定对您的代码的影响。

        阅读您的问题的其他一些评论:

          

        在这里,根据我所学到的,这种方法与共享内存完全相反。

        1. 在某些方面,共享内存和常量内存的使用与访问模式的观点“相反”。如上所述,有效使用常量内存涉及统一访问。在统一访问的情况下,共享内存也应该运行得很好,因为它具有广播功能。你担心共享内存中的银行冲突是正确的,但是新的GPU上的共享内存具有广播功能,可以在从同一实际位置进行访问时消除银行冲突问题反对同一家银行。关于共享内存的许多其他问题都涵盖了这些细微差别,因此我不会在此进一步讨论。

        2. 由于您将5个整数(0..50)打包到单个int中,您可能还会考虑(可能您已经这样做了)让每个线程执行5个数量的计算。这将消除跨多个线程共享该数据的需要,或者使多个线程读取相同位置的需要。如果您还没有这样做,那么让每个线程执行多个数据点的计算可能会有一些效率。

        3. 如果您想考虑切换到存储4个打包数量(每int)而不是5,那么可能会有一些机会使用SIMD instrinsics,具体取决于您正在做什么那些包装数量。