数据科学

使用 GPU 在 Apache Spark 上加速 JSON 处理

JSON 是一种热门的文本数据格式,可实现 Web 应用程序中系统之间的互操作性以及数据管理。这种格式自 21 世纪初就已存在,源于 Web 服务器和浏览器之间的通信需求。标准 JSON 格式由可包含嵌套对象的键值对组成。JSON 在存储 Web 事务信息方面的使用有所增长,并且可能包含非常大的值,有时每条记录的大小超过 1 GB。首先,解析和验证 JSON 不是与 GPU 加速相关的任务,因为文本格式的大小不规则,并且没有默认顺序。但是,随着 JSON 在许多企业数据应用程序中的使用,对加速的需求也在增长。

对于《财富》100 强零售公司而言,使用 JSON 格式存储基本库存数据。JSON 格式支持与产品分类和库存相关的非结构化数据。针对点击流数据的 JSON 处理包括在单个 Spark 工作负载中处理数十 TB 的 JSON 数据的大型查询。

GPU 加速助力零售商的生产

当应用于在 GPU 上运行的生产工作负载时,结果对零售商来说非常明显。GPU 运行时间从 16.7 小时缩短到 3.8 小时,与生产环境中类似的 CPU 集群相比,GPU 运行速度提高了 4 倍,并节省了 80% 的成本。

集群中的节点是 GCP n1-standard-16 实例,每个节点都附加了一个 NVIDIA T4 GPU。

GPU acceleration of JSON string processing comparing CPU to GPU showed over 4x improvement on a retailer workload. This production measurement is performed using GCP n1-standard-16 instances with a single T4 attached to each node.
图 1、适用于零售商工作负载的 GPU 加速。 此生产测量使用 GCP n1-standard-16 实例执行,每个节点附加一个 NVIDIA T4 GPU。

Spark 的 get_json_object 函数

自 22.02 版本发布以来,用于 Apache Spark 的 RAPIDS 加速器就一直存在针对 JSON 的 GPU 处理,但在加速处理方面存在挑战。在与零售商合作时,使用 Spark 的 get_json_object function 处理 JSON 记录的特定操作需要在 SQL 查询中动态解析 JSON。JSON 格式允许使用层次结构嵌入对象,例如数组:

JSON flow diagram showing how an object proceeds with whitespace and separates its value with a colon.  Additional objects are separated by a comma and closed with a curly brace.
图 2、JSON 对象和数组格式 (参考: https://www.json.org/json-en.html )

get_json_object 函数的目的是根据所提供的路径从 JSON 记录字符串中提取物体。这是一个简单的 SQL 查询示例,其中 get_json_object 函数用于提取嵌套元素:

SELECT get_json_object('[{"a":"b1"}, {"a":"b2"}]', '$[*].a')
["b1","b2"]

在实际用例中,该函数允许选择 JSON 记录中与 ETL 工作流中的其他处理相关的嵌套对象。

JSON 中大型字符串的挑战

在零售商工作负载中,使用中等宽度的输出表时,单个 SQL 查询语句可能会导致 get_json_object 函数被调用多达 50 次。字符串本身平均长度为多个 KiB。当您将调用频率与长 JSON 字符串相加时,GPU 在处理数据时可能会面临巨大的内存压力。尤其是对 GPU 的 L1 缓存。GPU 上的初始实施将在 GPU 上并行处理每条记录,以在默认情况下使用每线程记录来加速处理。这导致 L1 缓存尝试保存多个记录,最终导致缓存崩溃。此外,如果线程块内的线程发散,处理时间会减慢,因为线程块将在需要处理发散线程时暂停其他线程。测试 30 TB 工作负载的原始结果是,在 CPU 集群上运行 16 小时,在类似的 GPU 集群上运行 16.7 小时。

JSON path query mapped to data across thread blocks that run on the GPU. A single kernel would handle each JSON path query.
图 3. 每个 JSON 路径查询的单个内核

在深入了解零售商的查询和数据集时,我们发现数据包含 JSON 对象内的稀疏字段。这意味着,给定记录中出现字段的概率非常低;具体而言,超过 85% 的字段在不到 0.01% 的记录中出现。由于线程稀疏,在处理过程中线程很可能会在早期出现偏差。理想情况下,同一 warp 中的一组给定线程会运行所处理的类似数据。

CUDA 编程指南 中:

线程束一次执行一条通用指令,因此当线程束的所有 32 个线程在其执行路径上保持一致时,即可实现充分的效率。如果线程束的线程通过依赖数据的条件分支发散,则该线程束会执行所采用的每个分支路径,从而禁用不在该路径上的线程。分支发散仅发生在线程束内;不同的线程束独立执行,而不管它们执行的是公共代码路径还是不交接代码路径。

最初,GPU 在处理零售商查询中频繁的 get_json_object 调用时速度缓慢,这一点已得到确认,原因是线程发散,因此开始了优化此类处理的工作。

改进 GPU 上的 JSON 处理

为了解决使用大字符串(尤其是稀疏数据)优化 JSON 处理的问题,我们必须改进在线程束中处理数据的方式,以提高线程处理类似数据的概率。为了提升性能,我们经历了连续优化的过程,以帮助零售商加速工作负载。

为了提供验证优化的测试环境,我们执行了单节点基准测试,以演示优化工作的效果。本地基准测试环境包括具有 32 核 (CPU) 的 AMD Ryzen Threadripper PRO 5975WX 和 NVIDIA RTX A6000 48GB (GPU)。用于基准测试的数据是 5 列 20 万行生成的 JSON 数据,这些数据基于零售商的 JSON 数据的近似值。未压缩的基准测试数据约为 9.2 GB,使用 snappy 压缩存储为 Parquet 时约为 6.4 GB。请注意,由于大部分数据是随机生成的,因此压缩比非常低。get_json_object 调用的次数从每列一次到 50 多次不等。数据和路径都很复杂,嵌套级别超过 10,并且使用数组索引和通配符。

在使用 NVIDIA GPU(代表零售商使用的 GPU)的单节点上,使用大字符串生成的 JSON 数据的基准测试从最初略快于 CPU 处理,经过一系列优化后,速度提高了 5 倍以上。

JSON data mapped across thread blocks that run on the GPU.  A single kernel would handle each data grouping.
图 4. 每个线程块的单个内核数据

应用的第一种技术是在同一线程束中针对相同数据合并多个查询。如果在不同线程的字段中查询大型记录,我们可以刻意将它们分组到同一线程束中,以平衡线程差异并减轻缓存压力。这有助于将我们的本地基准提高 3.2 倍,因此我们已经取得了巨大的胜利。

JSON sorted data mapped across thread blocks that run on the GPU.  A single kernel would handle each sorted data grouping.
图 5、每个线程块的单个内核数据 (sorted)

但是,我们注意到,当跨线程束的 SQL 查询中对同一 JSON 路径进行后续查询时,仍可能出现线程发散。这促使我们对查询进行词法分类。这有助于进一步减少线程发散,因为查询在同一线程束内具有更高的相似性。实施该优化后,本地基准测试又提高了 10%,速度提高了 3.6 倍。

最后,我们已开始利用 RAPIDS cuDF 库中更具数据并行性的标记器,从而摆脱单字符解析,在处理 JSON 对象时实现多样化的字符串解析。基准测试表明,此优化性能再提升 50%,整体速度提升了 5.6 倍。实施方案将于今年发布。

GPU acceleration of JSON string processing comparing CPU to GPU showed over 5x improvement on local benchmarks with successive optimizations.
图 6、GPU 加速的 JSON 字符串处理

要点

处理大量字符串数据对 GPU 来说可能是一项挑战,因此需要进行特殊优化。适用于 Apache Spark 的 RAPIDS 加速器与 cuDF 一起增强了 JSON 处理,从而提高了 GPU 上的速度。

开始在 GPU 上使用 Apache Spark

企业可以利用适用于 Apache Spark 的 RAPIDS 加速器,将现有的 Spark 工作负载无缝过渡到 NVIDIA GPUs,且无需更改代码。适用于 Apache Spark 的 RAPIDS 加速器将 cuDF 的强大功能与 Spark 分布式计算框架的规模相结合,利用 GPU 加速处理。

通过 此 Colab Notebook 亲身体验 JSON 处理和适用于 Apache Spark 的 RAPIDS 加速器,并查看即将举行的 GTC 2025 会议 以了解详情。

未来工作

我们还计划针对 GPU 上的字符串处理进行其他优化,以便将用于加速 JSON 的类似技术用于更多表达式和功能。

如要跟进或帮助做出贡献,请查看适用于 Apache Spark 的 RAPIDS Accelerator cuDF 的开源作品。

 

标签