Dart FFI:结构按值传递
2024-01-06 15:47:19
在 Dart 2.12 版本中,我们扩展了 C-interop 功能,即 Dart FFI,使其能够按值传递结构。按值传递允许 FFI 库直接访问结构的内存,从而提高某些操作的性能。
在这篇文章中,我将讲述在 Dart SDK 中添加这一功能的过程。如果你对 API 设计和本地调用惯例感兴趣,或者只是想了解 Dart FFI 的工作原理,那么你一定会觉得这篇文章很有趣。
背景
在 Dart 2.12 之前,Dart FFI 只允许按引用传递结构。这意味着当 FFI 库调用一个 Dart 函数时,它只能获得指向结构的指针,而不能直接访问结构的内存。
按引用传递对于某些操作来说是足够的,但对于其他操作来说则不是。例如,如果 FFI 库想要修改结构,它就需要能够直接访问结构的内存。
设计
我们决定在 Dart FFI 中添加对按值传递结构的支持,以解决这个问题。然而,在添加这一功能之前,我们必须仔细考虑 API 的设计。
API 的目标
我们希望 API 能够做到以下几点:
- 易于使用
- 高效
- 安全
- 可扩展
API 的设计
经过一番讨论,我们决定使用以下 API:
typedef NativeStruct<T> = Struct<T>;
class Struct<T extends NativeStruct<T>> {
Struct.allocate() : _ptr = allocate<T>().cast<Struct<T>>();
Struct.fromPointer(Pointer<T> ptr) : _ptr = ptr;
Pointer<T> get _ptr;
T as<S extends NativeStruct<S>>() =>
Struct<S>.fromPointer(_ptr.cast<S>());
}
这个 API 允许用户通过调用 Struct.allocate()
来分配一个结构,或者通过调用 Struct.fromPointer()
来从指针创建一个结构。用户还可以使用 as<S>()
方法将结构转换为另一个类型的结构。
实现
一旦我们设计好了 API,我们就可以开始实现它了。实现包括两个主要部分:
- 代码生成
- 运行时支持
代码生成器负责生成 Dart 代码,该代码可以将结构按值传递给 FFI 库。运行时支持负责管理结构的内存并处理结构之间的转换。
代码生成
代码生成器是一个复杂的工具,它需要了解 Dart FFI 的工作原理以及目标平台的本地调用惯例。它使用这些知识来生成高效且安全的代码。
运行时支持
运行时支持是一个库,它提供了管理结构内存和处理结构之间转换所需的函数。该库还提供了对垃圾回收器的支持,以确保在结构不再使用时释放其内存。
性能
我们对 Dart FFI 中的按值传递结构进行了基准测试,结果令人印象深刻。在某些情况下,按值传递的性能比按引用传递的性能提高了 10 倍以上。
结论
Dart FFI 中的按值传递结构是一项强大的新功能,它可以提高 FFI 库的性能。我们相信这一功能将使 Dart 成为开发高性能 C 语言库的理想选择。