Подтвердить что ты не робот

Zlib, deflate: Сколько памяти выделять?

Я использую zlib для сжатия потока текстовых данных. Текстовые данные поступают в куски, и для каждого фрагмента вызывается deflate(), при установленном флажке на Z_NO_FLUSH. После того, как все куски были извлечены, deflate() вызывается с установленным флажком на Z_FINISH.

Естественно, deflate() не производит сжатый вывод для каждого вызова. Он внутренне накапливает данные для достижения высокой степени сжатия. И это прекрасно! Каждый раз, когда deflate() создает сжатый вывод, этот вывод добавляется в поле базы данных - медленный процесс.

Однако, как только deflate() создает сжатые данные, эти данные могут не помещаться в предоставленный выходной буфер, deflate_out. Поэтому требуется несколько вызовов deflate(). И этого я хочу избежать:

Есть ли способ сделать deflate_out всегда достаточно большим, чтобы deflate() мог хранить все сжатые данные в нем, каждый раз он решает произвести вывод?

Примечания:

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

  • В файле include zconf.h я нашел следующий комментарий. Возможно, это то, что я ищу? То есть (1 << (windowBits+2)) + (1 << (memLevel+9)) максимальный размер в байтах сжатых данных, который deflate() может создавать?

    /* The memory requirements for deflate are (in bytes):
                (1 << (windowBits+2)) +  (1 << (memLevel+9))
     that is: 128K for windowBits=15  +  128K for memLevel = 8  (default values)
     plus a few kilobytes for small objects. For example, if you want to reduce
     the default memory requirements from 256K to 128K, compile with
         make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
     Of course this will generally degrade compression (there no free lunch).
    
       The memory requirements for inflate are (in bytes) 1 << windowBits
     that is, 32K for windowBits=15 (default value) plus a few kilobytes
     for small objects.
    */
    
4b9b3361

Ответ 1

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

Если вы хотите использовать Z_NO_FLUSH, то это становится намного сложнее, а также неэффективно, чтобы попытаться предсказать, как наибольший объем дефлятора() может испускаться при следующем вызове. Вы не знаете, сколько из потребляемого материала было израсходовано во время выпуска последнего пакета сжатых данных, поэтому вам нужно почти не принимать его, при этом размер буфера становится ненужным. Однако вы пытаетесь оценить максимальный результат, вы будете делать много ненужных mallocs или reallocs без уважительной причины, что неэффективно.

Нет смысла избегать вызова функции deflate() для большего объема вывода. Если вы просто зацикливаете на deflate() до тех пор, пока у вас не будет больше вывода для вас, вы можете использовать фиксированный выходной буфер malloced once. Именно так был разработан интерфейс deflate() и inflate(). Вы можете посмотреть http://zlib.net/zlib_how.html для хорошо документированного примера использования интерфейса.

Кстати, в последней версии zlib (1.2.6) есть функция deflatePending(), которая позволяет вам узнать, сколько ожидаемого доставки функции deflate().

Ответ 2

Если посмотреть на источники для подсказки, я упал на

/* =========================================================================
 * Flush as much pending output as possible. All deflate() output goes
 * through this function so some applications may wish to modify it
 * to avoid allocating a large strm->next_out buffer and copying into it.
 * (See also read_buf()).
 */
local void flush_pending(strm)
    z_streamp strm;
{
    unsigned len = strm->state->pending;
...

отслеживание использования void flush_pending() во время deflate() показывает, что верхняя граница требуемого выходного буфера в середине потока

strm->state->pending + deflateBound(strm, strm->avail_in)

первая часть учитывает данные, все еще находящиеся в трубе от предыдущих вызовов, к deflate(), вторая часть учитывает еще не обработанные данные длины avail_in.