news 2026/9/23 18:43:31

Dart SDK 中 FFI 基准测试原生库的构建与 CIPD 发布流程指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Dart SDK 中 FFI 基准测试原生库的构建与 CIPD 发布流程指南
  • 编程语言
  • 编译器
  • 语言运行时
  • 标准库
  • 开发工具

【免费下载链接】sdk

The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.

项目地址:https://gitcode.com/gh_mirrors/sdk1/sdk
点击查看免费下载

本文以 Dart SDK 仓库中的 benchmarks/FfiCall/native/README.md 为核心骨架,结合其配套的Makefilecipd.yamlnative_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_tint8_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 x64CC=gccout/linux/x64/libnative_functions.so
Linux ia32CC=gcc -m32out/linux/ia32/libnative_functions.so
Linux arm64CCARM64=aarch64-linux-gnu-gccout/linux/arm64/libnative_functions.so
Linux armCCARM=arm-linux-gnueabihf-gccout/linux/arm/libnative_functions.so
macOS arm64CC=gccout/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

各步骤含义:

  1. make:按第三节表格构建全部目标架构的动态库;
  2. find . -name "*.o" -type f -delete:删除中间目标文件,避免中间产物被误打包进 CIPD 包(最终产物只有各架构的.so/.dylib);
  3. 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 调用方式:

  1. 动态查找(lookupFunction:通过DynamicLibrary.lookupFunction<NativeFunction1Int64, Function1int>('Function1Int64')在运行时按符号名解析并调用,覆盖经典的dart:ffi路径;
  2. 静态 Native(@Native注解 +FfiNativeResolver:生成器在 benchmarks/FfiCall/generate_benchmarks.dart 中为每个函数生成带@Native<...>(symbol: 'FunctionNType', isLeaf: ...)注解的external声明,由 VM 通过符号解析直接绑定,是 Dart FFI 的高效绑定方式。

每种基准还区分isLeaf: true/false,用于度量「告知编译器不回调 Dart(leaf)时的调用开销差异」。main()末尾还通过dlopenGlobalPlatformSpecificRTLD_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-gccaarch64-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.

项目地址:https://gitcode.com/gh_mirrors/sdk1/sdk
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 18:43:07

高分遥感语义分割实战:PyTorch实现地物分类与面积估算全流程

简介&#xff1a;这是一份面向遥感与计算机视觉学习者的项目实践资源&#xff0c;以PyTorch为基础实现高分遥感影像语义分割&#xff0c;解决地物分类任务。资源基于GF2影像样本数据&#xff0c;覆盖模型设计、数据加载、训练验证与推理预测全流程&#xff0c;并重点展开膨胀预…

作者头像 李华
网站建设 2026/9/23 18:42:16

G6 节点(Node)体系全解析:内置类型、数据结构与样式配置实战

数据可视化前端图表库 【免费下载链接】G6 ♾ A Graph Visualization Framework in JavaScript. 项目地址&#xff1a; https://gitcode.com/gh_mirrors/g6/G6 点击查看 免费下载 节点是图可视化中最核心的构成单元。本文以 G6 官方文档《节点总览》为主线&#xff0c;结合 G6…

作者头像 李华
网站建设 2026/9/23 18:41:14

BUCK电路环路补偿设计与Saber仿真验证:从传递函数到相位裕量

简介&#xff1a;这是一份面向开关电源研发工程师的环路设计专题资料&#xff0c;聚焦BUCK电路从环路计算、补偿参数设计到仿真验证的完整流程。资料从自动控制理论中的乃奎斯特稳定性判据切入&#xff0c;讲解穿越频率、相位裕量、增益裕量、静态增益与动态响应等关键概念&…

作者头像 李华
网站建设 2026/9/23 18:40:21

单通道脑电睡眠分期实战:Python从EDF到分类模型

简介&#xff1a;这份资源面向计算机、人工智能、通信工程、自动化、电子信息等专业的在校学生与教师&#xff0c;以及希望入门生理信号处理的企业员工&#xff0c;提供一套基于单通道脑电信号实现自动睡眠分期的完整Python项目。项目围绕EEG信号预处理、数据集构建、深度网络建…

作者头像 李华
网站建设 2026/9/23 18:38:55

用C#解析STEP文件:从ISO-10303-21文本到B-Rep拓扑提取

简介&#xff1a;基于C#的STEP文件解析器完整源码与项目说明&#xff0c;属于本科毕设项目&#xff0c;主要面向计算机相关专业毕业生及需要工程实战的C#学习者。项目围绕STEP中性文件解析展开&#xff0c;实现了对文件中各组成元素的类型识别、详细信息提取&#xff0c;以及拓…

作者头像 李华