Я обрабатываю несколько больших заархивированных файлов и обнаружил кое-что интересное, чего я не понимаю: использование cat+pv+zcat быстрее, чем cat+zcat, которое быстрее, чем использование только zcat.
Для этого я использую заархивированный JSON-файл размером 1 ГБ в качестве теста.
time cat test.json.gz > /dev/null
time zcat test.json.gz > /dev/null
time cat test.json.gz | zcat > /dev/null
time cat test.json.gz | pv | zcat > /dev/null
real 0m8.245s
real 0m33.075s
real 0m30.504s
real 0m26.682s
Аналогично при записи в файл:
time cat test.json.gz > t0.json.gz
time zcat test.json.gz > t1.json
time cat test.json.gz | zcat > t2.json
time cat test.json.gz | pv | zcat > t3.json
real 0m21.053s
real 0m59.011s
real 0m57.110s
real 0m54.439s
Я также попробовал запустить тесты в обратном порядке, чтобы увидеть, есть ли какое-то кэширование, ускоряющее последующие запуски, но получил те же результаты. Я проверил, выходные файлы идентичны.
Обычно я считаю, что несколько шагов в конвейере увеличивают время, необходимое для обработки файла, так почему же добавление pv может ускорить процесс? Имеет ли он какое-то встроенное распараллеливание? Что здесь происходит?
Если это ожидаемое поведение, я только что наткнулся на очень простой способ увеличить скорость обработки на 10%, но мне бы хотелось понять, что происходит.
Я не могу это воспроизвести. zcat file
для меня быстрее, чем cat file | zcat
.
real
может быть неправильным значением для измерения. подумайте time sleep 10
По крайней мере, для моей системы настоящее — это точно: time sleep 10
— это real 0m10.001s
.
Обычно я считаю, что несколько шагов в конвейере увеличивают время, необходимое для обработки файла, так почему же добавление pv может ускорить процесс? Имеет ли он какое-то встроенное распараллеливание? Что здесь происходит?
На многоядерном процессоре вполне вероятно, что хотя бы иногда вы получаете настоящий параллелизм нескольких процессов. Даже если вы не получаете настоящего параллелизма, многопроцессорность действительно является формой параллельной обработки, так что да, некоторое распараллеливание происходит.
Скорее всего, сам zcat
работает в однопоточном режиме, чередуя чтение входных данных, распаковку и запись выходных данных. Любой ввод-вывод имеет тенденцию быть медленным, но вполне вероятно, что файловый ввод-вывод будет медленнее, чем ввод-вывод через канал. Таким образом, производительность самого zcat
можно улучшить, передав ему входные данные через канал, а не полагаясь на то, что он будет читать из файла.
Конечно, какому-то процессу все равно нужно прочитать этот файл, но вполне возможно, что распаковка zcat
+ выходной ввод-вывод обходится дороже, чем (скажем) файловый ввод-вывод + конвейерный ввод-вывод cat
. В таких обстоятельствах многопроцессорная обработка с помощью cat | zcat
могла бы стать победой.
Еще более загадочно, почему вставка pv
в конвейер между cat
и zcat
может улучшить производительность, но я предполагаю, что pv
буферизует больше данных за раз, чем cat
, так что с pv
в середине zcat
может прочитать данные в целом за меньшее количество операций чтения, каждое из которых больше. Это тоже потенциальный выигрыш в производительности. Вполне вероятно, что канальный ввод-вывод + анализ + канальный ввод-вывод pv
работает как минимум так же быстро, как файловый ввод-вывод + конвейерный ввод-вывод cat
, поэтому pv
вполне может не оказывать какого-либо отрицательного воздействия на стену. время выполнения всего конвейера.
Если это ожидаемое поведение, я только что наткнулся на очень простой способ увеличить скорость обработки на 10%.
Это объяснимое поведение, но не априорно ожидаемое поведение. Наблюдаемое вами ускорение, вероятно, будет зависеть от системы, данных, местоположения данных и других факторов. Вероятно, вы можете рассчитывать на дешевизну вставки pv
в конвейер в большинстве случаев, но вы не можете разумно полагаться на это для общего улучшения производительности.
Я предполагаю, что pv
не имеет отношения к этому и что cat .. | cat | zcat
будет иметь аналогичное преимущество, просто разрешив опережающее чтение другого буфера канала.
Это был бы интересный эксперимент, @thatotherguy. Вы можете быть правы.
@thatotherguy Думаю, ты прав. cat .. | cat | zcat
по сути имел ту же скорость, что и cat .. | pv | zcat
. Ради любопытства я тоже протестировал его на крайнем случае cat .. | cat | cat | cat | zcat
и он оказался примерно таким же, как cat .. | cat | zcat
, так что благо хватило только на две трубки. Я поигрался с несколькими другими вариантами и увидел улучшение только тогда, когда в конечном итоге использовал zcat
, так что, вероятно, это что-то уникальное для буферизации. Об этом стоит помнить, поскольку может оказаться полезным повысить производительность для больших заархивированных файлов.
«Обычно я рассматриваю несколько шагов в канале как увеличение времени, необходимого для обработки файла» — не обязательно. Вы потенциально получите достаточную выгоду от распараллеливания операций, чтобы преодолеть накладные расходы нескольких процессов. Дополнительный ввод-вывод не обязательно является проблемой, поскольку он выполняется параллельно. Кроме того, обычно можно ожидать, что ввод-вывод через канал будет намного дешевле, чем файловый ввод-вывод.