- 编程语言
- 编译器
- 语言运行时
- 标准库
- 开发工具
【免费下载链接】sdk
The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
本文以 Dart SDK 仓库中的 benchmarks/FfiCall/native/README.md 为核心骨架,结合其配套的Makefile、cipd.yaml、native_functions.c以及 Dart 侧基准源码,完整讲解FfiCall基准测试所需原生动态库的构建、多架构产物组织、CIPD 包上传与 DEPS 哈希更新流程。读完本文,你将掌握如何在 Dart SDK 仓库内重新构建并发布一套 FFI 基准测试原生库,也能理解其背后的dart:ffi加载机制与基准测试驱动方式。
一、FfiCall 基准测试的整体结构
benchmarks/FfiCall是 Dart SDK 中用于衡量「Dart 与 C 原生代码相互调用(FFI)开销」的微基准集合,目录结构如下:
benchmarks/FfiCall/ ├── dart/ │ ├── FfiCall.dart # 基准测试主驱动 │ ├── benchmark_generated.dart # 由生成器产出的基准类(勿手改) │ └── dlopen_helper.dart # 跨平台动态库加载辅助 ├── native/ │ ├── Makefile # 多架构动态库构建脚本 │ ├── README.md # 发布流程说明(本文主体) │ ├── cipd.yaml # CIPD 包定义 │ └── native_functions.c # 被测 C 函数集合 └── generate_benchmarks.dart # 基准代码生成器- Dart 侧基准逻辑位于 benchmarks/FfiCall/dart/FfiCall.dart;
- 原生 C 函数库位于 benchmarks/FfiCall/native/native_functions.c;
- 基准类由 benchmarks/FfiCall/generate_benchmarks.dart 自动生成到
dart/benchmark_generated.dart。
二、原生库的职责:被测 C 函数集合
native_functions.c提供了一组规则统一、便于量化对比的 C 函数,覆盖 FFI 基准所需的全部类型维度:
- 整型:
uint8_t/uint16_t/uint32_t/uint64_t与int8_t/int16_t/int32_t/int64_t,例如Function1Int32(int32_t x)返回x + 42; - 浮点:
float/double,如Function20Double(...)对 20 个参数求和; - 指针:
uint8_t *系列,如Function1PointerUint8返回a + 1; - 句柄:
void*系列,如Function1Handle(void* a)原样返回a。
每个类型都提供了参数个数为 1、2、4、10、20 的变体,用于度量「参数数量增长对 FFI 调用开销的影响」。对应的生成规则定义在 benchmarks/FfiCall/generate_benchmarks.dart 的generateFor配置中:
const generateFor = { 'Int8': [1], 'Int32': [1, 2, 4, 10, 20], 'Int64': [1, 2, 4, 10, 20], 'Float': [1, 2, 4, 10, 20], 'Double': [1, 2, 4, 10, 20], 'Pointer<Uint8>': [1, 2, 4, 10, 20], 'Handle': [1, 2, 4, 10, 20], };三、Makefile:多架构动态库构建
发布流程的第一步是构建动态库。native/Makefile通过交叉编译产出 4 个 Linux 架构产物,并为 Apple Silicon(M1)提供 macOS 目标:
| 架构 | 编译器变量 | 产物路径 |
|---|---|---|
| Linux x64 | CC=gcc | out/linux/x64/libnative_functions.so |
| Linux ia32 | CC=gcc -m32 | out/linux/ia32/libnative_functions.so |
| Linux arm64 | CCARM64=aarch64-linux-gnu-gcc | out/linux/arm64/libnative_functions.so |
| Linux arm | CCARM=arm-linux-gnueabihf-gcc | out/linux/arm/libnative_functions.so |
| macOS arm64 | CC=gcc | out/mac/arm64/libnative_functions.dylib |
统一编译参数为:
CFLAGS=-Wall -g -O -fPIC其中-fPIC是生成位置无关代码、供dlopen动态加载的必需选项。Makefile 顶部注释(TODO(37531))说明:当前仍由 Makefile 构建,待基准运行器支持后计划改用 SDK 构建,因此该文件属于阶段性过渡方案。
四、CIPD 发布流程(原文档核心步骤)
原 benchmarks/FfiCall/native/README.md 给出的完整发布流程如下,每一步都需要严格执行:
$ cd benchmarks/FfiCall/native $ make $ find . -name "*.o" -type f -delete $ cipd create -pkg-def=cipd.yaml各步骤含义:
make:按第三节表格构建全部目标架构的动态库;find . -name "*.o" -type f -delete:删除中间目标文件,避免中间产物被误打包进 CIPD 包(最终产物只有各架构的.so/.dylib);cipd create -pkg-def=cipd.yaml:依据cipd.yaml定义创建并上传 CIPD 包。
cipd.yaml 包定义说明
benchmarks/FfiCall/native/cipd.yaml 内容如下:
package: dart/benchmarks/fficall description: Dynamic libaries for running the benchmarks/FfiCall. install_mode: copy data: - dir: out/package:CIPD 包名,固定为dart/benchmarks/fficall;install_mode: copy:安装时以复制方式落盘(而非符号链接),保证基准运行时可稳定定位动态库;data:将out/目录整体作为包数据。
Makefile 中同样内置了一条等效命令(cipd目标):cipd create -name dart/benchmarks/fficall -in out -install-mode copy,效果与-pkg-def方式等价,二者可互换使用。
五、更新 DEPS 中的版本哈希
上传成功后,需要把新的实例哈希写回仓库根目录的 DEPS 文件。原文档给出的查询命令:
$ cipd instances dart/benchmarks/fficall该命令会列出该包的历史实例及对应哈希(含时间戳与refs),取最新一条的实例哈希,替换 DEPS 中dart/benchmarks/fficall条目下的version字段。
DEPS 中 CIPD 依赖的通用结构如下(以third_party/d8为例,benchmarks/fficall的条目采用同样模式):
{ "package": "dart/benchmarks/fficall", "version": "<替换为 cipd instances 输出的新哈希>", "dep_type": "cipd" }关于 CIPD 版本管理方式,DEPS 中已有的sdk_tag: "version:3.14.0-226.0.dev"(见 DEPS 第 58 行)等条目说明:CIPD 依赖既可以锁定为具体实例哈希,也可以通过ref或版本标签引用。更新哈希后需同步提交 DEPS 变更,构建与测试基础设施才能在gclient sync时拉取到新版本的原生库。
六、Dart 侧如何加载并调用这些动态库
平台相关的加载路径
benchmarks/FfiCall/dart/dlopen_helper.dart 负责按当前平台与 CPU 架构拼接动态库路径:
- macOS/iOS →
mac/<arch>/lib<name>.dylib - Windows →
win/<arch>/<name>.dll - 其余(Linux/Android)→
linux/<arch>/lib<name>.so
架构通过uname -m探测,并将arm64/aarch64/x86_64等名称归一化;同时利用sizeOf<IntPtr>()检测 32 位运行模式,将 x64/arm64 映射回ia32/arm目录,从而与 Makefile 产出的目录结构精确对应。
基准主文件 benchmarks/FfiCall/dart/FfiCall.dart 中按如下方式加载:
DynamicLibrary ffiTestFunctions = dlopenPlatformSpecific( 'native_functions', path: Platform.script.resolve('../native/out/').path, );两种调用路径:lookupFunction 与 @Native
FfiCall.dart同时度量两种 FFI 调用方式:
- 动态查找(
lookupFunction):通过DynamicLibrary.lookupFunction<NativeFunction1Int64, Function1int>('Function1Int64')在运行时按符号名解析并调用,覆盖经典的dart:ffi路径; - 静态 Native(
@Native注解 +FfiNativeResolver):生成器在 benchmarks/FfiCall/generate_benchmarks.dart 中为每个函数生成带@Native<...>(symbol: 'FunctionNType', isLeaf: ...)注解的external声明,由 VM 通过符号解析直接绑定,是 Dart FFI 的高效绑定方式。
每种基准还区分isLeaf: true/false,用于度量「告知编译器不回调 Dart(leaf)时的调用开销差异」。main()末尾还通过dlopenGlobalPlatformSpecific以RTLD_LAZY | RTLD_GLOBAL全局方式预加载动态库,确保符号在进程内全局可见。基准结果校验由FfiBenchmarkBase提供expectEquals/expectApprox/expectIdentical完成(如整型基准断言x == 0x7FFFFFFF00000000 + N * 42)。
重新生成基准代码
若需要新增类型或参数个数变体,可修改generateFor/nativeToDartType配置后运行:
$ dart benchmarks/FfiCall/generate_benchmarks.dart脚本会把生成结果写入dart/benchmark_generated.dart并自动执行dart format。生成的 Dart 代码以part of 'FfiCall.dart'方式内联(见 benchmarks/FfiCall/dart/benchmark_generated.dart 头部注释),目的是让库 URI 与主文件保持一致,从而保证FfiNativeResolver对主文件 URI 的正常解析。
七、完整发布工作流总结
综合原文档与仓库源码,一次完整的「修改原生函数 → 发布新版」流程为:
# 1. 修改 native_functions.c(或 Makefile 新增架构) $ cd benchmarks/FfiCall/native # 2. 构建全部目标架构动态库 $ make # 3. 清理中间 .o,确保只打包最终产物 $ find . -name "*.o" -type f -delete # 4. 依据 cipd.yaml 创建并上传 CIPD 包 $ cipd create -pkg-def=cipd.yaml # 5. 查询最新实例哈希 $ cipd instances dart/benchmarks/fficall # 6. 将 DEPS 中 dart/benchmarks/fficall 的 version 更新为新哈希并提交八、注意事项与边界说明
- 适用平台:当前
cipd.yaml打包的out/目录内容由 Makefile 产出,覆盖 Linux(x64/ia32/arm64/arm)与 macOS arm64;Windows 路径(win/目录)在dlopen_helper.dart中已预留,但需自行补充对应构建产物; - 编译器依赖:交叉编译 arm/arm64 依赖
arm-linux-gnueabihf-gcc与aarch64-linux-gnu-gcc工具链; - DEPS 变更影响:更新 DEPS 哈希后,所有通过
gclient同步该仓库的构建/测试机器会拉取新原生库,因此发布前应先在本地完整运行一次FfiCall基准(dart benchmarks/FfiCall/dart/FfiCall.dart,可通过首个参数按名称过滤基准项)验证正确性; - 阶段性方案:Makefile 顶部标注的
TODO(37531)表明该构建方式未来可能被 SDK 内建构建替换,关注该注释的后续变更即可。
通过上述流程,你可以像 Dart SDK 维护者一样,把更新后的 FFI 基准原生库安全、可复现地发布到 CIPD 并回写 DEPS,为 Dart 团队持续跟踪 FFI 调用性能提供一致的基础设施支撑。
- 编程语言
- 编译器
- 语言运行时
- 标准库
- 开发工具
【免费下载链接】sdk
The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
相关推荐
Dart SDK 基准测试原生库 CIPD 发布指南:以 FfiCallback 为例
Dart SDK 基准测试原生库 CIPD 发布指南:以 FfiCallback 为例 本指南以 Dart SDK 仓库中的 benchmarks/FfiCal
编程语言编译器语言运行时标准库开发工具Solana 聚合仓库实战指南:环境准备、构建、测试、基准测试、代码覆盖与发布流程
Solana 聚合仓库实战指南:环境准备、构建、测试、基准测试、代码覆盖与发布流程 本文基于 Solana 仓库根目录的 README https://link
区块链后端分布式系统Bluebird 贡献开发指南:构建、测试与基准测试全流程解析
Bluebird 贡献开发指南:构建、测试与基准测试全流程解析 本篇指南基于 Bluebird 官方 贡献指南 https://link.gitcode.com
后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考