将大量数据从Rust FFI库返回给C#调用者的最快方法是什么?

时间:2016-01-05 01:03:30

标签: c# serialization rust dllimport ffi

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

我这样做是因为我想将复杂的计算委托给Rust。在从Rust库处理之后,预计会将大量数据返回给C#。当在C#中使用时,这些数据应该是List<MyDataRecord>

然后我的问题是:

  1. 来自Rust,传递这些数据的最佳方法是什么?通过指向一个排列成结构数组的内存块的指针?
  2. C#如何找回这样的内存块指针?这里有线程安全问题吗?
  3. 如何在C#中快速将此类内存数据块转换为List<MyDataRecord>

1 个答案:

答案 0 :(得分:1)

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

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

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

在C#代码中为Rust函数定义DllImport时,可以使用MarshalAs attribute注释参数和返回值,以告诉.NET运行时参数和返回值如何应该整理或解组。特别是,UnmanagedType enumerationLPArray成员可能对您的情况有所帮助,但请注意it uses CoTaskMemAlloc and CoTaskMemFree to manage memory。如果Rust代码负责分配内存,您还可以通过将指针定义为IntPtr并使用Marshal.PtrToStructure解组结构并Marshal.SizeOf将指针偏移到指针来手动执行解编。下一个元素。