首先,我需要描述我正在使用CUDA的应用程序。
这是一个相当标准的热流模拟。我拿了一堆浮动的3D数组(可变的温度和热量,以及常数k值,sar_value和其他几个)并将它们复制到GPU上分配的线性数组中。所有这些都在全球记忆中。
接下来,我启动一个计算热流的内核函数。我使用2维块构造和1D线程构造启动此内核。该块对应于我们正在执行热流计算的模拟立方体的x和y坐标。线程对应z坐标。所有线程/块坐标都是总立方体大小的倍数,以便最大限度地提高性能。
接下来,在每个单元格上执行冗长的计算。所有的数组都是线性的,所以我准备了偏移量来计算z,y和x方向的下一个单元格。大量的空间局部性发生在写入/读取存储器中,因此纹理存储器不是那里的选择。总的来说,每次计算有2次写入大型数组,6次持续大型数组读取(如300 MB浮点数组的一个索引),8个可变大数组读取,6个常量小数组读取(如300个立方根中) MB)。所有这些都发生在两行代码中。我没有包含与单独读取相同内存位置的多次读取,因为我假设它们被缓存。
其次,我将描述我在计算中得到的结果。
我在特斯拉C1060上获得了大约2.25亿个细胞/秒。在大数据集(4千万到6千万个单元)中,我发现每个单元启动1个线程与每2个单元1个线程相比,每4个单元1个线程,一直到某个点,性能没有差异。这向我表明,计算的限制因素是实际的内存提取。当我为多个块启动1个线程时,我减轻了系统上的内存过载,因此每次计算都更快,尽管计算不太平行 - 没有性能增益,+或 - 一个或两个。
我尝试了什么?我已经尝试将我最大的空间局部恒定大数组放入3D纹理内存 - 灾难性的3-4倍减速。我认为常量内存不可行,因为数据访问模式使得大数组中的每个索引只被访问一次或两次,此外,我不一定知道编译时输入的大小。我在大型常量数组上尝试过1D纹理;也不好。
还有什么我可以做的吗?此外,如果你能看到每秒的字节提取次数(2.25亿/秒* 100左右),那么特斯拉C1060的内存带宽大约是10的倍数。为什么记忆是限制因素?我看到有人“铺设”了他们的数据集以进行类似的热流量计算(我认为在“Mint”背后的人的一篇论文中),这是什么意思?
感谢您的回答。请随时在评论部分提出任何问题。
答案 0 :(得分:3)
接下来,在每个单元格上执行冗长的计算。所有的数组都是线性的,所以我准备了偏移量来计算z,y和x方向的下一个单元格。
仔细考虑在warp中你在数组中获得什么类型的访问模式。 warp中的线程应该访问" row-major"中的连续地址。订购。如果你偶然结束了像#34;专栏"访问模式,您的代码将发出比必要更多的内存事务。
CUDA编程指南4.2中的2.2节“线程层次结构”描述了线程索引如何映射到线程ID。检查连续的线程ID是否也导致(至少在很大程度上)连续的数组索引。