1

I know this question has already been asked and might be considered duplicate, but after reading other answers I am still quite baffled and do not understand explanation that is provided there.

I would be very grateful if someone could provided a sample code and some explanation.

I am working on following task:
I have a texture/textures (cannot be loaded from drive, generated on fly) in Unity and I convert it into an array. It can be an array of int, byte or float.

Currently, I do my object detection in C#, but I want to make it faster. Therefore, I want to do following.
Access a C++ code (from dll, I have just started my journey with c++) and run a method which would take my input array, do calculations and return a new array (old is not modified) to C# code.

Alternatively, I can input two arrays (preferred) where one of them is modified.
Output array does not have to be created every time, it can be overwritten. I cannot use unsafe code in C# as this is for asset purposes (compatibility issues).

Mock-up code

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

I have found answer to this questions here: Updating a C# array inside C++ without Marshal.Copy or Unsafe

Sample solution:

c++ code:

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

    return true;
}

C# code

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

Regarding speed:
-external method (DLL) - 5 ms
-internal method (void Populate) - 23 ms

13
  • Is this possible? I guess you can send the data through web services created in both the languages/applications. Commented Jan 26, 2018 at 10:38
  • 3
    you could use C++/CLI -> stackoverflow.com/questions/5655936/… Commented Jan 26, 2018 at 10:40
  • 1
    @FarhanQasim this looks like an attempt to improve the performance of number-crunching of in-memory data; going to a web-service would be the worst possible way to implement this, unless the web-service has access to a massively more powerful processing engine (like: multiple GPUs, or with the web-service as a gateway to a cluster of servers that can shard the data) Commented Jan 26, 2018 at 10:57
  • 1
    The first link in the duplicate shows how to pass array from C# to C++. The second link shows how to do the opposite. You must free it after receiving the array returned from C++ to C#. You have to pin the array otherwise expect your plugin to crash Unity sometimes and work other times. Also you will be creating and destroying arrays which is slow. That's why people decide to pass array from C# to C++ to be modified. I suggest you read this post as it describes solution to most of the stuff you mentioned in this question. Commented Jan 26, 2018 at 13:12
  • 1
    @richej CLI is not compatible with every platform. OP should write the plugin in pure C or C++. Commented Jan 26, 2018 at 13:12

1 Answer 1

4

The P/Invoke support between .NET and C++ is pretty good. Usually it amounts to declaring some extern methods using [DllImport] to tell the runtime where to find the external dll. When passing arrays, the key question is who is allocating them. If the C# code is allocating them, then you can usually just pass them in - for example like here: https://stackoverflow.com/a/5709048/23354. If the C++ code is allocating them, then your C# code will need to talk in pointers, which means unsafe, which you're not allowed to do (from the question). So if possible: have the C# code own the arrays.

If the array doesn't cleanly match any of the available calls - or you need to apply offsets to the array - then you can declare the extern method to take an IntPtr or some int* etc, but you're back in unsafe land here. You would use fixed to turn the reference into a pointer, i.e. fixed(int* ptr = arr) { SomeExternalCall(ptr + offset, arr.Length - offset); }. But if possible, using the simple "pass the array" approach is simpler.

Note that if the methods are not synchronous - i.e. if the C++ code will keep hold of the pointer for some time - then you will need to manually pin the array via a GCHandle. The P/Invoke code knows to pin an array for the duration of a single extern call (just like fixed would do, in the example above), but it doesn't guarantee not to move it after that call has completed - which would make the pointer that the C++ code holds unreliable. A GCHandle solves this, by pinning the object in place for whatever period you need.

Frankly, though; C# isn't bad at processing data - even when using 100% safe code. If your C++ implementation isn't already written, I would suggest the first thing to try would be: to simply improve the C# implementation to be more efficient.

Sign up to request clarification or add additional context in comments.

2 Comments

Hi Marc, thank you for the answer. I am reading about marshaling, but it is confusing. I am already scratching bottom with my algorithm in c#. The only way to improve it (as far I can see it) is to use unsafe code (which I cannot). The algorithm is relatively simple (look for points in array, update other array accordingly). The problem is that images are of significant size, so speed at which you can go through array becomes bottleneck. Unsafe would improve it as I could work on bare memory, but cannot use it, so I though about c++. It looks that topics that Programmer found my be the answer.
Regarding "to simply improve the C# implementation to be more efficient." - I have already spent significant amount of time optimising it and this is next step which might provide better results. I want to explore all possibilities before caging myself with "comfortable" answers.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.