【问题标题】:zlib, deflate: How much memory to allocate?zlib,deflate:要分配多少内存?
【发布时间】:2012-02-12 17:55:20
【问题描述】:

我正在使用zlib 压缩文本数据流。文本数据以块的形式出现,对于每个块,调用deflate(),并将flush 设置为Z_NO_FLUSH。检索到所有块后,将调用 deflate() 并将刷新设置为 Z_FINISH

当然,deflate() 不会在每次调用时产生压缩输出。它在内部累积数据以实现高压缩率。没关系!每次deflate() 生成压缩输出时,该输出都会附加到数据库字段 - 一个缓慢的过程。

但是,一旦deflate() 生成压缩数据,该数据可能不适合提供的输出缓冲区deflate_out。因此需要多次调用deflate()。这就是我想要避免的:

有没有办法让deflate_out 总是足够大,以便deflate() 可以在每次决定产生输出时将所有压缩数据存储在其中?

注意事项:

  • 未压缩数据的总大小事先知道。如上所述,未压缩的数据以块的形式出现,压缩后的数据以块的形式附加到数据库字段。

  • 在包含文件zconf.h 中,我找到了以下评论。这可能是我正在寻找的吗? IE。 (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's 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.
    */
    

【问题讨论】:

  • @nos:这只有在输入的大小已知时才有用。
  • 我读到zconf.h 中的评论是压缩的内存要求,而不是输出缓冲区的大小。也就是说,输出缓冲区的上限是总内存需求(上例中为 128K+128K+“几千字节”)+ 标头长度(40 字节),这似乎是合乎逻辑的。

标签: c performance memory zlib


【解决方案1】:

在寻找提示的来源时,我跌倒了

/* =========================================================================
 * 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;
...

跟踪整个 deflate() 中对 void flush_pending() 的使用表明,流中间所需输出缓冲区的上限是

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

第一部分说明之前调用 deflate() 时仍在管道中的数据,第二部分说明尚未处理的长度为avail_in 的数据。

【讨论】:

  • 您对我现在已删除的答案的评论是正确的。我忘记了内部状态。出于好奇,我在快速测试中第一次调用 deflate 后查看了该挂起值。 avail_in 为零,avail_out 为 2,pending 为零 (0)。它似乎没有反映待处理数据的实际数量。下一次调用 deflate 以刷新它会将 ~8K 转储到输出中。所以这可能不是一个准确的测量......至少在一种情况下。
  • 你说strm-&gt;state-&gt;pending仍在管道中的数据的大小。如果我理解正确,那么这个大小会随着每次调用deflate() 而增加,直到达到未知的上限。而这个上限正是我正在寻找的。那么这有什么帮助呢?我错过了什么吗?
  • 我的意思是,如果你给 deflate() 一个大小为 strm->state->pending + deflateBound(strm, strm->avail_in) 的缓冲区,它永远不会耗尽缓冲区空间。跨度>
  • 我明白了。所以在调用deflate() 之前,需要为strm-&gt;next_out 分配strm-&gt;state-&gt;pending + deflateBound(strm, strm-&gt;avail_in) 字节的内存。感谢您解决这个问题!我仍然不确定我是否应该依赖这种方法。毕竟它没有被记录为 zlib API 的一部分。
  • zlib.h 中的文档强烈建议访问 strm-&gt;state 是一个坏主意:struct internal_state FAR *state; /* not visible by applications */
【解决方案2】:

deflateBound() 仅当您在一个步骤中完成所有压缩,或者强制 deflate 压缩当前可用的所有输入数据并为所有输入发出压缩数据时才有用。您可以使用诸如 Z_BLOCK、Z_PARTIAL_FLUSH 等刷新参数来执行此操作。

如果您想使用 Z_NO_FLUSH,那么尝试预测 deflate() 在下一次调用时可能发出的最大输出量将变得更加困难且效率低下。您不知道在发出最后一次压缩数据突发时消耗了多少输入,因此您需要假设几乎没有,因为缓冲区大小不必要地增长。无论您尝试估计最大输出,您都会无缘无故地执行大量不必要的 malloc 或 realloc,这是低效的。

没有必要避免调用 deflate() 以获得更多输出。如果您只是循环 deflate() 直到它没有更多输出给您,那么您可以使用一次分配的固定输出缓冲区。这就是 deflate() 和 inflate() 接口的设计用途。您可以查看 http://zlib.net/zlib_how.html 以获取有关如何使用该界面的详细记录示例。

顺便说一句,在最新版本的 zlib (1.2.6) 中有一个 deflatePending() 函数可以让你知道 deflate() 有多少输出等待交付。

【讨论】:

  • 非常感谢您的详细解答!为了预测下一次调用deflate() 所需的输出缓冲区,我考虑将deflatePending() 报告的大小和deflateBound() 返回的值相加。这类似于@EugenRieck 的建议。但是,据我了解,这不是一个好主意,因为 deflateBound() 被记录为仅在传递要压缩的整个输入的大小时才起作用。 IE。 deflateBound() 没有记录可用于输入块。
  • deflateBound() 可以用于输入块,但前提是所有先前的输入都已被压缩和发出。这只能通过使用除 Z_NO_FLUSH 之外的刷新选项并消耗先前调用的所有输出来确保。在这种情况下,当使用 Z_BLOCK 或 Z_PARTIAL_FLUSH 时,deflatePending() 将很有用,因为它们可以留下一些位。使用 Z_NO_FLUSH 时,deflateBound() + deflatePending() 会丢失第三部分,即之前的 deflate() 调用所消耗的输入大小,但尚未压缩和发出。
猜你喜欢
  • 2012-08-28
  • 1970-01-01
  • 1970-01-01
  • 2011-03-03
  • 2014-03-02
  • 2020-03-12
  • 2017-11-08
  • 1970-01-01
  • 2011-01-03
相关资源
最近更新 更多