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

Dar*_*ust 9 io macos grand-central-dispatch ios

使用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.
                        }
                    }
                );
            }
        }
    }
);
Run Code Online (Sandbox Code Playgroud)

das*_*das 8

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

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

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

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

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

您可以检查出的GCD IO缓冲政策的具体的实施.

在任何情况下,请确保在您看到默认GCD IO缓冲的性能问题的任何情况下提交错误.

  • 然而,事实证明"优化"版本的运行速度不比我的"天真"实现快,这表明内核已经足够智能,并且在比较NSStream与GCD解决方案时,真正的问题可能是错误的测量.哎哟.但是,非常感谢帮助我. (2认同)