【问题标题】:Allocating memory for data used by MTLBuffer in iOS Metal在 iOS Metal 中为 MTLBuffer 使用的数据分配内存
【发布时间】:2017-02-07 18:41:22
【问题描述】:

作为this answer 的后续问题。我正在尝试用 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];

运行时出现以下错误:

断言失败 `newBufferWithBytesNoCopy:pointer 0x16fd0bd48 is not 4096 byte alignment.'

现在,我没有分配任何内存,但(出于测试目的)只是创建一个固定大小的空浮点数组并用随机数填充它。所以我的主要问题是:

如何正确分配这些浮点数组,以满足以下要求

此值必须导致内存的页面对齐区域。

另外,还有一些问题:

  • 使用newBufferWithBytesNoCopy 方法创建MTLBuffer 是否有意义,或者复制数据在性能方面不是真正的问题? (我的实际数据将包含每个视频帧大约 43,000 个浮点值。)
  • MTLResourceStorageModeSharedMTLResourceOptions 的正确选择吗?
  • API 参考说明

    返回的新MTLBuffer对象的存储分配与指针输入值相同。现有的内存分配必须由单个 VM 区域覆盖,通常使用 vm_allocate 或 mmap 进行分配。明确禁止 malloc 分配的内存。

    这是否仅适用于输出缓冲区,或者是否应该为 MTLBuffer 使用的所有对象分配存储空间而不是 malloc

【问题讨论】:

    标签: ios c memory-management gpgpu metal


    【解决方案1】:

    分配页面对齐内存的最简单方法是使用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,而不使用 no-copy 变体。如果您需要对数据进行实时处理,您应该创建一个缓冲区池并在它们之间循环,而不是等待每个命令缓冲区完成。 Shared 存储模式适用于这些用例。与malloc 相关的警告仅适用于无复制情况,因为在其他所有情况下,Metal 都会为您分配内存。

    【讨论】:

    • 嗨@warrenm 一个关于通过缓冲区循环的快速问题。我确实想要实时处理数据,但是我在AVCaptureVideoDataOutput 上设置了setAlwaysDiscardsLateVideoFrames:YES,因此在完成旧帧的所有计算(包括金属部分)之前,永远不会处理新帧。在这种情况下,是否不需要创建一个缓冲区池,因为我总是可以使用相同的缓冲区?
    • 您不想在 Metal 处理中阻塞捕获输出的队列,因此您将把工作交给 Metal 命令队列以异步执行。根据处理一帧所需的时间,您可能同时有多个帧在运行。如果是这种情况,您仍然应该使用缓冲区池来避免读写冲突或不必要的阻塞。如果您发现您总是(或经常)在下一帧到达之前完成处理,您可以减少池大小甚至完全消除它。
    • 我不明白。我认为使用上述方法丢弃迟到的视频帧正是这样做的,即强制始终必须在处理下一帧之前完成处理,并丢弃早到的。
    • 在 CPU 上完成的处理确实如此,但没有理由在您分配给 GPU 的工作完成时阻塞 CPU。
    猜你喜欢
    • 1970-01-01
    • 2014-07-17
    • 2019-05-02
    • 2018-10-17
    • 2015-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-07
    相关资源
    最近更新 更多