alitrack

Lua 比 DuckDB 原生慢 10 倍,但为什么你还是该用?

前几篇聊了 duckdb-luajit 能读 DICOM、能扫目录、能补类型、能接私有 API——

都是"能读什么"。这篇回答一个躲不开的问题:快不快?

直接上实测(本机同一份数据、同一个 DuckDB CLI):

● ● ●

实测 1:100 万行 CSV(23.7MB)

-- A: DuckDB 原生 read_csv
SELECT count(*) FROM read_csv('/tmp/bench.csv', auto_detect=true);
-- B: luajit 纯 Lua 解析(读文件 + 逐行拆 3 列)
LOAD 'luajit';
SELECT val FROM luajit_table('<csv-parse.lua>');
方案
耗时
说明
A: 原生 read_csv
0.044 s
C++ 手写解析器 + 类型转换
B: luajit 纯 Lua
0.471 s
无类型转换(只拆列计数)
比率
10.8x
纯 Lua 慢一个数量级

解析密集场景,Lua 确实慢 10 倍。 这是物理性的——纯 Lua 的 string.gmatch

对 C++ 的手写 SIMD 解析器。

● ● ●

实测 2:DICOM 1000 文件(真实对比)

但是——场景不同,结论完全不同。DICOM 是"IO 密集的小文件"(每文件 1.6KB,

读文件 + 遍历几个 tag),不是"解析密集的大文件"。用同 1000 个 DICOM 文件

实测 luajit 和 dicom-rs(Rust 成熟 DICOM 库,dicom-ducklink 组件内的同源逻辑):

方案
耗时
说明
A: dicom-rs 原生(standalone)
17 ms
Rust 读 19 tag
B: luajit(含 LOAD 扩展)
32 ms纯解析 ≈17ms,与 dicom-rs 打平
比率
1.9x
(含 LOAD)
IO 密集场景 Lua 几乎不慢

luajit 只慢 1.9 倍,而且那 1.9 倍里大部分是扩展加载开销——纯解析部分

和 Rust 的成熟库打平。为什么?DICOM 元数据解析的开销被文件 IO 淹没,

Lua 的解析成本根本不占主导。

● ● ●

那 WASM 组件方案(ducklink)呢?

dicom-ducklink 的 WASM 组件内跑的就是 dicom-rs——**原生 17ms × WASM 系数

(1.5-3x)+ wasmtime 实例化(每次查询毫秒级起步),估算约 30-60ms+**——

和 luajit 的 32ms 相当或略慢(组件级为估算,datalink 依赖缺失未直接实测)。

大文件/解析密集场景,WASM 的差距会拉大。

● ● ●

所以选型方法论就一句话

  1. 01格式有社区扩展 → 用原生
    (解析密集快 10x,别折腾)
  2. 02没有 → read_text 一条 SQL 从仓库拉 Lua 库
    (毫秒级装上)
  • IO 密集小文件
    (DICOM/EXIF/PDF 元数据):和原生打平(1-2x)
  • 解析密集大文件
    :慢 10x——但"能用"vs"不能用"是 0 和 1 的区别
  1. 01vs WASM 组件
    :小文件批量相当或略快(无 wasmtime 初始化/边界)

性能叙事完整闭环:快不快取决于场景——格式有扩展用扩展;没有扩展时,

luajit 在 IO 密集场景和原生打平,在解析密集场景慢 10x 但仍是唯一可用的桥。

(完整实测代码:github.com/alitrack/labs/tree/master/duckdb/luajit-bench)