簡體   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