2017-02-16 24 views
1

Я экспериментирую с OpenCL (через Cloo интерфейс Cloo). Для этого я экспериментирую с обычным матричным умножением на GPU. Проблема в том, что во время моих тестов скорости приложение аварийно завершает работу. Я пытаюсь быть эффективным в отношении перераспределения различных объектов OpenCL, и мне интересно, не могу ли я что-то делать с этим.Я правильно использую объекты OpenCL/Cloo (C#)?

Я поставил код в этом вопросе, но для большей картины, вы можете получить код от GitHub здесь: https://github.com/kwende/ClooMatrixMultiply

Моя главная программа делает это:

 Stopwatch gpuSw = new Stopwatch(); 
     gpuSw.Start(); 
     for (int c = 0; c < NumberOfIterations; c++) 
     { 
      float[] result = gpu.MultiplyMatrices(matrix1, matrix2, MatrixHeight, MatrixHeight, MatrixWidth); 
     } 
     gpuSw.Stop(); 

Так что я m в основном выполняет вызов NumberOfIterations раз, а также время выполнения среднего времени выполнения.

В MultiplyMatrices вызова, первый раз через, я называю Инициализировать, чтобы настроить все объекты, я собираюсь использовать повторно:

private void Initialize() 
    { 
     // get the intel integrated GPU 
     _integratedIntelGPUPlatform = ComputePlatform.Platforms.Where(n => n.Name.Contains("Intel")).First(); 

     // create the compute context. 
     _context = new ComputeContext(
      ComputeDeviceTypes.Gpu, // use the gpu 
      new ComputeContextPropertyList(_integratedIntelGPUPlatform), // use the intel openCL platform 
      null, 
      IntPtr.Zero); 

     // the command queue is the, well, queue of commands sent to the "device" (GPU) 
     _commandQueue = new ComputeCommandQueue(
      _context, // the compute context 
      _context.Devices[0], // first device matching the context specifications 
      ComputeCommandQueueFlags.None); // no special flags 

     string kernelSource = null; 
     using (StreamReader sr = new StreamReader("kernel.cl")) 
     { 
      kernelSource = sr.ReadToEnd(); 
     } 

     // create the "program" 
     _program = new ComputeProgram(_context, new string[] { kernelSource }); 

     // compile. 
     _program.Build(null, null, null, IntPtr.Zero); 
     _kernel = _program.CreateKernel("ComputeMatrix"); 
    } 

Я тогда войти в основное тело моей функции (часть , который будет выполнен NumberOfIterations раз).

  ComputeBuffer<float> matrix1Buffer = new ComputeBuffer<float>(_context, 
       ComputeMemoryFlags.ReadOnly| ComputeMemoryFlags.CopyHostPointer, 
       matrix1); 
     _kernel.SetMemoryArgument(0, matrix1Buffer); 

     ComputeBuffer<float> matrix2Buffer = new ComputeBuffer<float>(_context, 
      ComputeMemoryFlags.ReadOnly | ComputeMemoryFlags.CopyHostPointer, 
      matrix2); 
     _kernel.SetMemoryArgument(1, matrix2Buffer); 

     float[] ret = new float[matrix1Height * matrix2Width]; 
     ComputeBuffer<float> retBuffer = new ComputeBuffer<float>(_context, 
      ComputeMemoryFlags.WriteOnly | ComputeMemoryFlags.CopyHostPointer, 
      ret); 
     _kernel.SetMemoryArgument(2, retBuffer); 

     _kernel.SetValueArgument<int>(3, matrix1WidthMatrix2Height); 
     _kernel.SetValueArgument<int>(4, matrix2Width); 

     _commandQueue.Execute(_kernel, 
      new long[] { 0 }, 
      new long[] { matrix2Width ,matrix1Height }, 
      null, null); 

     unsafe 
     { 
      fixed (float* retPtr = ret) 
      { 
       _commandQueue.Read(retBuffer, 
        false, 0, 
        ret.Length, 
        new IntPtr(retPtr), 
        null); 

       _commandQueue.Finish(); 
      } 
     } 

Третий или четвертый раз через (это несколько случайно, что намекает на проблемы доступа к памяти), программа падает. Вот мое ядро ​​(я уверен, что есть более быстрые реализации, но сейчас моя цель состоит в том только, чтобы получить что-то работает не взорвав):

kernel void ComputeMatrix(
    global read_only float* matrix1, 
    global read_only float* matrix2, 
    global write_only float* output, 
    int matrix1WidthMatrix2Height, 
    int matrix2Width) 
{ 
    int x = get_global_id(0); 
    int y = get_global_id(1); 
    int i = y * matrix2Width + x; 

    float value = 0.0f; 
    // row y of matrix1 * column x of matrix2 
    for (int c = 0; c < matrix1WidthMatrix2Height; c++) 
    { 
     int m1Index = y * matrix1WidthMatrix2Height + c; 
     int m2Index = c * matrix2Width + x; 

     value += matrix1[m1Index] * matrix2[m2Index]; 
    } 
    output[i] = value; 
} 

В конечном счете цель здесь, чтобы лучше понять особенности нулевого копирования OpenCL (поскольку я использую встроенный графический процессор Intel). У меня возникли проблемы с тем, чтобы заставить его работать, поэтому мне хотелось немного отступить, чтобы понять, поняли ли я еще более простые вещи ... видимо, я не понимаю, как я не могу заставить это работать без взрыва.

только другая вещь Я могу думать о том, как я привязываю указатель, чтобы отправить его функции .Read(). Но я не знаю альтернативы.

Edit:

Для чего это стоит, я обновил последнюю часть кода (код чтения) к этому, и он по-прежнему падает:

_commandQueue.ReadFromBuffer(retBuffer, ref ret, false, null); 
_commandQueue.Finish(); 

Edit # 2

Решение найдено huseyin tugrul buyukisik (см. Комментарий ниже).

При размещении

matrix1Buffer.Dispose(); 
matrix2Buffer.Dispose(); 
retBuffer.Dispose(); 

В конце концов, все это работало нормально.

+1

Одна вероятность того, что вы воссоздаете без использования ресурсов буфера, которая пересекает границы opencl и сбои. Одна возможность, ret.Length находится в байтах и ​​должна быть умножена на sizeof (float) или лучше sizeof (cl_float). ресурсы opencl необходимы для утилизации. если вы повторно используете их, не уничтожая их (если они недоступны), вам не нужно устанавливать аргумент снова и снова. Если их область видимости - это только 'gpu.MultiplyMatrices (' вам следует переместить создание буферов в init part. он opencl 2.0 или 1.2? –

+0

Ты прибил его, мой друг. Я положил вызовы Dispose в конце функции, чтобы освободить буферы, и он исправил это. Оказывается, GC не справляется с нагрузкой на GPU по-памяти. –

+1

GC не следует доверять. Должна быть некоторая 'using() {}' реализация или явное де-распределение. Возможно, C# более надежный, но у java были проблемы. –

ответ

0

Ресурсы OpenCl, такие как буферы, ядра и командные объекты, должны быть освобождены после освобождения других ресурсов, с которыми они связаны. Повторное создание без быстрого освобождения доступных слотов.

Вы повторно создавали массивы в методе gpu, и это был объем буферов opencl. Когда он заканчивается, GC не может отслеживать неуправляемые области памяти opencl и вызывает утечки, что приводит к сбоям.

Многие реализации opencl используют привязки C++, которым требуются явные команды релиза с помощью C#, Java и других сред.

Кроме того, часть set-argument не требуется много раз, когда повторное выполнение ядра использует тот же самый порядок буфера, что и параметры ядра.

+0

Насколько вы знакомы с понятием нулевой копии? В конечном итоге то, что я пытаюсь сделать, это предотвратить копирование буферов (я использую Intel Integrated GPU, и поэтому процессор GPU и «хост» используют одно и то же адресное пространство). Что нужно изменить, чтобы поддержать это? Должен ли я все же выпустить ресурсы так же? –

+0

map/unmap является нулевой копией (с use_host_ptr), как я знаю. Вы делаете буферные копии. Также вы должны запросить параметры устройства, чтобы знать, что у него есть своя память или действительно обменивается памятью процессора. –

+0

Хорошо. Я буду экспериментировать с этим и, возможно, сделать еще один вопрос StackOverflow, если я не могу заставить его работать. Я нашел несколько примеров, но их довольно трудно читать. Я, скорее всего, просто сделаю новое сообщение, поэтому я держу его на ходу. Вы очень помогли мне с последними вопросами о StackOverflow. Я очень ценю это. –