Crate 配置
Crate 配置
本节介绍如何在 Rust 项目中配置 DataFusion 的构建。配置设置一节列出了可控制 DataFusion 运行时行为其他方面的选项。
使用每夜构建的 DataFusion
DataFusion 的变更会按照发布流程发布到 crates.io。
如果你想使用或测试那些已经合并但尚未发布的 DataFusion 代码版本,可以利用 Cargo 的添加依赖项功能,直接指向某个 GitHub 分支:
datafusion = { git = "https://github.com/apache/datafusion", branch = "main"}此外,它在包级别同样有效。
datafusion-common = { git = "https://github.com/apache/datafusion", branch = "main", package = "datafusion-common"}以及功能特性
datafusion = { git = "https://github.com/apache/datafusion", branch = "main", default-features = false, features = ["unicode_expressions"] }更多内容参见 Cargo 依赖
优化构建
以下是几条建议,可让 Rust 编译器在编译 DataFusion 时生成更快的代码。请注意,这些改动可能会增加编译时间和二进制文件大小。
使用针对 CPU 的特定指令生成代码
默认情况下,Rust 编译器生成的代码可在多种 CPU 上运行,但可能无法利用你所使用 CPU 的全部特性(例如某些 SIMD 指令)。对于 x86_64 CPU 尤其如此,其默认目标是 x86_64-unknown-linux-gnu,该目标仅保证支持 SSE2 指令集。DataFusion 可以从 AVX2 和 AVX512 中更高级的指令中获益,从而加速过滤、聚合和连接等操作。要让 Rust 编译器使用这些指令,请设置 RUSTFLAGS 环境变量以指定更具体的 target CPU。
我们建议设置 target-cpu,或者至少设置 avx2,更好的做法是至少设置为 native(即当前 CPU 的型号)。例如,要针对当前 CPU 进行优化来构建并运行 DataFusion:
RUSTFLAGS='-C target-cpu=native' cargo run --release启用链接时优化 / 单一代码生成单元
通过将 DataFusion 编译为单一代码生成单元,你有可能提升性能,这使 Rust 编译器有更多机会跨越 crate 边界进行优化。为此,请按如下所示修改项目的 Cargo.toml,加入 lto = true 和 codegen-units = 1。请注意,使用单一代码生成单元会显著增加 --release 构建所需的时间。
[profile.release]
lto = true
codegen-units = 1剖析引导优化(PGO)
剖析引导优化(Profile Guided Optimization)可以将 DataFusion 的性能提升多达 25%。其工作方式是:先以插桩方式编译,运行具有代表性的工作负载以收集剖析数据,然后基于这些数据重新编译以进行优化。
使用插桩进行构建:
RUSTFLAGS="-C profile-generate=/tmp/pgo-data" cargo build --release运行你的工作负载以收集性能剖析数据。可以使用 TPCH 或 Clickbench 等基准测试,也可以使用你实际的生产查询:
./target/release/your-datafusion-app --benchmark使用收集到的 profile 重新构建:
RUSTFLAGS="-C profile-use=/tmp/pgo-data" cargo build --release提示:
- 使用与生产环境模式相匹配的工作负载
- 在性能分析期间多次运行,以获得更好的覆盖范围
- 结合 LTO 与针对特定 CPU 的优化,以获得最佳效果
更多详情请参阅 Rust 编译器指南。相关的讨论与结果见 issue devlive-community/knowforge#9507。
替代分配器:snmalloc
你也可以使用 snmalloc-rs crate 作为 DataFusion 的内存分配器,以提升性能。具体做法是按照如下所示在 Cargo.toml 中添加该依赖。
[dependencies]
snmalloc-rs = "0.3"然后,在 main.rs 中,于导入语句之后加入以下内容以更新内存分配器:
use datafusion::prelude::*;
#[global_allocator]
static ALLOC: snmalloc_rs::SnMalloc = snmalloc_rs::SnMalloc;
#[tokio::main]
async fn main() -> datafusion::error::Result<()> {
Ok(())
}启用回溯
默认情况下,DataFusion 只会返回纯文本形式的错误信息。如果你想获取更详细的错误信息(例如回溯信息),可以在 Cargo.toml 文件中启用 backtrace 特性,方式如下:
datafusion = { version = "55.1.0", features = ["backtrace"]}设置环境变量
RUST_BACKTRACE=1 ./target/debug/datafusion-cli
DataFusion CLI v31.0.0
> select row_numer() over (partition by a order by a) from (select 1 a);
Error during planning: Invalid function 'row_numer'.
Did you mean 'ROW_NUMBER'?
backtrace: 0: std::backtrace_rs::backtrace::libunwind::trace
at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be/library/std/src/../../backtrace/src/backtrace/libunwind.rs:93:5
1: std::backtrace_rs::backtrace::trace_unsynchronized
at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5
2: std::backtrace::Backtrace::create
at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be/library/std/src/backtrace.rs:332:13
3: std::backtrace::Backtrace::capture
at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be/library/std/src/backtrace.rs:298:9
4: datafusion_common::error::DataFusionError::get_back_trace
at /datafusion/datafusion/common/src/error.rs:436:30
5: datafusion_sql::expr::function::<impl datafusion_sql::planner::SqlToRel<S>>::sql_function_to_expr
............回溯信息在调试代码时非常有用。如果 datafusion/core/src/physical_planner.rs 中有一个测试
#[tokio::test]
async fn test_get_backtrace_for_failed_code() -> Result<()> {
let ctx = SessionContext::new();
let sql = "
select row_numer() over (partition by a order by a) from (select 1 a);
";
let _ = ctx.sql(sql).await?.collect().await?;
Ok(())
}要获取回溯信息:
cargo build --features=backtrace
RUST_BACKTRACE=1 cargo test --features=backtrace --package datafusion --lib -- physical_planner::tests::test_get_backtrace_for_failed_code --exact --nocapture
running 1 test
Error: Plan("Invalid function 'row_numer'.\nDid you mean 'ROW_NUMBER'?\n\nbacktrace: 0: std::backtrace_rs::backtrace::libunwind::trace\n at /rustc/129f3b9964af4d4a709d1383930ade12dfe7c081/library/std/src/../../backtrace/src/backtrace/libunwind.rs:105:5\n 1: std::backtrace_rs::backtrace::trace_unsynchronized\n...注意:回溯信息会被截断到系统调用处,因此回溯顶部的某些步骤可以忽略。
若要以美化打印的格式显示回溯信息,请使用 eprintln!("{e}");。
#[tokio::test]
async fn test_get_backtrace_for_failed_code() -> Result<()> {
let ctx = SessionContext::new();
let sql = "select row_numer() over (partition by a order by a) from (select 1 a);";
let _ = match ctx.sql(sql).await {
Ok(result) => result.show().await?,
Err(e) => {
eprintln!("{e}");
}
};
Ok(())
}然后运行测试:
$ RUST_BACKTRACE=1 cargo test --features=backtrace --package datafusion --lib -- physical_planner::tests::test_get_backtrace_for_failed_code --exact --nocapture
running 1 test
Error during planning: Invalid function 'row_numer'.
Did you mean 'ROW_NUMBER'?
backtrace: 0: std::backtrace_rs::backtrace::libunwind::trace
at /rustc/129f3b9964af4d4a709d1383930ade12dfe7c081/library/std/src/../../backtrace/src/backtrace/libunwind.rs:105:5
1: std::backtrace_rs::backtrace::trace_unsynchronized
at /rustc/129f3b9964af4d4a709d1383930ade12dfe7c081/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5
2: std::backtrace::Backtrace::create
at /rustc/129f3b9964af4d4a709d1383930ade12dfe7c081/library/std/src/backtrace.rs:331:13
3: std::backtrace::Backtrace::capture
...评论
登录后参与评论
KnowForge