优化字节对编码

时间:2010-01-19 11:56:59

标签: algorithm optimization compression

注意到byte-pair encoding (BPE)在大文本压缩基准测试中非常缺乏,我非常 quickly made 一个简单的文字实现。

压缩比 - 考虑到没有进一步处理,例如没有霍夫曼或算术编码 - 令人惊讶的好。

然而,我的琐碎实现的运行时间不是很好。

如何优化?是否可以一次性完成?

9 个答案:

答案 0 :(得分:5)

这是我目前的进展摘要:

谷歌搜索found this一些链接到原始代码并报告来源的小报告:

  菲利普盖奇,题为'一种新算法'   出现了“数据压缩”   在'C用户期刊' - 二月   1994年版。

Dr Dobbs网站上的代码链接已损坏,但该网页反映了它们。

该代码使用 hash 表来跟踪使用的有向图及其每次通过缓冲区的计数,以避免重新计算新的每一遍。

我的测试数据是来自enwik8Hutter Prize

|----------------|-----------------|
| Implementation | Time (min.secs) |
|----------------|-----------------|
| bpev2          | 1.24            | //The current version in the large text benchmark
| bpe_c          | 1.07            | //The original version by Gage, using a hashtable
| bpev3          | 0.25            | //Uses a list, custom sort, less memcpy
|----------------|-----------------|

bpev3 会创建所有有向图的列表;这些块的大小为10KB,并且通常有200个左右的有向图(4个,这是我们通过压缩可以获得一个字节的最小值);此列表已排序,并进行第一次替换。

随着替换,更新统计数据;通常每次通过只有大约10或20个有向图变化;这些都是'绘制'并排序,然后与有向图列表合并;这比仅总是对每个传递的整个有向图列表进行排序要快得多,因为列表接近排序。

原始代码在'tmp'和'buf'字节缓冲区之间移动; bpev3只是交换缓冲区指针,单独运行时间值约为10秒。

鉴于对bpev2的缓冲区交换修复将使穷举搜索符合哈希表版本;我认为哈希表是可论证的值,并且列表是解决此问题的更好结构。

它的窗台多通过。因此它不是一般的竞争算法。

如果查看大文本压缩基准,则会添加原始bpe。由于它的块大,它在enwik9上比我的bpe更好。此外,哈希表和我的列表之间的性能差距更接近 - 我把它降低到LTCB使用的march=PentiumPro

当然有适合和使用的场合; Symbian用它来压缩ROM映像中的页面。我推测Thumb二进制文件的16位特性使其成为一种简单而有益的方法;压缩在PC上完成,解压缩在设备上完成。

答案 1 :(得分:3)

我已经做了工作,优化LZF压缩实现,以及一些相同的原则,我用来提高性能是可用在这里。

加快字节对编码的性能:

  1. 限制块大小小于65KB(大概8-16 KB将是最优的)。这可以保证不是所有的字节将被使用,并允许在RAM保存中间处理信息。< / LI>
  2. 按短整型使用哈希表或简单的查找表(更多的RAM,但速度更快)来保存计数为一个字节对有65656 2字节对,和BlockSize实例可能(最大块大小64k)。这为您提供了128k可能输出的表格。
  3. 分配和复用的数据结构能够保持全压缩块,替换表,字节对计数和输出字节在存储器中。这听起来很浪费RAM,但是当你考虑到你的块大小很小时,它是值得的。您的数据应该完全位于CPU L2或(最坏情况)L3缓存中。这样可以大幅提升速度。
  4. 快速通过数据来收集计数,然后担心创建替换表。
  5. 包字节为整数或整数短尽可能(应用主要是C / C ++)。计数表中的单个条目可以用整数表示(16位计数,加上字节对)。

答案 2 :(得分:2)

JustBasic中的代码可以在这里找到输入文本文件。

Just BASIC Files Archive – forum post

EBC by TomC 02/2014 - 增强字节对编码

EBPE为字节对编码提供了两个后期处理


1。正在压缩字典(被认为是新奇的)

字典条目由3个字节组成:

AA – the two char to be replaced by (byte pair)
 1 – this single token (tokens are unused symbols)

所以"AA1"告诉我们每当我们看到"1"时解码时 数据文件,将其替换为"AA"

虽然可以进行长时间连续令牌,但让我们来看看 8标记示例:

AA1BB3CC4DD5EE6FF7GG8HH9

长度为24字节(8 * 3)

令牌2不在文件中,表明它不是开放令牌 使用或用其他方式说出来:2是在原始数据中。

我们可以看到最后7个令牌3,4,5,6,7,8,9是连续的,所以任何时候我们 看到4个令牌或更多的连续运行,让我们修改我们的字典:

AA1BB3<255>CCDDEEFFGGHH<255>

<255>告诉我们字节对的标记是隐含的 比我们看到的1的最后一个标记增加3。我们增加 一直到我们看到下一个<255>表示运行结束。

  • 原始字典是24字节,
  • 增强字典为20个字节。

我在令牌的文本文件中使用此增强功能保存了175个字节 128至254将按顺序以及其他一般顺序包括 由小写预处理创建的运行。

2。正在压缩数据文件

重新使用很少使用的字符作为代币并不是什么新鲜事。

使用所有符号进行压缩后(<255>除外), 我们扫描文件并在文件中找到一个"j"。让这个字符加倍 责任:

  • "<255>j"表示这是一个文字"j"
  • "j"现在用作重新压缩的令牌,

如果j在数据文件中出现1次,我们需要添加1 <255> 和一个3字节的字典条目,所以我们需要在BPE中保存超过4个字节 为此值得。

如果j发生了6次,我们需要6 <255>和3字节字典 因此我们需要在BPE中保存超过9个字节,这是值得的。

取决于是否可以进一步压缩以及保留多少字节对 在文件中,这个后期处理在测试运行中节省了超过100个字节。

注意:解压缩时请确保不要对每个"j"进行解压缩。 需要查看前一个字符,以确保它不是<255> 解压缩。最后,在完成所有解压缩后,继续删除<255> 重新创建原始文件。

3。 EBPE的下一步是什么?

此时未知

答案 3 :(得分:1)

我不相信这可以在一次通过中完成,除非你找到一种方法来预测,给定一个字节对替换,如果新的字节对(替换后)也适合替换,或者不

这是我一见钟情的想法。也许你已经或者已经考虑过这一切。

我会尝试以下方法。

两个可调参数:

  1. 在考虑替换之前,数据块中出现的字节对数。 (因此字典的增长速度不会超过块缩小的速度。)
  2. 在它可能不值得更换之前通过的替换次数。 (因此当算法只剩下1%或2%时,算法会停止浪费时间。)
  3. 我会做通过,只要它仍然值得压缩一个级别(根据参数2)。在每次传递期间,我会保留一个字节对的数量。

    我会稍微使用这两个参数,看看它是如何影响压缩比和速度的。可能它们应该根据要压缩的块的长度动态地改变(可能还有一两个其他的东西)。

    要考虑的另一件事是用于存储传递期间每个字节对的计数的数据结构。很可能有一种方法可以编写一个比通用数据结构更快的自定义方法。

    如果你尝试了一些并获得有趣的结果,请告诉我们!

答案 4 :(得分:1)

是的,请告诉我们。

保证?

BobMcGee提供了很好的建议。 但是,我怀疑“将块大小限制为小于65kB ......这保证不会使用所有字节”并非总是如此。 我可以生成一个长度小于1kB的(高度人为的)二进制文件,它有一个重复10次的字节对,但是根本不能用BPE压缩,因为它使用全部256个字节 - 没有BPE可以使用的空闲字节表示频繁的字节对。

如果我们将自己限制为7位ASCII文本,我们有超过127个可用字节可用,因此所有重复一个字节对足够时间的文件都可以通过BPE压缩至少一点。 但是,即使这样,我也可以(人工)生成一个仅使用isgraph()ASCII字符的文件,并且长度小于30kB,最终达到BPE的“无空闲字节”限制,即使仍有一个字节对仍然存在超过4次重复。

单程

似乎就像这个算法可以稍微调整一下就可以做到这一点。 假设7位ASCII明文: 扫描输入文本,记住我们在某种内部数据结构中看到的所有字节对,以某种方式计算到目前为止我们看到的唯一字节对的数量,并将每个字节复制到输出(高位零)。 每当我们遇到重复时,发出一个表示字节对的特殊字节(高位1,因此我们不会将字节对与字节对混淆)。 包含在特殊字节的字节“对”的内部列表中,以便压缩器可以稍后发出一些表示此特殊字节加上文字字节的其他特殊字节 - 因此其他特殊字节的净效果是表示三元组。 正如phkahler指出的那样,听起来和LZW几乎一样。

修改 显然,我上面提到的“无空闲字节”限制毕竟不是所有字节对压缩器的固有限制,因为至少有一个字节对压缩器没有这个限制。

你看到了吗? "SCZ - Simple Compression Utilities and Library"? SCZ似乎是一种字节对编码器。 SCZ显然比我见过的其他字节对compressors提供更好的压缩,因为 SCZ没有我上面提到的“无空闲字节”限制。

如果任何字节对BP在明文中重复了足够的次数(或者,在几轮迭代之后,部分压缩的文本), SCZ可以进行字节对压缩,即使文本已包含全部256个字节。

(SCZ在压缩文本中使用特殊的转义字节E,表示后面的字节用于表示字面,而不是扩展为字节对。 这允许压缩文本中的某些字节M执行双重任务: 压缩文本中的两个字节EM表示纯文本中的M. 压缩文本中的字节M(没有前面的转义字节)表示纯文本中的一些字节对BP。 如果某个字节对BP出现的次数比明文中的M多出许多倍,那么通过将每个BP字节对表示为压缩数据中的单个字节M而节省的空间大于通过将每个M表示为两个字节而“丢失”的空间EM。)

答案 5 :(得分:1)

您还可以优化字典,以便:

AA1BB2CC3DD4EE5FF6GG7HH8是8令牌的连续运行。

将其重写为:

AA1<255>BBCCDDEEFFGGHH<255>其中<255>告诉程序以下每个字节对(直到下一个<255>)是顺序的并且递增1。适用于文本 文件和任何至少有4个连续令牌的地方。

在最近的测试中保存175个字节。

答案 6 :(得分:1)

这是一个新的BPE(http://encode.ru/threads/1874-Alba)。 编译示例, gcc -O1 alba.c -o alba.exe 它比默认更快。

答案 7 :(得分:1)

我描述了here的字节对编码的O(n)版本。我在Java中获得了约200kB /秒的压缩速度。

答案 8 :(得分:0)

最简单有效的结构是二维数组,如byte_pair(255,255)。删除那里的计数并在文件压缩时进行修改。