Execution
学习 duckdb 的源代码,我是首先从 Execution 这一部份开始的:
- 虽然从结构上来看,Execution 是整个系统的最后一步,但秉着“以终为始”的学习原则, 从 Execution 开始,可以更好的了解整个系统的核心。从 Parser 到 Logical Plan 到 Optimizer 到 Physical Plan,都是在为 Execution 这一步做准备。
- DuckDB 的性能优化的关键,也是在 Execution 这一步,其核心是 Pipeline 的机制和 Vector 的数据结构。
理解了 Pipeline 和 Vector,再结合几个主要的 Operator 原理,就可以对 DuckDB 的执行机制有一个初步的认识了。
参考文档
- DuckDB internals
- Push-Based Execution In DuckDB
- 知乎链接 DuckDB Push-Based Execution Model
代码调试技巧
- 在 duckdb shell 中 使用
explain statement
或者explain analyze statement
查看执行计划。 - PipelineExecutor::Execute(idx_t max_chunks) 是 Pipeline 的执行入口,可以从这里添加断点,开启一个 Pipeline 的调试。
-. watches:
this->pipeline.ToString()
查看当前 Pipeline 的信息。 或者this->pipeline.Print()
在 console 中 查看当前 Pipeline 的 信息。 一次SQL 执行会产生多个 Pipeline,仅在满足条件的 Pipeline 上设置断点。 -. watches:this->pipeline.source->ToString(ExplainFormat::TEXT)
查看当前 Pipeline 的 Source 信息。 -. 在调试的 Variables 面板中,可以查看 pipeline 的 source, operators, sink 的信息,在对应的 operator 上设置断点。 - source 节点的入口是:
PhysicalXxxOperator::GetData(ExecutionContext &context, DataChunk &chunk, OperatorSourceInput &input)
, 可以在这里设置断点,查看 source 的执行流程。 - operator 节点的执行入口是:
PhysicalXxxOperator::Execute(ExecutionContext &context, DataChunk &input, DataChunk &chunk, GlobalOperatorState &gstate, OperatorState &state
- sink 节点的执行有3个入口:
PhysicalXxxOperator::Sink(ExecutionContext &context, DataChunk &chunk, OperatorSinkInput &input)
PhysicalXxxOperator::Combine(ExecutionContext &context, OperatorSinkCombineInput &input)
PhysicalXxxOperator::Finalize((Pipeline &pipeline, Event &event, ClientContext &context, OperatorSinkFinalizeInput &input)
在阅读代码过程中,可以通过上述的调试技巧,找到需要学习的代码的入口点,设置断点后,跟着调试器一步一步的阅读代码,并在 Variables 中查看各个变量的 值,理解数据流和代码执行流程。
接下来,我们将从一些简单的SQL 执行示例出发,来阅读 Execution 模块的源代码。