【问题标题】:Array from C# to C++ and back, without unsafe code [duplicate]从 C# 到 C++ 并返回的数组,没有不安全的代码 [重复]
【发布时间】:2018-07-05 16:40:32
【问题描述】:

我知道这个问题已经被问过并且可能被认为是重复的,但在阅读了其他答案后,我仍然很困惑,不明白那里提供的解释。

如果有人能提供示例代码 一些解释,我将不胜感激。

我正在处理以下任务:
我在 Unity 中有一个纹理/纹理(无法从驱动器加载,动态生成),我将其转换为数组。它可以是 int、byte 或 float 的数组。

目前,我在 C# 中进行对象检测,但我想让它更快。因此,我想做以下事情。
访问 C++ 代码(从 dll,我刚刚开始使用 C++ 的旅程)并运行一个方法,该方法将获取我的输入数组,进行计算并将新数组(旧的未修改)返回到 C# 代码。

或者,我可以输入两个数组(首选),其中一个被修改。
输出数组不必每次都创建,它可以被覆盖。 我不能在 C# 中使用不安全代码,因为这是出于资产目的(兼容性问题)。

样机代码

C#
create arrays aOne,aTwo;
SomeC++Method(aOne,ref aTwo)
or 
aTwo = SomeC++Method(aOne,aTwo)
or 
aTwo = SomeC++Method(aOne) // variant where aTwo is created in C++

SomeC++MethodToDeAllocateMemory() //so there is no memory leak

C++
SomeC++Method(aOne,aTwo)
  for (xyz)
   do someting
  return modified aTwo
SomeC++MethodToDeAllocateMemory()
  release memory

我在这里找到了这个问题的答案: Updating a C# array inside C++ without Marshal.Copy or Unsafe

示例解决方案:

c++ 代码:

extern "C" __declspec(dllexport) bool PopulateArray(float* floats, int length)
{
    for (int i = 0; i < length; ++i)
    {
        floats[i] = i;
    }

    return true;
}

C# 代码

using UnityEngine;
using System.Runtime.InteropServices;

public class Test : MonoBehaviour {

    [DllImport("ArrayFilling", CallingConvention = CallingConvention.Cdecl)]
    public static extern bool PopulateArray([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] float[] floats, int length);

    int length = 4000000; //corresponds to 2000x2000 texture of grayscale values

    void Start()
    {
        //create new array
        float[] floats = new float[length];

        //used for speed test
        System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();

        //check internal method speed
        sw.Start();
        //floats array gets update here
        Populate(ref floats);
        sw.Stop();
        Debug.Log("internal: " + sw.ElapsedMilliseconds);
        sw.Reset();

        //check external method speed
        sw.Start();
        //floats array gets updated here
        PopulateArray(floats, length);
        sw.Stop();
        Debug.Log("external: " +sw.ElapsedMilliseconds);

    }

    void Populate(ref float[] floats)
    {
        for (int i = 0,n = length; i<n;i++)
        {
            floats[i] = i;
        }
    }
}

关于速度:
-外部方法 (DLL) - 5 毫秒
- 内部方法(无效填充) - 23 毫秒

【问题讨论】:

  • 这可能吗?我想您可以通过两种语言/应用程序创建的 Web 服务发送数据。
  • 你可以使用 C++/CLI -> stackoverflow.com/questions/5655936/…
  • @FarhanQasim 这看起来像是在尝试提高内存数据的数字处理性能;使用 Web 服务将是实现这一点的最糟糕的方法,除非 Web 服务可以访问更强大的处理引擎(例如:多个 GPU,或者将 Web 服务作为通往服务器集群的网关可以分片数据)
  • 副本中的第一个链接显示了如何将数组从 C# 传递到 C++。第二个链接显示了如何做相反的事情。您必须在收到从 C++ 返回到 C# 的数组后释放它。您必须固定阵列,否则您的插件有时会导致 Unity 崩溃而有时会工作。此外,您将创建和销毁速度较慢的数组。这就是为什么人们决定将数组从 C# 传递到 C++ 以进行修改。我建议您阅读 this 帖子,因为它描述了您在此问题中提到的大多数内容的解决方案。
  • @richej CLI 并非与每个平台都兼容。 OP 应该用纯 C 或 C++ 编写插件。

标签: c# c++ arrays unity3d


【解决方案1】:

.NET 和 C++ 之间的 P/Invoke 支持非常好。通常它相当于使用[DllImport] 声明一些extern 方法来告诉运行时在哪里可以找到外部dll。传递数组时,关键问题是谁在分配它们。如果 C# 代码正在分配它们,那么您通常可以将它们传入 - 例如这里:https://stackoverflow.com/a/5709048/23354。如果 C++ 代码正在分配它们,那么您的 C# 代码将需要在指针中进行对话,这意味着 unsafe,您不允许这样做(来自问题)。所以如果可能的话:让 C# 代码拥有数组。

如果数组不完全匹配任何可用调用 - 或者您需要对数组应用偏移量 - 那么您可以声明 extern 方法以采用 IntPtr 或一些 int* 等,但是你回到unsafe 登陆这里。您将使用fixed 将引用转换为指针,即fixed(int* ptr = arr) { SomeExternalCall(ptr + offset, arr.Length - offset); }。但如果可能的话,使用简单的“传递数组”方法会更简单。

请注意,如果方法不是同步的 - 即如果 C++ 代码将保持指针一段时间 - 那么您将需要通过 GCHandle 手动固定数组。 P/Invoke 代码知道在单个 extern 调用期间固定一个数组(就像在上面的示例中 fixed 会做的那样),但它不能保证在之后不移动它 该调用已完成 - 这将使 C++ 代码持有的指针不可靠。 GCHandle 解决了这个问题,方法是在您需要的任何时间段内将对象固定到位。

不过,坦率地说; C# 在处理数据方面还不错 - 即使使用 100% 安全的代码。如果您的 C++ 实现尚未编写,我建议您首先尝试的是:简单地改进 C# 实现以提高效率。

【讨论】:

  • 嗨,马克,谢谢你的回答。我正在阅读有关编组的信息,但这令人困惑。我已经在 C# 中用我的算法摸索了。改进它的唯一方法(据我所知)是使用不安全的代码(我不能)。该算法相对简单(在数组中查找点,相应地更新其他数组)。问题是图像尺寸很大,因此您可以通过数组的速度成为瓶颈。 Unsafe 会改进它,因为我可以在裸内存上工作,但不能使用它,所以我想到了 C++。看起来程序员找到我的主题就是答案。
  • 关于“简单地改进 C# 实现以提高效率。” - 我已经花费了大量时间优化它,这是下一步可能会提供更好的结果。在用“舒适”的答案束缚自己之前,我想探索所有可能性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-04
  • 1970-01-01
  • 1970-01-01
  • 2012-11-06
  • 2012-02-03
相关资源
最近更新 更多