【问题标题】:Most efficient way to pass data from C++ to C#将数据从 C++ 传递到 C# 的最有效方法
【发布时间】:2015-03-19 12:02:07
【问题描述】:

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

我是否应该/我可以先在 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 类,我会更喜欢它。

【问题讨论】:

  • 你的 C++ 代码是 DLL 的一部分吗?
  • 是的。我正在编写 DLL 代码,所以我不需要尝试使用第三方封闭的 DLL。我目前正在编写 DLL 以直接使用 CLI 对象,但这意味着我必须编写两个例程来做同样的事情。一个用于 .NET 端,一个用于 C++ 端。使用包装器会隔离层,但以性能为代价,它可能不是正确的选择。也许有一种 typename 方式可以做到这一点,我可以在 .NET 构建和 c++ 构建之间切换并节省自己重新编码?
  • 您应该能够将子类的 C# 引用传递到 c++ DLL 中。您需要确保数据成员对齐。这里有一篇 stackoverflow 文章可以看:stackoverflow.com/questions/3939867/…
  • 这需要一个副本,因为您不能编组变量数组。不幸的是,我到处都有这种情况。引导子类 (100+) 如此深且重载(可能高达 12 级深),即使使用编组创建某些东西也是一个挑战。
  • 如果不复制,您将无法使用 std::string 和 std::vector。

标签: c# c++ pinvoke memcpy


【解决方案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();

至于字符串,我相信互操作库有一个可以为您处理指针运算和复制的实现。您可以直接在公开的导出函数中使用字符串类型,只要您指定如何使用 C# 中的 MarshalAs 属性和 C++ 中的库(如果您不转换为 C 类型 char *)来编组它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-22
    • 1970-01-01
    • 1970-01-01
    • 2015-10-22
    • 2021-11-01
    • 1970-01-01
    • 2021-06-23
    • 1970-01-01
    相关资源
    最近更新 更多