返回

Dart FFI:结构按值传递

开发工具

在 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 语言库的理想选择。