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


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

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

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

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

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

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

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

3 3

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) {

"в пределах необработанной области"звучит не совсем правильно, если мы обрабатываем файл gzipped. Давайте посмотрим на значение state->how в гзгуты.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#faq21 - " является ли zlib потокобезопасным? - Да. ... Конечно, вы должны работать только с любым заданным потоком zlib или gzip из одного потока одновременно.") и распакует "весь файл" до видимой позиции.

И формат zlib имеет плохое выравнивание (bit alignment) / нет полей смещения (deflate format), чтобы включить параллельную декомпрессию/поиск.

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

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

Страница справочника http://zlib.net/pigz/pigz.pdf и у него есть полезная информация.

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

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

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

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