【问题标题】:Retrieving Results from Kernel从内核中检索结果
【发布时间】:2016-11-09 08:24:09
【问题描述】:

我在玩内核函数时遇到了一些问题。

我想做的只是将一个数组发送到函数,然后在数组中waitUntilCompleted 之后返回结果。

下面是一个数组,在循环中malloc之后会填充0到123455之间的数字:

float *myVector = malloc(123456 * sizeof(float));

这里是数组,连同myVector,将被发送到内核:

float *resultData =  malloc(123456 * sizeof(float));
id <MTLBuffer> inBuffer = [device newBufferWithBytes:&myVector[0] length:sizeof(myVector) options:MTLResourceOptionCPUCacheModeDefault];
id <MTLBuffer> buffer = [device newBufferWithBytes:&resultData[0] length:sizeof(resultData) options:MTLResourceOptionCPUCacheModeDefault];

使用计算命令编码器,它们都设置为索引01,偏移量分别为0

以下设置线程组和组内线程的大小:

MTLSize threadGroupCounts = MTLSizeMake([device maxThreadsPerThreadgroup].width, 1, 1);
MTLSize threadGroups = MTLSizeMake((123456) / threadGroupCounts.width, 1, 1);

[commandEncoder dispatchThreadgroups:threadGroups threadsPerThreadgroup:threadGroupCounts];

[commandEncoder endEncoding];
[commandBuffer commit];
[commandBuffer waitUntilCompleted];

我收到以下错误的两倍:

由于在执行过程中发生错误,命令缓冲区的执行被中止 执行。导致 GPU 挂起错误(IOAF 代码 3)

在花了几个小时之后,我得出的结论是错误是由以下几行引起的:

MTLSize threadGroupCounts = MTLSizeMake([device maxThreadsPerThreadgroup].width, 1, 1);
MTLSize threadGroups = MTLSizeMake((123456) / [device maxThreadsPerThreadgroup].width, 1, 1);

例如,如果我将(123456) / [device maxThreadsPerThreadgroup].width 设置为32,则不会发生错误,但结果将全为零,除了数组中的前两个值。

这是我在处理后尝试获得结果的方式:

NSData *data = [NSData dataWithBytesNoCopy:buffer.contents length:sizeof(myVector) freeWhenDone:NO];
float *finalArray = malloc(sizeof(float) * 123456);
[data getBytes:&finalArray[0] length:sizeof(finalArray)];

函数如下:

kernel void test(const device float *inVector [[buffer (0)]],
                 device float *outVector [[buffer (1)]],
                 uint id [[thread_position_in_grid]])
{
    outVector[id] = -inVector[id]; 
}

我认为我在设置线程大小时遇到​​了问题。 作为测试,我想要实现的是设置每个线程组允许的最大线程数,将数组的大小除以该数字并将其发送到处理。有人可以告诉我如何设置线程组大小,将数组发送到函数并最终正确正确地检索数组中的结果吗?

谢谢。

【问题讨论】:

  • 您计算MTLBuffers 大小的方式有误。因为myVector 是一个指针,所以sizeof(myVector) 可能是8,而不是493824。这反过来又会导致您没有为数据分配足够的空间,并且读取超出内核函数中缓冲区的范围。尝试在创建缓冲区时使用与使用 malloc 分配浮点数组时相同的大小,看看是否有帮助。
  • 您还用正确的计数替换了使用 getBytes 复制的数据量?
  • 我认为您计算线程组大小和计数的方式是合理的,但您应该注意整数截断。如果maxThreadsPerThreadgroup 的宽度假设恰好是 128,则计算线程组数量的除法将产生 964.5,它会被截断为 964,导致缓冲区的最后 64 个元素不被处理。如果您需要在这样的异常大小的网格上进行计算,通常最好将您调度的线程组的数量四舍五入,然后在着色器中手动防止越界访问。
  • @warrenm 您能否在回答中引用一个简单的示例来说明您刚才提到的关于防止未通过舍入处理的元素的补救措施以及防止出局的方法着色器中的边界?
  • 完成!希望对您有所帮助。

标签: metal


【解决方案1】:

您计算MTLBuffers 大小的方式有误。因为myVector 是一个指针,所以sizeof(myVector) 可能是8,而不是493824。这反过来又会导致您没有为数据分配足够的空间,并且读取超出内核函数中缓冲区的范围。尝试在创建缓冲区时使用与使用 malloc 分配浮点数组时相同的大小,看看是否有帮助。

您需要使用getBytes:length: 对从输出缓冲区检索的字节数进行相应的更改。

我认为您计算线程组大小和计数的方式是合理的,但您应该注意整数截断。如果要处理的元素总数不能被线程组大小整除,则计算线程组计数的方式将向下取整,从而导致您跳过一些元素。

避免这种情况的一种方法是将您调度的线程组的数量取整,并明确检查缓冲区长度以防止越界访问。所以你会像这样计算你的线程组数量和大小:

const int elementCount = 123456;
MTLSize threadgroupSize = MTLSizeMake([device maxThreadsPerThreadgroup].width, 1, 1);
MTLSize threadgroups = MTLSizeMake(ceil(elementCount / (float)threadgroupSize.width), 1, 1);

...像这样传递缓冲区大小:

[computeCommandEncoder setBytes:&elementCount length:sizeof(elementCount) atIndex:2];

...并像这样检查边界:

kernel void test(const device float *inVector [[buffer (0)]],
                 device float *outVector [[buffer (1)]],
                 constant int &elementCount [[buffer (2)]],
                 uint id [[thread_position_in_grid]])
{
    if (id < elementCount) {
        outVector[id] = -inVector[id];
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-14
    相关资源
    最近更新 更多