将Rust FFI库中的大量数据返回到C#调用程序的最快方法是什么



我正在.NET C#中开发一个程序,该程序通过Rust FFI调用我自己的Rust库,使用extern C关键字并由DllImport加载。

我这样做是因为我想将复杂的计算委托给Rust。从Rust库处理后,预计会将大量数据返回到C#。当这些数据在C#中被消耗时,它们应该处于一种List<MyDataRecord>中。

那么我的问题是:

  1. 从Rust,传递回这些数据的最佳方式是什么?通过指向像结构数组一样排列的内存块的指针
  2. C#如何取回这样的内存块指针?这里有螺纹安全问题吗
  3. 如何在C#中将这样的内存数据块快速转换为我的List<MyDataRecord>

我们需要回答的第一个问题是:谁将负责分配和释放内存?如果您提前知道将返回多少元素,那么您可以让C#代码或Rust代码分配内存。如果你事先不知道会返回多少个元素,那么你有两个选项:1)让C#代码询问Rust代码会返回多少元素(如果可能的话),然后从C#分配内存;2) 让Rust代码分配内存。如果您从C#分配托管内存(例如,托管数组),那么您可以让垃圾收集器释放内存。如果从C#或Rust分配非托管内存,则必须使用正确的函数来释放内存。如果使用Rust的默认内存分配器,Rust代码将需要提供一个释放内存的函数。请记住,生成结果的Rust代码需要显式地"泄漏"数组/Vec,否则函数将在返回之前释放内存!

您会发现将.NET的List<T>类型与非托管代码一起使用很困难。您应该使用数组。

另一个需要考虑的重要方面是结构的布局。您必须将#[repr(C)]添加到Rust结构,并将[StructLayout(LayoutKind.Sequential)][StructLayout(LayoutKind.Explicit)](取决于结构内部的类型)添加到C#结构,以确保双方在内存中以相同的方式布置结构的字段。

当您在C#代码中为Rust函数定义DllImport时,您可以使用MarshalAs属性对参数和返回值进行注释,以告诉.NET运行时应该如何封送或取消封送参数和返回数值。特别是,UnmanagedType枚举具有LPArray成员,这可能对您的情况有所帮助,但请注意,它使用CoTaskMemAllocCoTaskMemFree来管理内存。如果Rust代码负责分配内存,您也可以手动执行解组,方法是将指针定义为IntPtr,并使用Marshal.PtrToStructure解组结构,使用Marshal.SizeOf将指针偏移到下一个元素。

相关内容

最新更新