当前位置: 首页 > news >正文

Apache Cloudberry 向量化实践(三)重塑表达式构建路径:Gandiva 优化实战

在向量化执行系统中,表达式构建是不可或缺的基础环节。无论是 SQL 中的投影、筛选,还是分区、聚合、排序,最终都需转化为底层执行引擎能识别和执行的表达式树。而在 Apache Cloudberry 向量化执行框架中,这一过程由 Gandiva 表达式引擎负责完成。
随着数据规模与查询复杂度的提升,我们逐渐意识到,表达式构建本身正成为影响执行性能的关键路径之一。特别是在高并发、多表达式拼接的场景下,构建过程的性能瓶颈愈加突出。本文将结合实际优化案例,分享我们如何识别问题、设计优化方案,并用火焰图验证成效。

为何选 Gandiva?JIT + Arrow 的组合拳
Gandiva 是 Apache Arrow 项目中的子模块,它基于 LLVM 构建 JIT 编译能力,专为高性能、批量化的列式计算而设计。我们选择 Gandiva 作为表达式引擎的主要原因有三点:

  1. 向量化执行友好:Gandiva 表达式以 Arrow RecordBatch 为输入/输出单位,与 Cloudberry 的内存格式完全兼容,避免额外序列化/反序列化开销。
  2. JIT 编译能力强:Gandiva 支持将表达式编译为本地机器码,执行效率显著优于解释执行。
  3. 表达式树抽象清晰:其表达式结构基于语法树(AST),便于分析、合并、转换、优化。
    但“强大”背后也隐藏着一个问题:表达式构建过程并非“零成本”,尤其在表达式数量和深度快速增长时,构建开销成为了不容忽视的负担。

原始构建路径的问题:节点重复 & 树结构过深
在未优化前,我们采用“逐表达式构建”的方式——每处理一条 SQL 表达式,就从头创建一棵新的表达式树。这种策略在简单查询下运行良好,但在复杂嵌套查询、窗口函数、联表计算等场景下暴露出以下问题:

  1. 公共子表达式重复构建:同一表达式片段(如 lower(colA))在不同上下文中多次出现时,每次都重新生成节点,造成冗余。
  2. 表达式树结构深且复杂:表达式链条变长时,嵌套层级加深,构建耗时近似呈线性增长。
  3. Hash 逻辑不稳定:相同表达式结构,由于构建路径差异导致节点 hash 不一致,影响缓存和优化判断。
    我们对典型查询的表达式构建过程进行了耗时统计,结果显示:
  • 在包含 20+ 表达式的复杂查询中,表达式构建耗时占整体查询时间的 10%~15%;
  • 其中约 40% 的表达式为可复用的子表达式,但未被有效识别与复用;
  • 构建阶段的所有开销几乎全部集中在 on-CPU 路径上,火焰图显示 CreateExpressionNode、ToArrowNode 等函数在 CPU 调用栈中占比极高,成为构建瓶颈的主要耗时点。
    这些现象表明:表达式构建过程不仅费时,而且浪费资源。

优化策略:公共子表达式识别 + 哈希原子化
我们采用两项优化手段来重构表达式构建路径:

  1. 公共子表达式识别(CSE)
    引入表达式 DAG 结构,在构建过程中为每个子表达式生成唯一 key(基于语义签名),并放入全局表达式缓存池。后续若再次请求相同表达式,直接复用已有子树。
  • 优点:减少冗余节点构建,降低构建深度;
  • 技术点:等价表达式归一化(如 a + b vs b + a)、表达式 hash 去重。
  1. 哈希表达式原子化
    将每一个表达式节点封装为具有确定性 hash 的原子单元,避免因构建路径差异导致 hash 冲突。统一采用 结构 hash + 类型信息 + 参数签名 的组合哈希策略,确保缓存命中率提升。
    优化后,我们实现了表达式构建路径的“结构性去重”:从构建“树”转为拼装“块”,如搭积木般复用构建单元,降低系统负担。

优化效果对比:结构简化 & 构建耗时下降
通过对比优化前后在复杂 SQL 下的表达式构建过程,我们观察到以下显著变化:
在这里插入图片描述

不仅节点数量明显下降,构建时间也随之降低了50%以上,特别是在复合查询中表现尤为明显。

火焰图验证:构建路径 on-CPU 时间显著下降
我们进一步通过 perf 工具配合火焰图对比优化前后的 CPU 使用情况,焦点集中在表达式构建阶段。
优化前的火焰图中,Gandiva::TreeExprBuilder::MakeExpression() 及其内部调用占据主火焰图的 30% 高度,显著吞噬 on‑CPU 资源。
优化后,火焰图中该函数堆栈深度显著缩减,仅占主图不到 10%,并可见更多时间释放给后续执行逻辑,如 Eval、Filter、Project 等。
这说明:表达式构建从 CPU 消耗的“主角”,退回到了其应有的“配角”角色。

表达式构建常被认为是“编译期行为”,但在现代向量化系统中,它的性能表现直接影响执行链路的起跑速度。
通过本次优化,我们验证了如下几点:

  • 表达式构建本身具有显著的优化空间;
  • 结构性去重 比单纯加快构建速度更有效;
  • 可观测性工具(如火焰图) 是评估优化效果的关键利器。
    这也为后续优化其他执行环节(如重分布、调度、缓存)提供了经验模板:先观测,再定位,再结构重构。
http://www.lryc.cn/news/585541.html

相关文章:

  • 2D下的几何变换(C#实现,持续更新)
  • SpringBoot或OpenFeign中 Jackson 配置参数名蛇形、小驼峰、大驼峰、自定义命名
  • SpringCloud之Ribbon
  • BootstrapBlazor与JS互调
  • Semi-Supervised Single-View 3D Reconstruction via Prototype Shape Priors
  • 小智AI模型接入MCP
  • 【一起来学AI大模型】微调技术:LoRA(Low-Rank Adaptation) 的实战应用
  • SQL Server通过CLR连接InfluxDB实现异构数据关联查询技术指南
  • SpringBoot JWT
  • Rust与UE5高效集成实战
  • uniapp制作一个个人页面
  • ffmpeg-api记录
  • UC浏览器PC版自2016年后未再更新不支持vue3
  • 小旺AI截图1.2.1版本上线:新增录屏音频、Mac长截屏
  • Docker高级管理--Dockerfile 镜像制作
  • 手把手一起使用Miniforge3+mamba平替Anaconda(Win10)
  • 机器学习week2-线性回归加强
  • Java的extends通配符
  • netdxf—— CAD c#二次开发之(netDxf 处理 DXF 文件)
  • 和鲸社区深度学习基础训练营2025年关卡2(3)pytorch
  • 利用Claude code,只用文字版系统设计大纲,就能轻松实现系统~
  • 免费应用分发平台的安全漏洞和防护机制是什么?
  • 60 美元玩转 Li-Fi —— 开源 OpenVLC 平台入门(附 BeagleBone Black 驱动简单解析)
  • Windows解决 ping 127.0.0.1 一般故障问题
  • 【Linux网络】深入理解HTTP/HTTPS协议:原理、实现与加密机制全面解析
  • 信号量机制
  • 聊聊AI大模型的上下文工程(Context Engineering)
  • Spring 声明式事务:从原理到实现的完整解析
  • 运行ssh -T git@github.com报错
  • 多端协作白板:如何改变传统会议模式!