CUDA内存用于查找表

7

我正在设计一组数学函数,并在CPU和GPU(使用CUDA)版本中实现它们。

其中一些函数基于查找表。大多数表占用4KB,其中一些略大。基于查找表的函数接受输入,选择一个或两个查找表条目,然后通过插值或应用类似技术计算结果。

我的问题是:这些查找表应该保存在哪里?CUDA设备有许多存储值的位置(全局内存、常量内存、纹理内存等)。假设每个表都可以被许多线程同时读取,并且输入值(因此查找索引)在每个warp的线程之间完全不相关(导致不相关的内存访问),哪种内存提供最快的访问速度?

我补充说明这些表的内容是预先计算的并且完全不变的。

编辑

仅澄清一下:我需要存储约10个不同的4KB查找表。无论如何,了解针对此情况的解决方案是否与具有例如100个4KB表或例如10个16KB查找表的情况相同将是很好的。


4
常量缓存旨在用于广播情况,即跨越一个warp的访问是均匀的。如果warp中的所有线程访问不同的位置,则它将起作用,但性能会受到影响。共享内存速度快,有48KB大小,所以非常适用,但您可能需要将其用于其他目的,或者您的代码是库的一部分,其中共享内存不起作用。如果无法使用共享内存,则建议使用纹理。最好根本不在GPU上使用任何表格(还请参见CUDA数学库),因为FLOPS(每秒浮点运算次数)比GPU代数中的内存带宽增加得更快。 - njuffa
谢谢njuffa 的清晰解释。我唯一的问题是关于共享内存。如果我没记错的话,这段内存在同一个warp中的线程之间是共享的。因此,我应该在所有的warp上复制我的表格吗?而且这些表格在内核终止后是否会保留下来? - Spiros
2
共享内存在线程块中的所有线程之间共享。因此,我担心您的代码在总共40KB的表存储空间中将被限制为每个SM的单个线程块。在大多数情况下,最好至少有两个线程块在运行,因此您可能需要考虑使用混合方案,其中一些表存储在共享内存中(具有最多访问量的表),而其他表存储在纹理内存中。纹理内存还具有一个优点,即您可以免费获得(低精度)线性插值。您正在实现哪些需要大型表格的数学函数? - njuffa
@njuffa:我不能在这里详细说明,因为这涉及到我学术团队的正在进行的研究,但我需要具有位可重现的三角函数(如sin)以及其他一些不太常见的函数。问题在于,CUDA提供的函数集返回的值与任何其他CPU实现返回的值都不同。因此,我正在实现这些函数,唯一快速而准确的方法似乎是使用查找表。纹理提供的自动插值不可行,因为我需要完全控制插值。 - Spiros
2
可重现的超越函数是一个非常高的要求,甚至不考虑GPU。我的经验是,即使使用像fdlibm这样的代码,由于编译器生成的不同代码,也不能保证位逐位相同的答案。令人遗憾的是,您甚至需要重新实现标准数学函数,如sin()。对于CUDA数学函数,我故意避免了常见的表格+多项式样式算法,因为使用表格会从用户代码中取走许多常量和/或共享内存资源,并且浮点运算比内存访问更好地扩展。 - njuffa
显示剩余4条评论
1个回答

2
纹理内存(现在称为只读数据缓存)可能是值得探索的选择,尽管不是为了插值的好处。它支持32位读取而不会超过这个数量。然而,您总共只能使用48K。对于Kepler(计算3.x),现在编程非常简单。
全局内存,除非您将其配置为32位模式,否则每个线程通常会拉入128字节,从而大大增加实际需要从内存中获取的数据量,因为(显然)无法合并内存访问。因此,如果您想使用超过48K(您提到了40K),则32位模式可能是您所需的。
考虑到合并,如果您要连续访问这些表中的一组值,则可以交错这些表,以便可以将这些组合分组并作为每个线程的64位或128位读取进行读取。这意味着来自全局内存的128字节读取可能很有用。
您将遇到的问题是,通过使用查找表,您正在使解决方案受到内存带宽的限制。将L1缓存大小(在Fermi / compute 2.x上)更改为48K可能会产生显着差异,特别是如果您没有使用其他32K的共享内存。尝试使用纹理内存,然后使用32位模式的全局内存,看看哪个对您的算法效果最好。如果有硬件选择,则选择具有良好内存带宽数字的卡。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接