1

这可能只是.NET框架分配的内存对象没有正确页面对齐的问题,但我不明白为什么零拷贝对我来说比非零拷贝慢。

我将在这个问题中包含内联代码,但可以在此处查看完整的源代码:https ://github.com/kwende/ClooMatrixMultiply/blob/master/GiantMatrixOnGPU/GPUMatrixMultiplier.cs 。

由于这是我第一次尝试让零拷贝工作,我写了一个简单的矩阵乘法示例。我首先初始化我的 OpenCL 对象:

    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");
    }

...如果我的代码尚未初始化,这只会执行一次。然后我进入主体。对于非零副本,我执行以下操作:

  public float[] MultiplyMatrices(float[] matrix1, float[] matrix2,
  int matrix1Height, int matrix1WidthMatrix2Height, int matrix2Width)
  {
        if (!_initialized)
        {
            Initialize();
            _initialized = true;
        }

        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.ReadWrite | 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();
            }
        }

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

        return ret;
    }

您可以看到我如何为我的所有 ComputeBuffer 分配显式设置 CopyHostPointer。这执行得很好。

然后我对(包括设置“UseHostPointer”和调用 Map/Unmap 而不是 Read)进行以下调整:

    public float[] MultiplyMatricesZeroCopy(float[] matrix1, float[] matrix2,
        int matrix1Height, int matrix1WidthMatrix2Height, int matrix2Width)
    {
        if (!_initialized)
        {
            Initialize();
            _initialized = true;
        }

        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.ReadWrite | ComputeMemoryFlags.UseHostPointer,
            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);

        IntPtr retPtr = _commandQueue.Map(
            retBuffer,
            false,
            ComputeMemoryMappingFlags.Read,
            0,
            ret.Length, null);

        _commandQueue.Unmap(retBuffer, ref retPtr, null);
        _commandQueue.Finish();

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

        return ret;
    }

然而,时机说明了一切。我的程序吐出这个:

CPU矩阵乘法:1178.5ms

GPU矩阵乘法(复制):115.1ms

GPU矩阵乘法(零拷贝):174.1ms

GPU(带副本)快 10.23892 倍。

GPU(零拷贝)快 6.769098 倍。

...所以零拷贝速度较慢。

4

1 回答 1

2

感谢 huseyin tugrul buyukisik,我能够弄清楚发生了什么。

我需要更新我的英特尔驱动程序。一旦我这样做了,那么零拷贝就会快得多。

为了后人,这里是零拷贝代码的最终版本:

    public float[] MultiplyMatricesZeroCopy(float[] matrix1, float[] matrix2,
        int matrix1Height, int matrix1WidthMatrix2Height, int matrix2Width)
    {
        if (!_initialized)
        {
            Initialize();
            _initialized = true;
        }

        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];
        GCHandle handle = GCHandle.Alloc(ret, GCHandleType.Pinned); 
        ComputeBuffer<float> retBuffer = new ComputeBuffer<float>(_context,
            ComputeMemoryFlags.UseHostPointer,
            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);

        IntPtr retPtr = _commandQueue.Map(
            retBuffer,
            true,
            ComputeMemoryMappingFlags.Read,
            0,
            ret.Length, null);

        _commandQueue.Unmap(retBuffer, ref retPtr, null);
        //_commandQueue.Finish();

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

        return ret;
    }
于 2017-02-16T21:55:20.133 回答