Я экспериментирую с 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();
В конце концов, все это работало нормально.
Одна вероятность того, что вы воссоздаете без использования ресурсов буфера, которая пересекает границы opencl и сбои. Одна возможность, ret.Length находится в байтах и должна быть умножена на sizeof (float) или лучше sizeof (cl_float). ресурсы opencl необходимы для утилизации. если вы повторно используете их, не уничтожая их (если они недоступны), вам не нужно устанавливать аргумент снова и снова. Если их область видимости - это только 'gpu.MultiplyMatrices (' вам следует переместить создание буферов в init part. он opencl 2.0 или 1.2? –
Ты прибил его, мой друг. Я положил вызовы Dispose в конце функции, чтобы освободить буферы, и он исправил это. Оказывается, GC не справляется с нагрузкой на GPU по-памяти. –
GC не следует доверять. Должна быть некоторая 'using() {}' реализация или явное де-распределение. Возможно, C# более надежный, но у java были проблемы. –