c++ - 压缩二进制数组以克服 GPU 内存限制
问题描述
我正在 GPU (CUDA) 上进行物理模拟,并努力解决标准显卡上有限的可用内存量。
问题如下:
我有一个巨大的二进制掩码(> 30 GByte)用作查找表。为了评估特定变量的有效性,我从变量属性生成索引并检查查找表。这是在 GPU 上同时为数百万个变量并行完成的(只需要读取访问权限)。
为了最小化这个二进制掩码的大小以使其适合 GPU 内存,我正在寻找通过索引底层数据仍然允许快速访问的压缩技术(最好由一个负责所有事情的透明容器类)。由于掩码本身包含多个重复的单个位,因此我还希望可以实现高压缩比。
所以我的问题是:
在 nvidia 的 CUDA 实现中是否已经有任何已知的方法可用,或者是否有任何其他默认的 c++ 库可以做到这一点?
解决方案
游程编码
我不知道有任何图书馆可以为您执行此操作,但我可以为您提供如何执行此操作的想法。由于您的掩码包含相同位的许多重复,因此合适的方法是运行长度编码 (RLE)。这个想法是,不是对单个字节进行编码,而是对字节及其长度进行编码:
aaabbbababaaaaaaaa -> 3a,3b,1a,1b,1a,1b,6a
在实践中有很多方法可以实现这一点。我正在研究体素模型压缩,最适合我的方法是使用字节0x00
和0xff
作为转义序列。所以[0x00, N]
编码 N 个零字节,[0xff, N]
编码 N 个填充字节。剩余的字节保持未压缩。或者,您可以使用 zlib 使用 DEFLATE 压缩,我相信这也有 GPU 实现。
获得 O(1) 随机访问
任何类型的压缩技术的问题在于它将数据减少到可变大小,使得随机访问变得不可能。为了解决这个问题,您必须将数据压缩为 1024 字节的块。然后,您可以存储一个指向每个块开头的指针表,允许您随机访问。
显而易见的问题是,您一次只能保留一个块未压缩,并且每次访问不同的块时,您也需要对其进行解压缩。这可能非常昂贵。
解决 O(log n) 随机访问
另一种技术是将数据压缩为八进制树。较高级别的字节的八位代表较低级别的八个字节中的哪些存在,哪些不存在。
0 0 1 1 // Higher-level bitmask representing
/ | | \ // which bytes exist.
0000.0000 0000.0000 0010.1111 1111.1111 // Lower-level bytes.
这里,a1
表示现有的子树,a0
表示缺失的子树。我们可以将这棵树优化为:
0 0 1 1
| \
0010.1111 1111.1111
较高级别的零位表示较低级别的全零数据,因此我们可以优化那些较低级别的数据。通过像这样将数据排列在树中,我们可以以 O(log n) 的复杂度随机访问任何位。这种技术的优点是我们有很多相邻的 1 或 0,这些将被优化掉并在更高级别变成单个位。
请注意,我们也可以优化全为一的子树。为此,我们使用0x00
更高级别的掩码。0x00
掩码不会自然发生,因为它会被优化为更高级别的单个零位。所以我们可以赋予它一些特殊的含义。
推荐阅读
- java - 如何修复 Java appium 中的 DesiredCapabilities 错误?
- c# - 为什么 ReadFromEnumerable 方法不起作用?机器学习网络
- android - Android-Keyboard 随机显示 onResume
- assembly - MOV 从内存中注册不能在 nasm 中使用 BITS 32
- mongodb - 如何同时高效地将数据写入 NoSQL 和 RDBMS
- javascript - jQuery 代码没有运行 $(function(){...}); 但没有它会运行
- php - PHP POST 未填写
- php - 在 Woocommerce 客户处理和完成的电子邮件通知中更改文本
- java - Spring boot - 休眠配置问题(创建名称为“entityManagerFactory”的bean时出错)
- python - 通过遍历键列表并与另一个字典配对来使用键值对填充字典