使用GCD复制文件的最有效方法?

时间:2012-03-09 11:33:01

标签: ios macos io grand-central-dispatch

使用Grand Central Dispatch,您可以安排读取和写入,而无需担心何时/如何发生。与我之前基于NSStream的方法相比,这需要更少的外部管理。但是,我的天真实现比基于NSStream的方法慢。

对于NSStream,我查询了源和目标(NSURLPreferredIOBlockSizeKey)的首选IO大小。然后我将整个“首选输入大小的块”读入缓冲区,并且只要我在缓冲区中至少有“首选输出大小”字节,我就会将整个块写入目标(当然除了最后一个块)。在读写性能方面,这应该非常接近最佳值。

然而,对于GCD,我对此没有多大影响。想象一下,源的首选IO大小为100kB,目标首选的IO大小为1MB:我的朴素实现现在写入的次数是基于NSStream的解决方案的10倍。

那么,用GCD解决这个问题最有效的方法是什么?只需写入读取器块中的缓冲区,一旦收集到足够的数据,就可以调度“首选输出大小”的写入块?我想GCD可能会在这里为我提供一个我还不知道的解决方案。

这是我目前GCD解决方案中最重要的部分:

// input_ and output_ are of type dispatch_io_t

dispatch_io_read(
    input_,
    0,
    SIZE_MAX,
    dispatch_get_main_queue(),
    ^(bool done, dispatch_data_t data, int error) {
        size_t data_size;

        if (error) {
            NSLog(@"Input: error %d", error);
            [self cancel];
            return;
        }
        if (data) {
            data_size = dispatch_data_get_size(data);
            if (data_size > 0) {
                dispatch_io_write(
                    output_,
                    0,
                    data,
                    dispatch_get_main_queue(), ^(bool done, dispatch_data_t data, int error) {
                        // TODO: I don't know how to get the offset (for progress). So I need to
                        // pass it from the calling block.
                        if (error) {
                            NSLog(@"Output: error %d", error);
                            return;
                        }
                        if (done) {
                            bytesWritten_ += data_size;
                            // Update progress report here.
                        }
                    }
                );
            }
        }
    }
);

1 个答案:

答案 0 :(得分:8)

虽然在大多数情况下不需要,但您可以使用dispatch_io_set_high_water(3)dispatch_io_set_low_water(3) API来影响GCD使用的IO大小。

GCD不会读取或写入大于通道高水位线的块。读取/写入处理程序也永远不会被一个小于低水位线的数据对象调用。

E.g。通过将示例中的input_的低水位标记设置为1MB,可以确保当前的读取回调不会将小于1MB的数据对象传递给dispatch_io_write(3)

如果此控件在您的情况下还不够,您还可以通过dispatch_data_create_concat(3)组合从读取处理程序的连续调用中收到的多个数据对象,直到它们的大小足以传递到dispatch_io_write(3)

希望这不是必需的,但是,将源端的低水位标记设置为首选源块大小的倍数,大小足以达到首选目标块大小,并将目标通道的高水位标记设置为首选目标块大小(或其倍数)应该为您提供与当前基于NSStream的解决方案相同的性能。

您可以在implementation

中查看GCD IO缓冲区策略的细节

在任何情况下,请确保提交bug,其中列出了您发现默认GCD IO缓冲性能问题的任何情况。