Самый эффективный способ скопировать файл с GCD? - PullRequest
9 голосов
/ 09 марта 2012

С Grand Central Dispatch вы можете планировать чтение и запись, не беспокоясь о том, когда и как это произойдет. По сравнению с моим предыдущим подходом, основанным на NSStream, это требует меньше управления извне. Однако моя наивная реализация медленнее, чем мой подход, основанный на NSStream.

Для NSStream я запросил предпочтительный размер ввода-вывода как источника, так и пункта назначения (NSURLPreferredIOBlockSizeKey). Затем я полностью прочитал «предпочтительные чанки входного размера» в буфер, и как только в буфере было по крайней мере «предпочтительный выходной размер», я записал целые чанки в место назначения (за исключением, конечно, последнего чанка). Это должно быть довольно близко к оптимальному в отношении производительности чтения и записи.

Однако, с GCD, я не сильно влияю на это. Представьте, что источник имеет предпочтительный размер ввода-вывода 100 кБ и что целевой размер предпочтительного размера ввода-вывода для цели составляет 1 МБ: моя наивная реализация теперь будет писать в 10 раз чаще, чем в моем решении на основе NSStream.

Итак, как наиболее эффективно решить эту проблему с помощью 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 Ответ

8 голосов
/ 12 марта 2012

Хотя в большинстве ситуаций это не требуется, вы можете влиять на размер ввода-вывода, используемый GCD, с помощью API dispatch_io_set_high_water(3) и dispatch_io_set_low_water(3).

GCD не будет читать или записывать фрагменты, размер которых превышает верхнюю отметку канала.Обработчики чтения / записи также никогда не будут вызываться с объектом данных, который меньше отметки низкого уровня воды.

Например, установив для отметки низкого уровня воды input_ в вашем примере значение 1 МБ, вы можете убедиться, что текущийобратный вызов чтения не передает объекты данных размером менее 1 МБ в dispatch_io_write(3).

Если в вашей ситуации этого элемента управления недостаточно, вы также можете объединить несколько объектов данных, полученных из последовательных вызовов вашего обработчика чтения через dispatch_data_create_concat(3) пока они не достигнут размера, достаточного для перехода к dispatch_io_write(3).

Надеемся, что в этом не должно быть необходимости, однако, установив низкую отметку на стороне источника на кратное предпочтительного размера фрагмента источника, достаточно большого, чтобы достичьпредпочтительный размер чанка назначения и установка максимальной отметки целевого канала на предпочтительный размер чанка назначения (или их кратное число) должны обеспечить ту же производительность, что и текущее решение на основе NSStream.

Вы можете ознакомиться со спецификойполитики буфера GCD IO в реализация .

В любом случае, пожалуйста, обязательно подайте ошибку со спецификой любого случая, когда вы видите проблемы с производительностью при буферизации GCD IO по умолчанию.

...