在iOS Metal中为MTLBuffer使用的数据分配内存

7
作为对此答案的跟进问题。我正在尝试用Metal中的内核函数替换在CPU上运行的for循环,以并行化计算并加快性能。
我的函数基本上是卷积。由于我反复接收到输入数组值的新数据(数据源自AVCaptureSession),因此使用newBufferWithBytesNoCopy:length:options:deallocator:创建MTLBuffer对象似乎是明智的选择。下面是相关代码:
id <MTLBuffer> dataBuffer = [device newBufferWithBytesNoCopy:dataVector length:sizeof(dataVector) options:MTLResourceStorageModeShared deallocator:nil];
id <MTLBuffer> filterBuffer = [device newBufferWithBytesNoCopy:filterVector length:sizeof(filterVector) options:MTLResourceStorageModeShared deallocator:nil];
id <MTLBuffer> outBuffer = [device newBufferWithBytesNoCopy:outVector length:sizeof(outVector) options:MTLResourceStorageModeShared deallocator:nil];

当运行此代码时,会出现以下错误:
``` failed assertion `newBufferWithBytesNoCopy:pointer 0x16fd0bd48 is not 4096 byte aligned.' ```
目前,我没有分配任何内存,但(为了测试目的)只是创建一个大小固定的空浮点数数组并用随机数填充它。因此,我的主要问题是:
如何正确地分配这些浮点数数组,以满足以下要求?
``` 该值必须导致页面对齐的内存区域。 ```
另外,还有一些额外的问题:
- 使用 `newBufferWithBytesNoCopy` 方法创建 `MTLBuffer` 是否有意义,或者复制数据在性能方面不是一个问题? (我的实际数据将包含每个视频帧约43000个浮点值。) - `MTLResourceStorageModeShared` 是否是 `MTLResourceOptions` 的正确选择? - API 参考文档说:
``` 返回的新 MTLBuffer 对象的存储分配与指针输入值相同。现有内存分配必须由单个 VM 区域覆盖,通常使用 vm_allocate 或 mmap 分配。不允许使用 malloc 分配内存。 ```
这仅适用于输出缓冲区吗?还是所有用于 `MTLBuffer` 的对象的存储分配都不能使用 `malloc` 进行?
1个回答

8

最简单的分配页面对齐内存的方法是使用 posix_memalign。以下是创建带有页面对齐内存的缓冲区的完整示例:

void *data = NULL;
NSUInteger pageSize = getpagesize();
NSUInteger allocationSize = /* required byte count, rounded up to next multiple of page size */ pageSize * 10;
int result = posix_memalign(&data, pageSize, allocationSize);

if (result == noErr && data) {
    id<MTLBuffer> buffer = [device newBufferWithBytesNoCopy:data
                                                     length:allocationSize
                                                    options:MTLResourceStorageModeShared
                                                deallocator:^(void *pointer, NSUInteger length)
                                                            {
                                                                free(pointer);
                                                            }];
    NSLog(@"Created buffer of length %d", (int)buffer.length);
}

由于您不能确保数据会到达页面对齐的指针,因此最好只分配一个能容纳您的数据的MTLBuffer,而不是使用不带副本的变体。如果您需要对数据进行实时处理,则应创建一个缓冲区池并在它们之间循环,而不是等待每个命令缓冲区完成。这些用例需要使用Shared存储模式。与malloc相关的警告仅适用于不带副本的情况,因为在其他情况下,Metal将为您分配内存。


嗨@warrenm,关于循环缓冲区的问题想请教您。我确实需要实时处理数据,但是我在我的AVCaptureVideoDataOutput上设置了setAlwaysDiscardsLateVideoFrames:YES,因此在我完成旧帧的所有计算(包括Metal部分)之前,新帧永远不会被处理。在这种情况下,是否不需要创建缓冲池,因为我始终可以使用相同的缓冲区? - Maxi Mus
您不希望在Metal处理过程中阻塞捕获输出队列,因此您将把工作交给Metal命令队列以异步执行。根据处理一帧所需的时间长短,您可能会同时有多个帧在运行。如果是这种情况,您仍应使用缓冲池来避免读写冲突或不必要的阻塞。如果您发现总是(或经常)在下一帧到达之前完成处理,则可以减少缓冲池大小甚至完全消除它。 - warrenm
我不明白。我认为使用上述方法丢弃晚到的视频帧正是这样做的,即强制处理始终在下一帧处理之前完成,并且丢弃早到的内容。 - Maxi Mus
1
这适用于在 CPU 上进行的处理,但没有理由在已经分派给 GPU 的工作完成时阻塞 CPU。 - warrenm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接