繁体   English   中英

Java源代码生成中的霍夫曼代码解码器编码器

[英]Huffman Code Decoder Encoder In Java Source Generation

我想用Java创建一个快速的霍夫曼代码解码器,因此考虑了查找表。 由于这些表占用内存,并且我们使用Java代码导航和访问表,因此可以轻松(或不容易)编写表示同一表的程序/方法。

这种方法的问题是,我不知道什么是最佳策略。 我知道很多有关缓存和分支预测的内容。 同样,切换案例的实现意味着实际的ASM在我之外。 如果我有一个内存中查找表(或它的层次结构),则可以简单地跳入和跳出,但出于我的目的,我建议将该表放入高速缓存中。

由于我实际上是走一棵树,因此可以像执行其他语句一样需要一定数量的比较来实现它,但是对于每个比较,它都需要附加的二进制运算。

因此存在以下选项:

  • 内存查找表中使用的通用算法
  • 决策树的if / else表示
  • if / else表示法,使用小的switch语句查找正确的符号组(相同的位模式长度)(if语句较少,可能是更多代码)。
  • 代码的switch语句表示

编写和基准测试非常棘手,因此任何初步想法都会很棒。

起作用的另一个问题是位的顺序。 最重要的位始终位于最前面,这意味着它以相反的顺序存储。

如果您的树是A = 0,B = 10,C = 11以编写BAC,则实际上为01 + 0 + 11(加上表示追加)。

因此,实际上必须以相反的顺序编写代码。 对组使用if / else或switch方法将不会有问题,因为屏蔽位很简单,并且位的反转也很可能,但是由于相反,它将失去将组内的索引移出掩码的想法位顺序的添加和删除具有不同的含义,并且不可能进行简单的查找。

反转位是一项昂贵的操作(我使用4位查找表),不会超过二进制操作的性能损失。

但是,在旅途中反转位更适合此操作,并且每位需要进行四个操作(上移,屏蔽,加法以及下移输入)。 由于我提前读取了所有这些操作,因此所有这些操作都将在寄存器中完成,因此它们可能只需要几个周期。

这样,我可以使用switch,sub和if来找到正确的符号组并返回它们。

最后,我需要一些建议。 由于我的代码在语言处理方面是全球通用的,因此可以进行硬连线(即在源代码中)。

我想知道像ANTRL这样的解析器生成器是用来表达那些决定的。 由于他们也根据输入符号来缝制切换或是否/其他,这可能会给我一个提示。

[更新]

我发现了一种简化方法,可以避免反向位问题,但仍会增加每组的成本。 因此,我最终按照要遍历的组的顺序编写了位。 因此,我不需要每个位四个修改,而是每个组(不同的位长)。

对于每个组,我们都有:1.第一个元素的值,大小(以及该组中最后一个元素的值)。

因此,对于每个组,算法如下:1.读取mbits并与当前读取值组合。 2.将值与该组的最后一个值进行比较,如果它不在该组中,则该值在该组中较小。 ->接下来阅读3.如果在组内,则可以访问值数组或使用switch语句。

这是完全通用的,可以不使用循环就可以有效地使用。 同样,如果检测到该组,则代码的位长是已知的,并且由于代码看起来很远(从流中读取),因此可以从源中消耗这些位。

[更新2]

要访问实际值,可以使用按组分组的单个大元素数组。 由于按组分组的可传输性降低,因此很可能有相当一部分适合L2或L1缓存,从而加快了访问速度。

或者使用switch语句。

[更新3]

根据开关的情况,编译器会生成表开关或查找开关。 查找开关的复杂度为O(log n),并存储密钥,jmp偏移对,因此不理想。 因此,检查组更适合if / else。

tableswitch本身仅使用跳转偏移量表,并且仅需进行减法,比较,访问和jmp即可到达目的地,而它必须对常量执行返回值。

因此,表访问看起来更有希望。 另外,为了避免不必要的跳转,每个组可能包含访问和返回组符号表的逻辑。 将所有内容存储在一个大表中是有希望的,因为每个符号可能是int或short,而我的代码通常最多最多只有1000到4000个符号,因此实际上它很短。

我将检查1-模式是否将使我有机会以更好的方式存储和访问掩码,从而允许二进制搜索正确的组而不是在O(n)中前进,甚至可能在处理期间完全避免任何移位操作。

我无法理解您在(长)问题中写的大部分内容,但是有一个简单的方法。

我们将从一个表开始。 假设您最长的霍夫曼代码是15位。 (实际上,deflate将其霍夫曼代码的大小限制为15位。)然后构造一个包含32768个条目的表,其中每个条目是下一个代码中的位数,以及该代码的符号。 对于少于15位的代码,表中对于同一代码有多个条目。 例如,如果符号'C'的代码为10010110(7位),则表xxxxxxxx10010110的所有索引都具有相同的含义。 这些条目都带有{7,'C'}。

然后,您从流中获得15位,并查找表中的下一个代码。 您从该表条目中删除位数,并使用结果符号。 现在,您需要从流中获取尽可能多的位,并具有15位,然后重复。 因此,如果您使用了7位,则再增加8位回到15,然后查找下一个代码。

下一个微妙之处是,如果您的霍夫曼代码经常更改,那么您可能最终要花费更多的时间为每个新的霍夫曼代码填充大表,而不是实际解码。 为了避免这种情况,您可以创建一个两级表,该表在代码的第一部分具有9位查找(512个条目)。 如果代码为9位或更少,则按上述步骤进行。 这将是最常见的情况,因为较短的代码会更频繁(这是霍夫曼编码的重点)。 如果表条目表明代码中有10位或更多位(并且您还不知道还有多少位),那么您将消耗前9位,并转到第二级表以查找所指向的前9位按第一个表中的条目排序,该表具有其余六位的条目(64个条目)。 这样可以解决代码的其余部分,从而告诉您要消耗多少位以及什么是符号。 这种方法可以极大地减少花费在填充表上的时间,并且由于短代码更为常见,因此速度几乎一样快。 这是使用的方法充气ZLIB

最后,这非常简单。 我现在支持几乎所有解决方案。 可以测试每个符号组(相同的位长),使用查找表(10bit + 10bit + 10bit(仅10bit的表,symbolscount +1是对这些标签的引用))并生成Java(并且如果需要javascript,但目前我使用GWT进行翻译)。

我什至使用长读取和移位操作来减少对二进制信息的访问。 这样一来,由于我仅支持最大位大小(20位(即表的表)),因此使代码更有效,这使2 ^ 20个符号,因此最多为100万个符号。

对于排序,我仅使用移位操作就可以使用位掩码的生成器,而无需反转位顺序等。

表查找也可以用Java表示,将表存储为数组数组(有趣的是,不用编译器就可以抱怨Java文件有多大)。

我还发现有趣的是,由于比较表示一种顺序(我猜是半顺序),因此可以对符号进行排序,而不是映射映射比较索引的符号。 通过比较两个索引,人们可以简单地对代码流进行排序,而无需花太多精力。 通过还存储前两个或前两个比较索引(16或32位),可以使用相同的霍夫曼代码对压缩的字符串进行有效排序,从而对二进制字符串进行二进制排序,这使得以某种特定语言压缩字符串非常理想。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM