【发布时间】:2013-06-09 22:07:56
【问题描述】:
我正在设计一组数学函数并在 CPU 和 GPU(带有 CUDA)版本中实现它们。
其中一些函数基于查找表。大多数表占用 4KB,其中一些需要更多。基于查找表的函数接受一个输入,从查找表中选取一个或两个条目,然后通过插值或应用类似技术来计算结果。
我现在的问题是:我应该将这些查找表保存在哪里? CUDA 设备有许多存储值的地方(全局内存、常量内存、纹理内存……)。假设每个表可以由多个线程同时读取,并且输入值以及查找索引在每个 warp 的线程之间可以完全不相关(导致内存访问不相关),那么哪个内存提供最快的访问?
我补充说,这些表格的内容是预先计算的并且完全不变。
编辑
澄清一下:我需要存储大约 10 个不同的 4KB 查找表。无论如何,很高兴知道这种情况下的解决方案是否与例如情况相同。 100 个 4KB 表或带有例如10 个 16KB 的查找表。
【问题讨论】:
-
常量缓存适用于广播情况,即跨 warp 的访问是统一的。如果经线中的所有线程都访问不同的位置但性能会受到影响,它将起作用。共享内存速度很快,大小为 48KB,因此非常合适,但您可能需要它用于其他目的,或者您的代码是无法正常工作的库的一部分。如果您不能使用共享内存,我建议您使用纹理。最好不要在 GPU 上使用任何表(另请参阅 CUDA 数学库),因为 FLOPS 的增长速度超过了 GPU 各代内存带宽的增长速度。
-
感谢 njuffa 的明确解释。我唯一的问题是关于共享内存。如果我没记错的话,这个内存是在同一个 warp 中的线程之间共享的。那么,我应该在所有经线上复制我的表吗?并且这些表在内核终止后会保持不变吗?
-
共享内存在线程块中的所有线程之间共享。所以我担心总共有 40KB 的表存储,您的代码将被限制为每个 SM 的单个线程块。在大多数情况下,最好至少运行两个线程块,因此您可能需要考虑使用混合方案,其中一些表存储在共享内存中(访问次数最多的表),而其他表存储在纹理内存中。纹理内存还具有可以免费获得(低精度)线性插值的优势。您正在实现哪些需要大表的数学函数?
-
@njuffa:我不能在这里详细说明,因为这涉及到我的学术小组正在进行的研究,但我需要具有位可重现的超验函数,如 sin 以及其他一些不那么琐碎的函数。这里的问题是 CUDA 提供的函数集给出的值始终不同于任何其他 CPU 实现返回的值。所以,我正在实现这些,唯一快速准确的方法似乎是使用查找表。纹理提供的自动插值是不行的,因为我需要完全控制插值。
-
位可重现的超越函数是一个非常高的要求,甚至不考虑 GPU。我的经验是,由于编译器生成的代码不同,即使使用像 fdlibm 这样的代码也不能保证按位相同的答案。遗憾的是,您甚至必须重新实现像 sin() 这样的标准数学函数。对于 CUDA 数学函数,我故意避免使用常见的表+多项式风格的算法,因为使用表会从用户代码中占用许多常量和/或共享内存资源,并且浮点运算的扩展性比未来的内存访问要好。跨度>
标签: cuda lookup-tables