Как правильно преобразовать IntPtr в массив при использовании памяти устройства в гибридизаторе - PullRequest
1 голос
/ 17 июня 2019

Я пишу код на C # для обработки CUDA GPU с использованием Hybridizer.Моя проблема в том, что я не понимаю, как передать объекты, хранящиеся в памяти устройства, в код гибридизатора и получаю RuntimeBinderException.

У меня определена точка входа гибридизатора: -

[EntryPoint]
static void Multiply(Complex[] a, Complex[] b,float[] d,int len)
{            
}

Этоработает нормально, когда массивы Complex хранятся в памяти хоста, но я пытаюсь минимизировать передачу данных между хостом и устройством.Поэтому я ранее передал массивы в память устройства: -

    static void Main(string[] args)
    {
        int numelements = 12;
        Complex[] a = new Complex[numelements];
        Complex[] b = new Complex[numelements];
        float[] d = new float[numelements];
        int memsize = numelements * Marshal.SizeOf(new Complex());

        GCHandle aH = GCHandle.Alloc(a, GCHandleType.Pinned);
        GCHandle bH = GCHandle.Alloc(b, GCHandleType.Pinned);
        IntPtr da, db;
        cuda.ERROR_CHECK(cuda.Malloc(out da, memsize));
        cuda.ERROR_CHECK(cuda.Malloc(out db, memsize));

        cuda.ERROR_CHECK(cuda.Memcpy(da, aH.AddrOfPinnedObject(), memsize, cudaMemcpyKind.cudaMemcpyHostToDevice));
        cuda.ERROR_CHECK(cuda.Memcpy(db, bH.AddrOfPinnedObject(), memsize, cudaMemcpyKind.cudaMemcpyHostToDevice));

        HybRunner runner = HybRunner.Cuda();
        dynamic wrapped = runner.Wrap(new Program());
        runner.saveAssembly();

// the following line gives a runtimeBinderException as detailed below
        wrapped.Multiply(da, db,d, numelements);

        cuda.DeviceSynchronize();
    }

Сообщение = Наилучший перегруженный метод соответствует для Program_wrapped_CUDA.Multiply (Hybridizer.Runtime.CUDAImports.float2 [], Hybridizer)..Runtime.CUDAImports.float2 [], float [], int) 'имеет несколько недопустимых аргументов

Как мне:

  • привести IntPtrs к Complex []?
  • перенастроить список параметров для умножения и затем получить доступ к содержимому массива?

1 Ответ

1 голос
/ 17 июня 2019

Я приму предположение, что d хранит результат умножения. Если это не так, вы сможете легко изменить код.

Проблема заключается в том, что вы смешиваете IntPtr (маршалирование вручную) и управляемые типы (маршалирование автоматически).

Вы можете увидеть созданную оболочку, запустив runner.saveAssembly(); после вызова Wrap:

HybRunner runner = HybRunner.Cuda();
dynamic wrapped = runner.Wrap(new Program());
runner.saveAssembly();

Затем запустите IlSpy , чтобы проверить сгенерированную dll и увидеть все создаваемые перегрузки:

generated overloads

Как видите, одна перегрузка с массивами, а другая с native int.

первый предназначен для автоматического управления памятью, а второй - для ручного управления памятью.

Однако вы не можете смешивать оба.

В качестве решения вы должны вручную управлять памятью для вашего параметра d:

static void Main(string[] args)
{
    int numelements = 12;
    Complex[] a = new Complex[numelements];
    Complex[] b = new Complex[numelements];
    float[] d = new float[numelements];
    int memsize = numelements * Marshal.SizeOf(new Complex());

    GCHandle aH = GCHandle.Alloc(a, GCHandleType.Pinned);
    GCHandle bH = GCHandle.Alloc(b, GCHandleType.Pinned);
    GCHandle dH = GCHandle.Alloc(d, GCHandleType.Pinned);

    IntPtr da, db;
    cuda.ERROR_CHECK(cuda.Malloc(out da, memsize));
    cuda.ERROR_CHECK(cuda.Malloc(out db, memsize));
    IntPtr dd;
    cuda.ERROR_CHECK(cuda.Malloc(out dd, numelements * sizeof(float)));

    cuda.ERROR_CHECK(cuda.Memcpy(da, aH.AddrOfPinnedObject(), memsize, cudaMemcpyKind.cudaMemcpyHostToDevice));
    cuda.ERROR_CHECK(cuda.Memcpy(db, bH.AddrOfPinnedObject(), memsize, cudaMemcpyKind.cudaMemcpyHostToDevice));

    HybRunner runner = HybRunner.Cuda();
    dynamic wrapped = runner.Wrap(new Program());
    runner.saveAssembly();

    // the following line gives a runtimeBinderException as detailed below
    wrapped.Multiply(da, db, dd, numelements);

    cuda.Memcpy(dH.AddrOfPinnedObject(), dd, numelements * sizeof(float), cudaMemcpyKind.cudaMemcpyDeviceToHost);

    cuda.DeviceSynchronize();
}

Однако в вашем случае я бы использовал тип ResidentArray , предоставляемый Hybridizer. Они предназначены, чтобы помочь вам с такими проблемами.

[EntryPoint]
static void Multiply(ResidentArrayGeneric<Complex> a, ResidentArrayGeneric<Complex> b, FloatResidentArray d, int len)
{

}

static void Main(string[] args)
{
    int numelements = 12;
    var a = new ResidentArrayGeneric<Complex>(numelements);
    var b = new ResidentArrayGeneric<Complex>(numelements);
    var d = new FloatResidentArray(numelements);

    // populate a and b

    a.RefreshDevice();
    b.RefreshDevice();


    HybRunner runner = HybRunner.Cuda();
    dynamic wrapped = runner.Wrap(new Program());
    runner.saveAssembly();

    // the following line gives a runtimeBinderException as detailed below
    wrapped.Multiply(a, b, d, numelements);

    d.RefreshHost();

    cuda.DeviceSynchronize();
}

Более подробный образец вы можете найти с ResidentArrays здесь

...