faster way to return data as an array interoping c++

烂漫一生 提交于 2019-12-17 21:34:03

问题


this is a very clean and nice solution to marsahall a struct array from unmanaged C++ code. it is allmost perfect solution when it comes to simplicity, it took me a while to get to that level of understanding the concept, so that in few lines of code, as you can see C# Main(), i have a populated array of struct ready to be 'harvested'..

typedef struct {
int Id;
BSTR StrVal;
}Package;

extern "C" __declspec(dllexport) void dodata(int requestedLength,int StringSize, Package **Packs){

    int count;
    count=0;
    *Packs = (Package*)LocalAlloc(0, requestedLength * sizeof(Package));
    Package *Cur = *Packs;
    while(count!= requestedLength)
    {
        Cur[count].StrVal = NULL;
        Cur[count].Id = count;
        Cur[count].StrVal=SysAllocString(L"abcdefghij");
        Cur[count].StrVal[StringSize-1]=count+'0';

    ++count;

    }

}

C#

[DllImport(@"ExportStructArr.dll", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void dodata(int requestedLength, int StringSize, out IntPtr csPkPtr);


[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct csPk
{
    public int V;
    [MarshalAsAttribute(UnmanagedType.BStr, SizeConst = 10)]
    public string testStr;
}

static void Main(string[] args){

    int ArrL = 16000;
    csPk[] Cpk = new csPk[ArrL];
    IntPtr CpkPtr = IntPtr.Zero;
    int szPk = Marshal.SizeOf(typeof(csPk));
    dodata(ArrL, 10, out CpkPtr);
}

now all i have to do is :

        for (int i = 0; i < Cpk.Length; i++)
        {
            Cpk[i] = (csPk)Marshal.PtrToStructure(new IntPtr(CpkPtr.ToInt32() + (szPk * i)), typeof(csPk));
        }

the solution is quite easy as you can see the question is using unsafe or any kind of transformation to the data, even going down to bytes...

how could i optimize it to perform better returning the data ?

Edit:

links i have tried to learn from other answers here in SO:

  • answer from Hans Passant
  • answer from AbdElRaheim
  • answer from supercat

also tried google : wikipedia , a github post by stephentoub


回答1:


this is a complete blazing fast solution to populate a list of objects, i did my best and i will be happy to have comments and suggestions.

c++

typedef struct _DataPacket
{
    BSTR buffer;
    UINT size;
} DataPacket;

extern "C" __declspec(dllexport)  void GetPacksUnsafe( int size, DataPacket** DpArray )
{
    int szr = size;int count=0;
    *DpArray = (DataPacket*)CoTaskMemAlloc( szr * sizeof( DataPacket ));

    if ( DpArray != NULL )
    {
        DataPacket* CurPack = *DpArray;

        for ( int i = 0; i < szr; i++, CurPack++ )
        {
            CurPack->size = i;
            CurPack->buffer = SysAllocString(L"SomeText00");
            CurPack->buffer[9]=i+'0';   

        }
    }
}

C#

    [DllImport(@"ExportStructArr.dll", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
    public static extern void GetPacksUnsafe(int size, PackU** outPackUArr);

    [StructLayout(LayoutKind.Sequential)]
    public unsafe struct PackU
    {
        public char* StrVal;
        public int IntVal;
    }


    public static unsafe List<PackU> PopulateLstPackU(int ArrL)
    {
        PackU* PackUArrOut;
        List<PackU> RtLstPackU = new List<PackU>(ArrL);
        GetPacksUnsafe(ArrL, &PackUArrOut);
        PackU* CurrentPack = PackUArrOut;
        for (int i = 0; i < ArrL; i++, CurrentPack++)
        {
            RtLstPackU.Add(new PackU(){ StrVal = CurrentPack->StrVal, IntVal=CurrentPack->IntVal});
        }
        Marshal.FreeCoTaskMem((IntPtr)PackUArrOut);

        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine("{0}", new string(RtLstPackU[i].StrVal));
        }
        return RtLstPackU;
    }

using the code is as simple as it could possibly be

    static unsafe void Main(string[] args)
    {
        int ArrL = 100000;
        List<PackU> LstPackU;
        LstPackU = PopulateLstPackU(ArrL);
    }

there you have a list of custom data as fast as a bullet..

EDIT

using pointers instead of strings :

typedef struct _DataPackCharPnt
{
    char* buffer;
    UINT IntVal;
} DataPackCharPnt;


extern "C" __declspec(dllexport)  void GetPacksPnt( int size, DataPackCharPnt** DpArrPnt )
{

    int count = 0;
    int TmpStrSize = 10;
    *DpArrPnt = (DataPackCharPnt*)CoTaskMemAlloc( size * sizeof( DataPackCharPnt ));
    DataPackCharPnt* CurPackPnt = *DpArrPnt;
    char dummyStringDataObject[]= "abcdefgHi";
    for ( int i = 0; i < size; i++,CurPackPnt++ )
    {

        dummyStringDataObject[9] = i+'0';
        CurPackPnt->IntVal=i;
        CurPackPnt->buffer = (char*)malloc(sizeof(char)*TmpStrSize);
        strcpy(CurPackPnt->buffer, dummyStringDataObject);

    }
}

reduced the time taken from 11 to 7 ms populating 100k elements

is there any part of creating the buffer i could omit ?

  • the duty of dummyStringDataObject is to simulate work, say getting a file name then set the buffer with its value, so except for this extra time which is the whole purpose of this function, to return some unknown values and lengths of the strings...

could you optimize it even further ?



来源:https://stackoverflow.com/questions/33219095/faster-way-to-return-data-as-an-array-interoping-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!