C++/C Несколько потоков для чтения файла gz одновременно

Я пытаюсь прочитать сжатый gzip файл из нескольких потоков.

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

Упрощенный код похож на

// in threads
auto gf = gzopen("file.gz",xxx);
gzseek(gf,offset);
gzread(xx);
gzclose(gf);

К моему удивлению, моя многопоточная версия программы вообще не ускоряется. Версия с 20 потоками использует то же самое время, что и версия с одним потоком. Я почти уверен, что это далеко от узкого места на диске.

Я предполагаю, что функциональность zlib infulation может потребовать распаковки всего файла для чтения даже небольшой части, но я не смог получить никакой подсказки из их руководства.

Кто-нибудь есть идеи, как ускорить в моем случае?

3 ответа

Решение

tl;dr: zlib не предназначен для произвольного доступа. Это представляется возможным реализовать, хотя для создания индекса требуется полное прочтение, поэтому в вашем случае это может оказаться бесполезным.

Давайте посмотрим на источник zlib. gzseek - это оболочка для gzseek64, которая содержит:

/* if within raw area while reading, just go there */
if (state->mode == GZ_READ && state->how == COPY &&
        state->x.pos + offset >= 0) {

"Внутри необработанной области" звучит не совсем правильно, если мы обрабатываем сжатый файл. Давайте посмотрим на значение state->how в gzguts.h:

int how; /* 0: get header, 1: copy, 2: decompress */

Правильно. В конце gz_open вызов gz_reset наборы how на 0. Возвращаясь к gzseek64 в итоге мы получим эту модификацию состояния:

state->seek = 1;
state->skip = offset;

gzread, когда вызывается, обрабатывает это с помощью вызова gz_skip:

if (state->seek) {
    state->seek = 0;
    if (gz_skip(state, state->skip) == -1)
        return -1;
}

Следуя этой кроличьей норе чуть дальше, мы обнаруживаем, что gz_skip звонки gz_fetch до тех пор gz_fetch обработал достаточно ввода для желаемого поиска. gz_fetch в своей первой итерации цикла вызывает gz_look который устанавливает state->how = GZIP, что приводит к gz_fetch распаковать данные из ввода. Другими словами, ваше подозрение верно: zlib распаковывает весь файл до того момента, когда вы используете gzseek,

Краткий ответ: из-за серийного характера потока дефлята, gzseek() должен декодировать все сжатые данные от начала до запрошенной точки поиска. Таким образом, вы не можете получить никакой выгоды от того, что вы пытаетесь сделать. Фактически, общее количество потраченных циклов будет увеличиваться с квадратом длины сжатых данных! Так что не делай этого.

В реализации zlib нет многопоточности ( http://www.zlib.net/zlib_faq.html - "Является ли zlib поточно-ориентированным? - Да.... Конечно, вы должны работать только с любым потоком zlib или gzip из один поток за раз.") и распакует" весь файл "до искомой позиции.

А формат zlib имеет плохое выравнивание (битовое выравнивание) / нет полей смещения ( формат дефляции), что позволяет выполнять параллельную декомпрессию / поиск.

Вы можете попробовать другие реализации z (deflate/inflate), например, http://zlib.net/pigz/ (или перейти от древнего сжатия из эпохи одноядерных к современным параллельным форматам без zlib, xz/lzma/ что-то из гугла)

pigz, который означает параллельную реализацию gzip, является полностью функциональной заменой gzip, которая использует несколько процессоров и несколько ядер для сжатия при сжатии данных. pigz был написан Марком Адлером и использует библиотеки zlib и pthread. Чтобы скомпилировать и использовать pigz, пожалуйста, прочитайте файл README в дистрибутиве исходного кода. Вы можете прочитать страницу руководства pigz здесь.

Страница руководства - http://zlib.net/pigz/pigz.pdf и она содержит полезную информацию.

Он использует формат, совместимый с zlib, но адаптированный для параллельного сжатия:

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

Тем не менее, формат DEFLATE плох для параллельной распаковки:

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

Другие вопросы по тегам