代码之家  ›  专栏  ›  技术社区  ›  David Hay

在deflate算法中,确定块大小的一些好策略是什么?

  •  9
  • David Hay  · 技术社区  · 16 年前

    我正在编写一个压缩库作为一个小的附加项目,并且我已经做了足够的工作(我的库可以提取任何标准的gzip文件,并生成兼容的(但肯定还不是最佳的)gzip输出),现在是时候制定一个有意义的块终止策略了。目前,我只是在每32K输入(LZ77窗口大小)后切断这些块,因为它是针对性的,而且实现起来很快——现在我要回到过去,并尝试实际提高压缩效率。

    这个 Deflate spec 只有这样说:“当压缩机确定用新树启动新块是有用的,或者当块大小填满压缩机的块缓冲区时,它终止块”,这并不是所有的帮助。

    我对sharpziplib代码进行了排序(我认为这是最可读的开放源码实现),发现它每16K字输出就终止一个块,忽略了输入。这很容易实现,但似乎必须有一些更具针对性的方法,特别是考虑到规范中的语言“确定使用新树启动新块是有用的”。

    那么,是否有人对新战略或现有战略有任何想法?

    事先谢谢!

    2 回复  |  直到 16 年前
        1
  •  2
  •   ShuggyCoUk    16 年前

    建议你去。

    具有足够大小的缓冲区的推测性展望,表明优越的压缩是值得更改的。

    这会改变流行为(在输出发生之前需要输入更多的数据),并显著地使刷新等操作复杂化。这也是压缩桩中相当大的额外荷载。

    在一般情况下,可以通过在可能启动新块的每个点分支,使两个分支在必要时递归,直到所有路由都被占用,从而确保生成最佳输出。有窝行为的道路是成功的。这不太可能是可行的非琐碎的输入大小,因为选择何时开始一个新的块是如此开放。

    简单地将其限制为至少8K个输出字面值,但阻止块中超过32K个字面值将导致尝试推测性算法的相对容易处理的基础。称8K为子块。

    其中最简单的是(伪代码):

    create empty sub block called definite
    create empty sub block called specChange
    create empty sub block called specKeep
    target = definite
    While (incomingData)
    {
      compress data into target(s)    
      if (definite.length % SUB_BLOCK_SIZ) == 0)
      {
        if (targets is definite)
        {
          targets becomes 
            specChange assuming new block 
            specKeep assuming same block as definite
        }        
        else
        {
          if (compression specChange - OVERHEAD better than specKeep)
          {
            flush definite as a block.
            definite = specChange
            specKeep,specChange = empty
            // target remains specKeep,specChange as before 
            but update the meta data associated with specChange to be fresh
          }
          else 
          {
            definite += specKeep
            specKeep,specChange = empty
            // again update the block meta data
            if (definite is MAX_BLOCK_SIZE)
            {
              flush definite
              target becomes definite 
            }
          }
        }
      }
    }
    take best of specChange/specKeep if non empty and append to definite
    flush definite.
    

    开销是一个常数,用来说明切换块的成本。

    这很粗糙,可能会得到改善,但如果没有其他的话,这是分析的开始。在代码中插入导致切换的信息,使用该代码确定好的启发式方法,即更改可能有益(可能压缩比已显著降低)。

    这可能导致只有在启发式方法认为规范变更是合理的情况下才进行规范变更的构建。如果启发式的结果是一个强有力的指标,那么你可以去掉投机性质,然后简单地决定在点上交换,无论什么。

        2
  •  0
  •   David Hay    16 年前

    嗯,我喜欢一些启发式分析的想法,试图想出一些“规则”,当结束块可能是有益的。我今晚会研究一下你建议的方法,看看我能用它做些什么。

    同时,我想到,为了在这个问题上做出一个完全知情的选择,我需要更好地了解块大小决策的优缺点。很快我就明白了,更小的块可以让你有一个潜在的更好的目标符号字母表——代价是更多地定义树会增加开销。较大的块与它们更通用的符号字母表相对应,具有一定的比例效率(对于大量编码数据,只需存储和解码一棵树)。

    从我的头顶上看,不清楚小代码与长度、距离代码的相对分布是否会对最佳块大小产生特定的影响。不过,这是值得深思的好东西。