使用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.
}
}
);
}
}
}
);
答案 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缓冲性能问题的任何情况。