简体   繁体   English

将数据从 C++ 传递到 C# 的最有效方法

[英]Most efficient way to pass data from C++ to C#

I am looking for the best way to transfer a large amount of data from C++ (struct or a value class?) into a C# class doing as little data copying as possible.我正在寻找将大量数据从 C++(结构或值类?)传输到 C# 类的最佳方法,并尽可能少地复制数据。 In the sample code below, I have a vector of SubClass objects that has the potential to be very large (10+ million).在下面的示例代码中,我有一个可能非常大(10+ 百万)的 SubClass 对象向量。 So I want to avoid a data copy if possible.所以我想尽可能避免数据复制。

Should I/can I just allocate the objects in GC first and use them directly in c++ and forget about the native c++ structures?我应该/我可以先在 GC 中分配对象并直接在 C++ 中使用它们而忘记本机 C++ 结构吗? (Performance is my concern with this one.) (性能是我关心的问题。)

Or, is there some trick that I leverage what is allocated in C++ without causing a data copy?或者,是否有一些技巧可以利用 C++ 中分配的内容而不会导致数据复制?

Here is a sample of something along the lines of what I want to use as a transfer between managed and unmanaged code.这是我想用作托管和非托管代码之间传输的内容的示例。

#include <string>
#include <vector>
struct SubClass {
    std::string DataItem1;
    // lots more here
    std::string DataItem50;
};

struct Sample {
    int IntValue;
    std::string StringValue;
    std::vector<std::string> SmallList;
    std::vector<SubClass> HugeList;
};

If I can avoid getting into the weeds with pinvoke and COM classes, I would prefer it.如果我可以避免陷入 pinvoke 和 COM 类的杂草,我会更喜欢它。

Following the example from Unity (who uses C#), Native plugin example uses a GC handle to transfer data from C# to C++.遵循 Unity(使用 C#)的示例,本机插件示例使用 GC 句柄将数据从 C# 传输到 C++。 We can try the opposite to send data to C# from C++.我们可以尝试相反的方法,将数据从 C++ 发送到 C#。

Pin down a C# variable to allow faster copying.固定一个 C# 变量以允许更快的复制。

using System;
using System.Collections;
using System.Runtime.InteropServices;
// vertices is a Vector3[], where Vector3 is a struct 
// of 3 floats using a sequential layout attribute
void test(){
GCHandle gcVertices = GCHandle.Alloc (vertices, GCHandleType.Pinned); 
}

Transfer the handle to C++ using marshaling.使用封送处理将句柄传输到 C++。 It's unavoidable that you have to copy something.你不得不复制一些东西是不可避免的。 Here copying a pointer should be good enough.这里复制一个指针应该就足够了。 More on marshaling according to Microsoft doc .更多关于根据 Microsoft doc进行编组的信息。

[DllImport("your dll")]
private static extern void SendHandle(IntPtr vertexHandle, int vertexCount);
SendHandle(gcVertices, vertices.Length);

Inside C++, you'll receive the handle as a pointer type to a C++ type of your choosing.在 C++ 中,您将收到作为指向您选择的 C++ 类型的指针类型的句柄。 In this case, vertices are a list of structs of 3 floats.在这种情况下, vertices是 3 个浮点数的结构列表。 The reference code decided to use float * .参考代码决定使用float * You just need to do pointer arithmetic properly depending on the pointed type, including the case of void * .您只需要根据指向的类型(包括void *的情况)正确地进行指针算术。

extern "C" __decl(dllexport) void SendHandle(float* vertices, int vertexCount);

Here the example code copies data directly from the pointer, but you can also write to the pointer's location.这里的示例代码直接从指针复制数据,但您也可以写入指针的位置。

for (int i = 0 ; i < vertexCount; i++)
{
   // read from C# heap
   float x = vertices[0];
   float y = vertices[1];
   float z = vertices[2];

   // write to C# heap
   *vertices = sqrt(x);
   *(vertices + 1) = sqrt(y);
   *(vertices + 2) = sqrt(z);

   vertices += 3; // because it is a list of struct of 3 floats
}

Clean up the pinned handle from the C# side to resume the garbage collector.从 C# 端清除固定句柄以恢复垃圾收集器。

gcVertices.Free();

As for strings, I believe the interop library has an implementation that handles pointer arithmetic and copying for you.至于字符串,我相信互操作库有一个为您处理指针算术和复制的实现。 You could probably just use a string type directly inside the exposed export function, as long as you specify how to marshal it with the MarshalAs attribute in C# and a library in C++ if you are not converting to the C type char * .您可以直接在公开的导出函数中使用字符串类型,只要您指定如何使用 C# 中的MarshalAs属性和 C++ 中的库(如果您不转换为 C 类型char *

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM