【技术DNA】
【智慧场景】
********** | ******************** | ******************** | ******************** | ******************** | ******************** | ******************** | ******************** | ******************** | ******************** | ******************** | ******************** | ******************** | ******************** | ******************** | ***************** | ***************** |
场景 | 自动驾驶 / AR | 语音信号 | 流视频 | GPU 渲染 | 科学、云计算 | 内存缩减 | 科学应用 | 医学图像 | 数据库服务器 | 人工智能图像 | 文本传输 | GAN媒体压缩 | 图像压缩 | 文件同步 | 数据库系统 | 通用数据 |
技术 | 点云压缩 | 稀疏快速傅里叶变换 | 有损视频压缩 | 网格压缩 | 动态选择压缩算法框架 | 分层数据压缩 | 医学图像压缩 | 无损通用压缩 | 人工智能图像压缩 | 短字符串压缩 | GAN 压缩的在线多粒度蒸馏 | 图像压缩 | 文件传输压缩 | 快速随机访问字符串压缩 | 高通量并行无损压缩 | |
开源项目 | SFFT | Ares | LZ4 | DICOM | Brotli | RAISR | AIMCS | OMGD | rsync | FSST | ndzip |
引言
- 无损数据压缩是一种很有前途的软件方法,可以减少加速器集群上科学应用的带宽需求,而不会引入近似误差。合适的压缩器必须能够有效压缩浮点数据,同时使系统互连饱和以避免引入不必要的延迟。
- 在通往百亿亿次的道路上,能源效率正成为高性能计算(High Performance Computing, HPC) 创新的主要驱动力。节点内并行性的快速增长,包括GPU作为通用加速器的出现,大大降低了计算密集型应用程序的能源成本。
- 为了证明数据压缩在加速节点间通信方面的可行性,论文探讨了 GPU 压缩如何提供必要的性能。在ndzip的基础上,提出了ndzip-gpu,这是一种用于 ndzip 的高效 GPU 并行化方案,一种先进的无损浮点压缩器。
背景
并行无损数据压缩的挑战
- 由于可变编码器/解码器状态和可变长度输出流编码的必要性,传统的无损压缩器倾向于支持串行实现。
可变编码器/解码器状态
- 在一般情况下,通过为输入数据构建概率模型并将较短的表示分配给可能的输入,将较长的表示分配给不太可能的输入来实现数据量的无损减少(比如Huffman编码)。解码器必须知道编码器的概率模型才能反转此映射。由于模型通常既不是提前知道的,也不是整个数据流的静态模型,因此对于单程压缩器来说,显式地交换它变得不可行。编码器和解码器都将从先前观察到的未压缩符号构建并不断更新相同的模型。
- 一个高度并行的压缩器必须能够打破这个依赖链,以避免由同步共享状态主导的运行时行为。具有大状态的压缩器(例如字典编码器)在对它的输入空间进行粒度细分是会明显的降低效率。局部去相关方案在这方面更加稳健。
可变长度编码
- 分块数据流的压缩是一个输入并行问题,因为压缩的块长度事先不知道。并行压缩器的线程必须同步才能确定输出流中各个块的位置。有两种基本方法可以避免围绕这种依赖进行序列化:
- 在快速暂存内存中的 k 个并行线程中压缩 k 个块,在屏障之后导出输出位置,最后让每个线程将写入提交到输出流。
- 将整个流压缩到足够大的中间缓冲区,使用前缀和计算所有块的输出位置,并使用单独的压缩步骤最终确定输出流。
专用浮点压缩器
- 浮点二进制表示具有比面向字节的通用压缩器所假定的更大的字长。此外,来自实际应用程序的浮点数据有很多位相同的重复值,这些值很容易进行重复数据删除。因此,传统的字典编码器方法在这类数据上并不是特别有效。
- 源自物理模拟或传感器阵列的密集网格数据往往表现出低频分量,这使得从相邻值进行局部预测是可行的。网格的维数越高,由于每个值的相邻单元数越多,预期的局部相关性就越多。因此,专门的浮点压缩器的构建通常包括以下三个组件:
- 预测器通过字典、哈希表或相邻值从先前编码的点估计数据。
- 差分算子以可逆方式计算值与其预测之间的残差,例如使用 XOR 运算或整数差。
- 残差编码器使用有利于小幅度值的可变长度代码表示残差。算法通常旨在通过诸如游程编码(Run-length encoding, RLE)或算术编码(Arithmetic coding)之类的表示来消除前导零位(leading-zero)。
- 除了 ndzip 算法之外,还有几个著名的基于 CPU 的无损浮点压缩器。fpzip使用洛伦佐预测器来利用1维网格内点的直接邻域的平滑度,压缩使用范围编码器的残差。该方案表现出很高的压缩效率,特别是对于单精度值,但仅限于单线程操作。FPC使用一对基于哈希表的值预测器来压缩非结构化双精度数据流。线程并行 pFPC 变体允许通过处理块中的输入数据来进一步确定压缩吞吐量的优先级。ZFP是一种固定速率有损压缩器,它使用频率变换对多维网格中的浮点值进行去相关。
GPU上的数据压缩
适用于浮点数据的GPU的公开可用的无损数据压缩器比较少,作者在文中列举了几个:
- 通用压缩机。nvCOMP³是适用于英伟达GPU的无损数据压缩框架。它包括众所周知的 LZ4 压缩器的高吞吐量实现和非常适合整数数据的可配置级联压缩管道。
- cudppCompress是一个通用的面向字节的GPU压缩器。它并行化了著名的bzip2压缩器的三个阶段,与类似时代的硬件CPU实现相比,实现了可测量的加速。对应的并行化解压器没有实现。
- 在有些工作中,GPU已成功用作协处理器来加速Burrows-Wheeler变换。LZW和LZSS压缩器还存在并行实现,GPU熵编码有快速Huffman和非对称数字系统(ANS)编码器。
- 专门的浮点压缩机。 MPC是一种用于单精度或双精度浮点数据的非结构化、多变量流的 GPU 压缩方案。两步一维值预测与垂直位打包相结合,这是一种很好地映射到目标硬件的编码方案。
- GFC是一种用于非结构化双精度数据的超快 GPU 压缩器。来自一维预测器的残差通过游程编码前导零位来压缩。与所有其他评估过的压缩器不同,GFC 会生成碎片压缩输出,并在传输回主机时进行压缩。
NDZIP
ndzip 是先进的块压缩器,针对单精度或双精度浮点数据的一维到三维网格。它使用整数洛伦佐变换逼近洛伦佐预测器,这是一种用于多维块的局部去相关的可分离就地操作。残差使用先前在MPC 中发现的垂直位打包方案进行编码,消除了相邻残差位位置的零游程。通过完全在整数域内操作,该算法保证了压缩操作的可逆性以及可移植性。与既定的通用压缩器(例如 Deflate)和专用算法(例如 fpzip或FPC)相比,ndzip 已被证明可以在 CPU 上提供出色的吞吐量,其实现同时利用线程和 SIMD 并行性。而ndzip-gpu压缩器完全再现了 ndzip 的压缩格式。
关于ndzip更多的内容,可以看之前发布的文章OpenHarmony啃论文俱乐部—数据高通量无损压缩方案,里面详细的介绍了ndzip算法,并且包含算法的使用教程。
并行化方案
这部分,我们将介绍并行化方案ndzip-gpu如何能够在多达 768 个线程之间有效地分配变换和残差编码,同时将分支发散和序列化保持在最低限度。我们的目标是在设备上的全局内存缓冲区之间进行压缩和解压缩。
压缩管道概述
并行压缩的输出偏移问题可以通过每个块中的全设备同步或多个内核启动以及通过中间全局暂存缓冲区的往返来解决。ndzip-gpu采用第二种方法,预计全局障碍将部分否定计算繁重的残差编码器中短路评估的好处。
图 1 三级压缩管道
上图详细说明了三级压缩过程。内核1将一个未压缩的块从全局加载到共享内存中,将浮点值转换为其整数表示。然后n维整数洛伦佐变换计算n中的残差在原地传递块数据。残差被分组为 32 个单精度或64个双精度值的序列,并通过垂直位打包进行编码,从而产生一个头字和可变数量的非零列。
分配了一个全局暂存缓冲区,为不可压缩的情况提供了足够的空间。索引空间被细分为块,每个块为所有头字保留一个块,然后为每个位压缩列序列保留一个较小的块。从输入网格的维度,暂存缓冲区中的所有块偏移量都是先验已知的。
编码后,每个线程块将它们各自的块写入暂存内存,并将块长度写入单独的缓冲区。内核2计算长度缓冲区上的并行前缀和,以获得紧凑输出流中所有块的偏移量。最后,使用偏移缓冲区,内核 3 从零内存加载块并将它们存储在输出流中的最终位置。每个块中第一个块的输出偏移量收集在流头中.
图2中可视化的流布局有意将固定大小的元信息(块偏移和块头)与可变长度的压缩列编码分开。这允许解码器并行计算压缩列的绝对偏移量,而无需同步或多次通过流。
图 2 压缩流布局
解压管道概述
由于可以从流标头中检索压缩缓冲区中每个块的偏移量,因此解压缩是输出并行的,并且不需要块之间的同步。单个内核启动足以解码整个流或任意块子集。图3详细说明了单个块的解压过程。
- 内核首先从第一个块中加载所有的头,对设置的位进行计数以获得每个序列的非零列数,最后执行前缀求和以在共享内存中生成偏移表。
- 然后可以并行反转所有块的位打包,扩展到残差的共享内存块。
- 然后通过逆整数洛伦佐变换恢复未压缩值的整数表示。
- 最后,通过反转整数映射来恢复浮点位模式,然后将块写入全局输出网格缓冲区。
图 3 单级解压管道
共享内存布局
必须仔细选择多通道转换步骤的中间结果的共享内存布局,以避免所有必需的访问模式之间的存储库冲突。硬件将根据需要将冲突的加载或存储拆分为尽可能多的无冲突访问,这可以显著增加受共享内存访问限制的函数的运行时间,例如整数洛伦佐变换。这个问题没有明显的通用解决方案,相反,索引空间变换必须分别专门针对一维、二维和三维情况以及单精度和双精度数据。
- 填充。为了确保沿所有轴访问超立方体的连续索引可以映射到不重叠的银行,插入了填充词。由于每个内存块都是32位宽,并且64位加载和存储是作为两个连续的 32 位访问执行的,因此双精度情况下的填充仍然必须是 32 位宽。这需要对 64 位值进行故意未对齐的访问。
- 定向访问顺序。在变换步骤的每个维度中,对通道项目的迭代可以建模为固定步幅的循环。然而,由于每个激活的warp(SM的基本执行单元)同时处理 32 个通道,因此必须明确计算每个通道中第一项的内存偏移量。必须再次小心地对一组通道进行分区以避免存储库冲突。
并行整数洛伦佐变换
n维整数洛伦佐变换,包括正向和逆向,由n个通道组成。在每个定向通道中,可以并行处理L个数据;这些通道分布在线程块的线程之间。
- 前向变换。前向变换在每条车道4096次迭代中构造残差,用与其前任的整数差替换值表示。前驱值在寄存器中进行跟踪,因此该方案只需要在共享内存中的每个数据点执行一次加载和一次存储。
- 逆变换。为了重构值表示,每个逆变换通道必须将已解码的前驱添加到每个残差。由于这引入了一个大小与块边长相等的依赖链,因此最多可以有4096^{1-1/n}个独立通道(1对应1维, 64对应2维,256对应3维)。
由于每个通道的逆变换构成前缀和,因此可以通过采用并行扫描来避免串行化。在实践中,我们通过在连续块内存上使用快速并行前缀和来反转一维变换,并通过对每个通道执行顺序求和来接受二维和三维情况的有限占用。
Warp合作垂直位封装
固定宽度整数序列的垂直位封装已经在数据库系统中看到了先前的应用。这种压缩长度不能被处理器最小可寻址单元分割的位模式的方法在并行硬件上有效地矢量化,例如支持 SIMD 的处理器。
它可以很容易地适应于压缩输入位位置的任意子集,而不是对整数中的连续位进行操作,再次允许在 SIMD 架构上高效实现。在这种形式中,它以前曾作为MPC压缩器的一部分用于 GPU 浮点压缩。在下文中,我们将未压缩的单词称为行,将未压缩序列中相同位置的位称为列。
ndzip-gpu的新颖打包方案通过以下方式显着提高了现代GPU上MPC方法之外的性能:
- 短路评估无线程发散的全零块的昂贵转置步骤。
- 通过避免块来允许独立的前向进展- 在打包期间完全同步。
- 通过将压缩块写入全局暂存缓冲区并在解包期间使用单独的压缩内核。
- 避免围绕输出流位置进行序列化,通过读取粗粒度块偏移量来避免围绕输入流位置进行序列化来自流标头并计算块内的细粒度块偏移作为并行前缀和。
- 包装。在ndzip-gpu编码器中,32 个线程协作打包 32 个 32 位或 64 个 64 位行。图4显示了更简单的32位情况的机制,其中一个字对应一个线程。
图 4 合作垂直位包装
- 解包。解码阶段使用类似的线程分配,如图5中所示的32位情况。首先,每个打包块的长度被确定为其头部的位数(popcount)。根据这些长度,使用线程块并行前缀和计算打包流的偏移量。
图 5 合作垂直位解包
参数调整
由于ndzip格式要求固定块大小,最重要的可调参数是每个块的线程数。这个数字可以独立于实现的其余部分进行选择,并允许用缓存局部性换取更高的占用率,从而提高隐藏指令延迟的能力。
评估
评估方法
将数据集的压缩比定义为压缩大小除以未压缩大小(以字节为单位),较低的比率表示更好的压缩。该定义允许使用未加权算术平均值从一组观察值中对预期压缩比进行有意义的分析。
通过测量从第一个内核开始到最后一个内核结束的设备执行时间来评估压缩器性能。缓冲区分配以及主机设备内存传输不包括在测量范围内。我们报告每秒未压缩字节的吞吐量,它转换为压缩输入和解压缩输出带宽。重复测量每个算法-数据集对,直到总运行时间超过一秒,但至少五次。
所有 CPU 算法都通过测量执行时间来进行基准测试,不包括可以提前执行的所有内存分配。
结果
图6展示了ndzip-gpu提供的吞吐量和压缩比之间的出色权衡。
- 在评估的测试数据上,并行化方案在单精度情况下同时提供了所有测试过的压缩器的最佳平均压缩比和最高吞吐量。
- 对于双精度,GFC 和nvCOMP级联方案超过ndzip-gpu在 RTX 2070 SUPER 和 A100 GPU 上的速度,但是压缩比更差。
- 与GFC和MPC不同,ndzip-gpu显示出压缩和解压缩速度之间的显着差异。这可以用压缩器的多级架构来解释,它需要完整的全局内存往返来进行压缩。
图 6 与Tesla V100上的压缩器/解压缩器吞吐量相比的平均压缩比
表 1 Tesla V100 上的每个 GPU 压缩器实现的每个数据集的压缩率和吞吐量
- 每个数据集的压缩效率。上表列出了每个压缩器在每个数据集上实现的压缩率和吞吐量。虽然ndzip-gpu平均实现了最佳的数据缩减和最高的吞吐量,但某些数据集可以通过竞争对手的算法更有效或更快地压缩。对于大多数输入,ndzip和MPC的比率非常接近,因为这两种算法共享相同的残差编码算法。