首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >我是否正确地重用OpenCL/Cloo(C#)对象?

我是否正确地重用OpenCL/Cloo(C#)对象?
EN

Stack Overflow用户
提问于 2017-02-16 19:04:25
回答 1查看 1.5K关注 0票数 1

我正在试验OpenCL (通过Cloo的C#接口)。为了做到这一点,我正在用传统的矩阵乘法GPU进行实验。问题是,在我的速度测试中,应用程序崩溃了。我正在努力提高对各种OpenCL对象的重新分配的效率,我想知道这样做是否有什么问题。

我将把代码放在这个问题中,但更大的情况是,您可以从github获得以下代码:https://github.com/kwende/ClooMatrixMultiply

我的主程序是这样做的:

代码语言:javascript
复制
        Stopwatch gpuSw = new Stopwatch();
        gpuSw.Start();
        for (int c = 0; c < NumberOfIterations; c++)
        {
            float[] result = gpu.MultiplyMatrices(matrix1, matrix2, MatrixHeight, MatrixHeight, MatrixWidth);
        }
        gpuSw.Stop();

因此,我基本上是在执行调用NumberOfIterations时间,并对平均执行时间进行计时。

在MultiplyMatrices调用中,我第一次调用Initialize来设置我要重用的所有对象:

代码语言:javascript
复制
    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时间的部分)。

代码语言:javascript
复制
         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();
            }
        }

第三次或第四次(它有点随机,暗示内存访问问题),程序崩溃。下面是我的内核(我肯定有更快的实现,但现在我的目标只是让一些东西在不爆炸的情况下工作):

代码语言:javascript
复制
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的零拷贝特性(因为我使用的是英特尔的集成GPU)。我一直很难让它开始工作,所以我想退一步看看我是否理解了更基本的things...apparently,因为我甚至无法在不爆炸的情况下让它工作。

我唯一能想到的另一件事是如何将指针绑定到.Read()函数。但我不知道还有别的选择。

编辑

为了说明它的价值,我更新了代码的最后一部分(读代码),它仍然崩溃:

代码语言:javascript
复制
_commandQueue.ReadFromBuffer(retBuffer, ref ret, false, null);
_commandQueue.Finish(); 

编辑#2

解决方案由huseyin买方公司找到(见下面的评论)。

放置时

代码语言:javascript
复制
matrix1Buffer.Dispose();
matrix2Buffer.Dispose();
retBuffer.Dispose(); 

最后,一切都很顺利。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-02-16 20:07:23

OpenCl资源(如缓冲区、内核和命令队列)应该在其他资源被绑定后释放--释放。重新创建,而不释放耗尽贪婪的插槽迅速。

您一直在用gpu的方法重新创建数组,这就是opencl缓冲区的范围。完成后,GC无法跟踪opencl的非托管内存区域,从而导致泄漏,从而导致崩溃。

许多opencl实现使用C++绑定,这需要C#、Java和其他环境的显式发布命令。

而且,当重复的内核执行使用与内核参数完全相同的缓冲区顺序时,不需要多次使用set参数部分。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42282548

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档