将Rust FFI库中的大量数据返回到C#调用程序的最快方法是什么
本文关键字:程序 调用 是什么 方法 数据 FFI Rust 返回 | 更新日期: 2023-09-27 18:29:02
我正在.NET C#中开发一个程序,该程序通过Rust FFI调用我自己的Rust库,使用extern C关键字并由DllImport加载。
我这样做是因为我想将复杂的计算委托给Rust。从Rust库处理后,预计会将大量数据返回到C#。当这些数据在C#中被消耗时,它们应该处于一种List<MyDataRecord>
中。
那么我的问题是:
- 从Rust,传递回这些数据的最佳方式是什么?通过指向像结构数组一样排列的内存块的指针
- C#如何取回这样的内存块指针?这里有螺纹安全问题吗
- 如何在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
成员,这可能对您的情况有所帮助,但请注意,它使用CoTaskMemAlloc
和CoTaskMemFree
来管理内存。如果Rust代码负责分配内存,您也可以手动执行解组,方法是将指针定义为IntPtr
,并使用Marshal.PtrToStructure
解组结构,使用Marshal.SizeOf
将指针偏移到下一个元素。