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

时间:2015-01-21 03:40:56

标签: c# c++ pinvoke memcpy

我正在寻找将大量数据从C ++(结构或值类?)传输到C#类的最佳方法,尽可能少地进行数据复制。在下面的示例代码中,我有一个SubClass对象的向量,可能非常大(超过1000万)。所以我想尽可能避免数据拷贝。

我是否可以直接在GC中分配对象并直接在c ++中使用它们并忘记原生c ++结构? (性能是我对这个问题的关注。)

或者,是否有一些技巧可以利用C ++中分配的内容而不会导致数据副本?

以下是我希望用作托管代码和非托管代码之间转换的内容的示例。

#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;
};

如果我可以避免使用pinvoke和COM类进入杂草,我会更喜欢它。

1 个答案:

答案 0 :(得分:1)

下面是Unity(使用C#)的示例,Native plugin example使用GC句柄将数据从C#传输到C ++。我们可以尝试相反的方法,将数据从C ++发送到C#。

固定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); 
}

使用封送处理将句柄转移到C ++。不可避免地,您必须复制一些东西。在这里复制指针应该足够了。根据Microsoft doc,有关封送的更多信息。

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

在C ++内部,您将收到作为所选择C ++类型的指针类型的句柄。在这种情况下,vertices是3个浮点数的结构列表。参考代码决定使用float *。您只需要根据指针类型(包括void *的情况)正确地进行指针算术即可。

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

这里的示例代码直接从指针复制数据,但是您也可以写入指针的位置。

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
}

从C#端清理固定的句柄以恢复垃圾回收器。

gcVertices.Free();

对于字符串,我相信interop库具有一个可以为您处理指针算术和复制的实现。只要不使用C#类型的MarshalAs属性和C ++库的类型,只要在C#中使用char *属性指定如何将其编组,就可以直接在暴露的导出函数中使用字符串类型。 1}}。