Apache Gluten单元测试指南:如何为原生引擎编写可靠测试用例
2026/7/27 20:23:40 网站建设 项目流程

Apache Gluten单元测试指南:如何为原生引擎编写可靠测试用例

【免费下载链接】glutenGluten is a middle layer responsible for offloading JVM-based SQL engines' execution to native engines.项目地址: https://gitcode.com/GitHub_Trending/glu/gluten

Apache Gluten作为JVM-based SQL引擎与原生执行引擎之间的中间层,其单元测试对于确保查询执行的正确性和性能至关重要。本文将详细介绍如何为Gluten原生引擎编写可靠的测试用例,涵盖测试环境搭建、测试框架使用、关键测试类型及最佳实践,帮助开发者快速上手单元测试开发。

单元测试在Gluten中的重要性

Gluten通过Substrait协议将Spark执行计划转换为原生引擎(如Velox、ClickHouse)可执行的计划,单元测试需验证这一转换过程的准确性、数据处理的正确性以及性能表现。可靠的单元测试能够:

  • 确保原生引擎正确执行SQL算子逻辑
  • 提前发现跨引擎数据类型转换问题
  • 验证内存管理和资源释放的有效性
  • 保障不同Spark版本和原生后端的兼容性

Gluten的测试代码主要分布在以下目录:

  • gluten-ut/:包含针对不同Spark版本的单元测试
  • backends-clickhouse/src/test/:ClickHouse后端测试
  • backends-velox/src/test/:Velox后端测试
  • cpp/velox/tests/:C++层原生执行测试

测试环境搭建

1. 基础环境准备

首先需要构建Gluten项目及测试相关组件:

# 克隆仓库 git clone https://gitcode.com/GitHub_Trending/glu/gluten cd gluten # 构建Velox后端并开启测试选项 ./dev/buildbundle-veloxbe.sh --build_tests=ON --build_benchmarks=ON # 生成测试所需的Parquet数据和Substrait计划文件 mvn test -Pspark-3.5 -Pbackends-velox -pl backends-velox \ -am -DtagsToInclude="org.apache.gluten.tags.GenerateExample" \ -Dtest=none -DfailIfNoTests=false -Dexec.skip

生成的测试文件位于backends-velox/generated-native-benchmark/目录,包含:

  • plan_{stageId}_{partitionId}_{vId}.json:Substrait执行计划
  • data_{stageId}_{partitionId}_{vId}_{iteratorIdx}.parquet:输入测试数据
  • conf_{stageId}_{partitionId}_{vId}.ini:测试配置文件

2. 测试配置与依赖

Gluten提供了统一的测试基类GlutenSQLTestsBaseTrait,位于gluten-ut/common/src/test/scala/org/apache/spark/sql/GlutenSQLTestsBaseTrait.scala,该类预设了测试所需的Spark配置:

// 关键测试配置 .set("spark.plugins", "org.apache.gluten.GlutenPlugin") .set("spark.shuffle.manager", "org.apache.spark.shuffle.sort.ColumnarShuffleManager") .set("spark.sql.adaptive.enabled", "true") .set("spark.memory.offHeap.enabled", "true") .set("spark.memory.offHeap.size", "1024MB")

通过继承该Trait,可以快速搭建测试环境并获取预配置的SparkSession。

核心测试类型与实现方法

1. SQL查询结果验证测试

这类测试验证Gluten执行SQL查询的结果是否与Spark原生执行一致,是最基础也最重要的测试类型。典型实现如下:

class GlutenDataFrameAggregateSuite extends GlutenSQLTestsTrait { test("test aggregate function sum") { val df = spark.createDataFrame(Seq((1, "a"), (2, "b"), (3, "a"))) .toDF("id", "name") // Gluten执行结果 val glutenResult = df.groupBy("name").sum("id").orderBy("name").collect() // 禁用Gluten的基准结果 withSQLConf(GlutenConfig.GLUTEN_ENABLED.key -> "false") { val sparkResult = df.groupBy("name").sum("id").orderBy("name").collect() assert(glutenResult sameElements sparkResult) } } }

Gluten提供了GlutenSQLTestsTrait测试特质,自动处理测试前后的环境切换,确保测试的隔离性。

2. 执行计划验证测试

验证Spark计划是否被正确转换为原生执行计划,确保算子下推和优化的正确性:

test("validate columnar execution plan") { val df = spark.sql("SELECT id, count(*) FROM test_table GROUP BY id") val executedPlans = getExecutedPlan(df) // 验证是否生成了ColumnarShuffleExchange assert(executedPlans.exists(_.nodeName.contains("ColumnarShuffleExchange"))) // 验证Aggregate算子是否下推到原生引擎 assert(executedPlans.exists(_.nodeName.contains("ColumnarAggregate"))) }

getExecutedPlan方法(定义于GlutenSQLTestsBaseTrait)可提取DataFrame的执行计划树,便于验证计划转换结果。

3. 原生引擎C++层测试

对于C++实现的核心逻辑,可使用Google Test框架直接测试,例如Velox后端的内存管理测试:

TEST(MemoryManagerTest, TestMemoryAllocation) { auto memoryManager = std::make_shared<VeloxMemoryManager>(); auto pool = memoryManager->getPool(); // 测试内存分配 auto buffer = pool->allocate<uint8_t>(1024); ASSERT_NE(buffer.get(), nullptr); ASSERT_EQ(buffer->size(), 1024); // 测试内存限制 memoryManager->setMemoryLimit(2048); ASSERT_THROW(pool->allocate<uint8_t>(3072), std::bad_alloc); }

C++测试可通过以下命令单独运行:

cd cpp/build/velox/tests ./velox_tests --gtest_filter=MemoryManagerTest.*

4. 性能基准测试

使用Gluten提供的基准测试框架验证原生执行性能,位于cpp/velox/benchmarks/目录。以TPCH Q5查询为例:

# 运行TPCH Q5基准测试 ./generic_benchmark \ --plan ../data/generic_q5/q5_first_stage_0.json \ --split ../data/generic_q5/q5_first_stage_0_split.json \ --with-shuffle --threads 8 --iterations 10

基准测试会输出详细的性能指标,包括执行时间、内存使用和算子耗时,帮助评估优化效果。

图:TPCH Q5查询的第一阶段执行计划,展示了Gluten如何将SQL转换为原生执行算子

测试结果分析与调试

1. 测试报告解读

Gluten使用Surefire插件生成详细的测试报告,包含测试用例执行情况、失败原因和性能数据。典型的测试报告界面如下:

图:Gluten单元测试报告示例,显示测试总数、跳过数和失败数,以及详细的失败原因

报告中会高亮显示失败的测试用例,并提供完整的错误堆栈信息,便于定位问题。

2. 调试技巧

(1)Java/Scala层调试

使用IntelliJ IDEA远程调试Spark测试:

export SPARK_SUBMIT_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8008" mvn test -Pspark-3.5 -Pbackends-velox -DwildcardSuites=GlutenDataFrameAggregateSuite

在IDEA中配置远程调试,连接到8008端口即可断点调试。

(2)C++层调试

使用GDB调试原生执行逻辑:

gdb cpp/build/velox/benchmarks/generic_benchmark (gdb) b VeloxBackend.cc:50 # 在Velox后端初始化处设置断点 (gdb) r --plan /path/to/plan.json --data /path/to/data.parquet

通过bt命令查看调用栈,p命令打印变量值,定位C++代码中的问题。

(3)执行计划调试

开启调试模式输出Substrait计划:

mvn test -Pspark-3.5 -Pbackends-velox -Dspark.gluten.sql.debug=true

生成的JSON格式计划可通过Substrait可视化工具进行分析,验证计划转换的正确性。

测试最佳实践

1. 测试用例设计原则

  • 原子性:每个测试用例只验证一个功能点,便于定位问题
  • 可重复性:测试数据固定,确保每次运行结果一致
  • 覆盖全面:覆盖不同数据类型、边界条件和异常场景
  • 性能平衡:单元测试应快速执行,避免过度复杂的数据集

2. 常用测试工具与框架

  • ScalaTest:用于Scala层测试,提供丰富的断言和测试风格
  • Google Test:C++层单元测试框架,支持参数化测试和测试夹具
  • Benchmark:Google Benchmark框架,用于性能基准测试
  • Surefire:Maven插件,生成统一的测试报告

3. CI集成

Gluten的CI流程会自动运行所有单元测试,确保代码质量。开发者提交PR前应本地运行:

# 运行所有单元测试 mvn test -Pspark-3.5 -Pbackends-velox -Pspark-ut # 运行特定模块测试 mvn test -Pspark-3.5 -pl gluten-ut/spark35

总结

编写可靠的单元测试是保障Gluten原生引擎质量的关键环节。本文介绍了Gluten单元测试的环境搭建、核心测试类型、实现方法和调试技巧,涵盖从SQL层到C++原生层的全栈测试。通过遵循本文所述的最佳实践,开发者可以有效验证Gluten的功能正确性和性能表现,为SQL引擎的原生加速提供坚实保障。

Gluten的测试框架持续演进,更多测试工具和用例可参考官方测试文档和测试示例代码。

【免费下载链接】glutenGluten is a middle layer responsible for offloading JVM-based SQL engines' execution to native engines.项目地址: https://gitcode.com/GitHub_Trending/glu/gluten

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询