GPU 加速的查询引擎通常会受到内存和 I/ O 带宽的限制。NVIDIA 硬件的进步 (包括高带宽显存 (HBM) 、NVIDIA NVLink-C2C 和 NVIDIA GB200 NVL4 中采用的专用解压缩引擎) 可提高有效存储容量、加速 CPU 和 GPU 之间的数据传输,并在不消耗流多处理器 (SM) 资源的情况下加速数据访问,从而帮助消除这些瓶颈。
在本文中,我们将展示数据库如何使用这些技术来加速 GPU 查询执行。您将学习高效的 CPU-GPU 数据移动、压缩、分区剪枝以及将数据传输与计算重叠的技术。
GQE 架构概述
GQE (GPU 查询引擎) 是一种参考架构,旨在通过现代 NVIDIA 硬件上的大型数据集以高性能执行 SQL 查询。实际上,GQE 使用 NVIDIA cuDF 和其他 NVIDIA CUDA-X 库,包括 CCCL、nvCOMP 和 nvSHMEM。
GQE 可以帮助查询引擎:
- 将执行移至 GPU。
- 将解压缩移至 nvCOMP。
- 使数据格式对 GPU 友好。
- 弥补在 GPU 上运行时的端到端性能差距。

图 1. SQL 查询流经 GQE 的三个架构层 (查询、数据和执行) ,实现 GPU 加速
在图 1 中,我们通过将 GQE 分解为查询层、数据层和执行层来概述系统设计。它们管理从 SQL 查询和输入数据到硬件级执行的过渡。各层拟合如下。
查询层通过 SQL 解析器和查询优化器补充执行引擎。查询层原生接受 Substrait 计划 (一种开源查询计划格式) ,以便在 GQE 中执行。通过从现有数据库产品中导出查询计划并在 GQE 中运行该计划,Substrait 可以评估 GPU 执行的优势。在图 2 中,Apache DataFusion 将 SQL 字符串转换为 Substrait 计划。GQE 将该计划用作优化的逻辑查询计划,添加特定于 GQE 的优化,并将查询转换为物理计划。
数据层存储和整理用户数据,以便执行程序快速访问。在 GQE 中,存储被抽象为可插拔的专用读取器,可处理不同的数据格式和存储介质,目前支持 GPU 显存、CPU 显存和磁盘。在本文中,我们将重点介绍高性能 GQE 内存表格式,并假设此数据存储在 CPU 内存中。GQE 按需将数据块传输到 GPU,使 GPU 的工作量饱和,而无需将完整数据集存储在 GPU 显存中。当数据块到达 GPU 时,数据层将移交给执行层。
执行层针对数据执行物理查询计划,以生成查询结果。GQE 将物理计划生成为任务图形,该图形定义了执行计划。任务图包含基于开源 NVIDIA cuDF 库 构建的关系运算符,该库可在高度优化的 CUDA C++ 代码中实现运算符。由于数据层会以数据块的形式传输,因此 GQE 可以分解运算符,并作为工作流 CUDA 流同时在这些数据块上执行任务。
总之,GQE 通过 GPU 原生设计释放了硬件的高吞吐量。
数据布局和传输编排
GQE 数据层经过优化,可高效地将数据从主机内存传输到设备内存。我们通过更大限度地提高吞吐量和减少移动的数据量来更大限度地减少数据传输延迟。在下文中,我们概述了内存数据布局和主机到设备传输编排,这有助于更大限度地降低传输延迟。
GQE 设计目标
由于 GQE 基于 cuDF 构建,该设计假设 GPU 内的数据结构为 cuDF 原生表。但是,主机内存布局可以优化 NVIDIA NVLink C2C 和 PCIe 的传输。cudaMemcpy 是标准传输方法。在这种方法中,CPU 会编排 GPU 执行,并在批量传输中复制数据。这也构成了压缩传输的基础。
数据布局
图 2. GQE 的内存表格式将列式数据整理成行组和分区,以便高效传输至 GPU图 2 显示了表格数据布局,该布局按水平细分为多个行组。每个行组由列和封装元数据组成。在行组中,GQE 将列存储为非连续分区。在传输期间,存储层会将一组分区转换为 cuDF 列。因此,数据层从执行层隐藏了压缩和分区剪枝的实现细节。
传输编排
图 3. 跨行组的工作流并行重叠调度、数据传输、解压缩和 GPU 执行,可加快传输速度在图 3 中,我们展示了 CPU 如何编排传输。根据最佳 CUDA 实践,传输使用工作流并行来高效利用硬件组件。流水线传输由多个阶段组成。在压缩的分区数据中,有四个阶段。
- 在第 0 阶段,主机线程执行调度。调度涉及计算要传输的内存范围、分配目标缓冲区,以及调用必要的 CUDA 方法。
- 在第 1 阶段,GPU 执行 H2D 传输。
- 第 2 阶段解压缩数据。
- 第 3 阶段,添加到数据层之外,CUDA 核函数在其中计算查询。
这四个阶段应该重叠。理想情况下,查询运行时等于运行时间最长的阶段,所有剩余阶段均由工作流隐藏。
数据传输优化
快速数据访问在 GQE 实现的性能优势方面发挥着重要作用。采用的主要数据访问优化是压缩和分区剪枝。在下文中,我们将介绍这些优化的工作原理。
压缩
GQE 从压缩中获得两个主要优势:查询数据集容量和查询加速。压缩使查询引擎能够通过减少整体内存占用来扩展可使用给定内存分配进行处理的数据集大小。压缩缓冲区的数据传输与 GPU 的快速解压缩相结合,即使在 NVLink C2C 等快速互连上也能加快传输速度。GQE 使用 GPU 优化格式压缩数据集,与使用传统格式相比,这些格式可提高压缩比并提供更出色的 GPU 解压缩速度。
NVIDIA nvCOMP 库
NVIDIA nvCOMP 是一个用于 GPU 加速压缩和解压缩的库。它提供了一系列标准和 GPU 优化的压缩格式。用户可以从支持的算法中进行选择,以平衡压缩比、压缩和解压缩吞吐量。nvCOMP 可以将 lz4hc 等 CPU 库封装到其高级接口中,从而提供额外的配置选项。GQE 使用 nvCOMP 进行压缩和解压缩例程。
NVIDIA Blackwell 解压缩引擎
NVIDIA 在 NVIDIA Blackwell 架构中推出了新的解压缩引擎 (DE) ,使 nvCOMP 能够快速解压缩基于 LZ77 的格式 (如 LZ4、Snappy 和 Deflate) ,而无需使用 SM 资源。在使用多个 CUDA 流时,使用 DE、SM 内核和 CE 副本进行的解压缩可以完全重叠。
在单个 NVIDIA Blackwell B200 GPU 上,DE 在数据库应用中的速度最高可达 400 GB/s。例如,以 4 倍的压缩比,它可实现约 400 GB/s 的有效主机到设备吞吐量,同时保留 100 GB/s 的 C2C 主机到设备可用带宽。其余带宽可用于传输其他数据,包括在 SM 上解压缩的编码数据。
NVIDIA GQE 的压缩方法
图 4 展示了混合压缩方法,该方法使用轻量级算法 (如 Cascaded) ,尽可能在结构化数据中使用特定模式,并在需要基于 LZ 的算法来实现良好的压缩比时使用 DE。
图 4. nvCOMP 级联格式链的增量编码、运行长度编码和位打包,可高效压缩列式数据在考虑如何压缩给定列时,查询引擎有几个选项。它可能需要用户为每列指定一种算法,但对于非常大的数据库来说,这很麻烦。我们采用的方法是同时尝试使用 LZ4 和 Cascaded。LZ4 是我们处理通用数据的首选,因为与其他仅使用 LZ77 的压缩器相比,它实现了较高的比率,并且由解压缩引擎提供支持。
为确定要使用的压缩算法,我们使用 LZ4 和 Cascaded 算法对数据进行压缩。级联可以实现超快的压缩率,在 B200 上大约为 500 GB/s。这使我们能够在数据加载阶段尝试额外的算法,而不会产生大量开销。
我们使用两种启发式方法来平衡何时使用 Cascaded 和 LZ4:
- Cascaded 和 LZ4 具有不同的压缩比值,这为我们建立了使用该算法的最小值。
- 与选择 LZ4 相比,级联必须实现高于 LZ4 的压缩比。触发器是 LZ4 压缩比的可配置倍数。
我们使用算法选择来帮助平衡 C2C 带宽、DE 和 SM 资源。
分区剪枝
在将数据从 CPU 传输到 GPU 之前,GQE 会使用过滤器剪枝来跳过不影响查询结果的分区。此机制依赖于总结表格内容和 SQL 查询中定义的谓词的元数据。
元数据和存储
GQE 使用区域图来支持滤镜剪枝。将数据加载为内存中的表格时,GQE 会将该表格水平拆分为行组和固定大小的分区,默认为 1000 万行。对于每个分区,GQE 都会计算每列的最小值和最大值,并将此元数据存储为 GPU 显存中的 cuDF 表,因此在运行剪枝时不会成为瓶颈。计算区域图会使 Parquet 的初始加载时间增加约 1%,并且只发生一次,而不是在查询执行期间。
剪枝和任务编排
图 5. 过滤器剪枝根据分区级区域图评估查询谓词,以在传输前跳过不相关的数据,从而减少向 GPU 的数据移动图 5 显示了滤波器剪枝过程。在任务图构建期间,GQE 通过将查询谓词转换为与行组的区域图的比较来获得剪枝表达式。无法生成查询结果的分区将被裁剪。在本示例中,我们对 1 分区进行了剪枝,因为分区图指示存储在此分区中的所有值都小于 9,因此也小于 15,即下界。系统会将其余分区传输至 GPU 显存,并在必要时对其进行解压缩。即使在剪枝后,CPU 内存中的分区不连续 (例如,由于它们包含在多个行组中) ,它们也会被传输并组合到一个连续的内存块中,该块被包装成 GPU 上的 cuDF 表。
GQE 中的滤镜剪枝非常有效。在使用 1 TB 比例数据集的 TPC-H 基准测试中,滤波器剪枝会在全部 22 个查询中跳过 31% 的数据。其影响是端到端提速 1.43%。
对于 1 TB 数据的基准查询,区域图的评估所增加的用度最低,平均为 2.2 毫秒。
数据传输优化
在 GQE 中,我们设想了一种针对分区的新型批量传输优化。
使用 cudaMemcpyBatchAsync 将多个分区分批传输到 GPU,从而减少细粒度分区的用度。批处理还有助于避免交错 CUDA 流造成的延迟。单独传输分区时,从其他流传输可能会延迟下一次核函数启动。在同一批中移动分区可以避免这种延迟。
性能亮点
为评估上述 B200 GPU 在完整 Grace Blackwell 系统中的功能,我们使用 NVIDIA GB200 NVL4 服务器中的两个 B200 GPU 中的一个,在 TPC-H 上以 Scale Factor 1000 (1TB) 对 GQE 进行了基准测试,其中 B200 GPU 通过 NVLink-C2C 连接到 Grace CPU。我们使用 Turin Epyc 9755 CPU 上的 DuckDB 1.4.1 作为基准。每次查询的平均值为 5 次以上的热缓存运行次数,并且在两侧均启用了压缩和剪枝。我们调整每个查询的 GQE 参数,包括并行程度和物理运算符规划。
通过对 l_shipdate 上的线性项表和 o_orderdate 上的订单表进行聚类,并按月对这两个表进行分区,TPC-H 数据集针对分区剪枝和压缩进行了优化。在内部,每个分区分别按 l_orderkey 和 o_orderkey 排序。
在图 6 中,我们显示了 22 个查询的运行时。在 22 个查询中,有 20 个在 GQE 上表现优于 DuckDB,其中 Q11、Q14 和 Q15 的提升最大,分区剪枝和压缩大幅减少了 NVLink C2C 中的数据移动。GQE 展示了借助这些优化,即使是 Q1 和 Q6 等带宽密集型查询也能在 GPU 上快速执行。总之,GQE 在 9.0 秒内运行所有查询,而在单插槽和双插槽配置中,DuckDB 的运行时间分别为 74.0 秒和 70.6 秒。
图 6. 根据 NVIDIA 的测试结果,在超过 90% 的基于 TPC-H 的查询中,单个 GB200 GPU 上的 GQE 在 1TB 比例系数下的表现优于双插槽 AMD Turin CPU 上的 DuckDB
图 7. 与最佳 CPU 配置相比,GQE 可提供 7.5 倍的总体加速,每个查询的速度提升范围从接近奇偶校验到超过 25 倍不等我们在图 7 中展示了加速效果。与 DuckDB 的最佳 CPU 插槽配置相比,GQE 的性能提升高达 25.5 倍,在 22 次查询中的 20 次表现优于 DuckDB,在 17 次查询中达到 3 倍或更高。通过汇总所有查询,GB200 上的 GQE 将总执行时间提高了 7.5 倍。
此博客文章中的测试结果源自 TPC-H 决策支持基准测试,无法与已发布的 TPC-H 结果相美,因为此博客中的测试结果不符合 TPC-H 规范。
将 GQE 最佳实践应用于数据平台
数据库引擎可以通过定向优化将 NVIDIA Grace Blackwell 硬件功能转化为可衡量的查询性能提升。在 GQE 中,分区剪枝和混合压缩可更大限度地减少传输量,而 NVLink-C2C 和 DE 硬件可提高传输吞吐量。这些优化可缩短传输时间,并使用 NVIDIA cuDF、NVIDIA nvCOMP 和其他 CUDA-X 库组合为复杂的查询执行。
在 TPC-H SF1000 上,GQE 实现了比先进 CPU 数据库快 7.5 倍的总执行时间,展示了如何针对现代数据库引擎同时设计数据布局、压缩策略和执行。
利用 GQE 开源参考架构 和设计以及性能优化,并探索 GQE 如何加速您的数据平台。
致谢
作者在此感谢 Tanmay Gujar 为 GQE 做出的技术贡献以及他对本文的回顾。我们还要感谢所有 GQE 贡献者 (高、Yadu Kiran、James Xia、Eyal Soha、Lingyan Yin、Daniel Juenger、Siyuan Lin、Bret Alfieri、Nico Iskos、Zhengru Wang、Rui Bao、Dhruv Sundararaman、Jiachun Li 和 Kate Cheng) 所做的技术贡献。最后,感谢 Nikolay Sakharnykh 和 Nuttiiya Seekhao 的评测。