如何优化Lz77滑动窗口压缩机?

时间:2018-10-01 03:25:39

标签: java performance compression sliding-window lz77

我写了一个Java压缩器,用于超级模糊的压缩格式。 (它主要是在1990年代用于Amiga计算机上的。)

关于如何解压缩文件格式的文档很多,但实际上没有关于如何压缩文件的文档。

因此,我尝试自己制作。它有效,但是有一个问题。在“低强度设置”下,我需要42秒钟来压缩我要压缩的所有文件。在较高的强度设置下,该时间大约是原来的10倍。

我相信它可以比这快得多。

基本上是Lz77滑动窗口的变体。

真正的瓶颈是寻找要压缩的现有事件。 现在,我正在使用Map<Byte, List<Integer>>List<Integer>是该字节所在的所有索引。)

要找到潜在的匹配项,它的作用是:

获取正在压缩的文件的当前索引。 它从Map中获取List<Integer>,并且该字节位于当前索引。

它使用该列表查找文件中已出现的字节的最长子列表,并仅检查它们匹配的时间。

我认为一个更好的数据结构可以大大加快这一步,但是我仍然停留在这一点上。

我必须使用的限制之一是由于该程序的用途,因此我必须严格遵守这种古老的压缩格式。

如何在不降低压缩效率的情况下优化压缩?

主要瓶颈代码:

private static int search(byte[] data, int bufferEnd, List<Byte> target, Map<Byte, List<Integer>> dictionary) {
    int minIndex = Math.max(0, bufferEnd - getMaximumOffset(target.size())); // There's a certain point at which data will not be compressed. By calculating it here, it saves a lot of overheard, and prevents this from becoming O(n^2)

    byte test = target.get(0);
    if (!dictionary.containsKey(test))
        return -1; // No results found.

    List<Integer> possibleResults = dictionary.get(test);

    for (int i = possibleResults.size() - 1; i >= 0; i--) {
        int testIndex = possibleResults.get(i);
        if (minIndex > testIndex)
            break; // We've gone too far.

        // Test this
        boolean pass = true;
        for (int j = 1; j < target.size(); j++) {
            if (target.get(j) != data[j + testIndex]) {
                pass = false;
                break; // Break from the j for loop.
            }
        }

        if (pass) // A match has been found. Return it.
            return testIndex;
    }

    return -1;
}

谁叫:

while ((tempIndex = search(data, i, searchList, dictionary)) >= 0) { // Find the longest compressable bunch of characters.
    if (data.length - 1 == readIndex) // If we've reached the end of the data, exit.
        break;

    searchList.add(data[++readIndex]);
}

完整代码here供任何需要的人使用。

1 个答案:

答案 0 :(得分:0)

您缺少很多优化,尤其是低级优化。

  

Map<Byte, List<Integer>>

那是非常低效的。

实际上,Map相当快,但比数组慢得多。代替执行自动装箱和地图查找(一些索引计算和一些数组访问)的map.get(someByte),您可以使用array[someByte & 0xFF]进行单个数组访问,从而获得一个数量级的加速。

类似地,List<Integer>意味着从int开始时会自动装箱。自动装箱通常是可以接受的,但是当它处于苛刻算法的核心时,则不可接受。您可以为此编写自己的类,例如List<int>或google(有一些不错的库)。


if (!dictionary.containsKey(test))
    return -1; // No results found.

List<Integer> possibleResults = dictionary.get(test);

这是不必要的双重查询。除非您使用null值,否则它可以写为

List<Integer> possibleResults = dictionary.get(test);

if (possibleResults == null)
    return -1; // No results found.

这是以前的两倍,但是正如我所写的,您应该在此处使用一个数组。


关于高级优化,我真的不知道如何有效压缩,但是我敢肯定,有很多技巧。如果没有压缩资源,我将从滚动哈希开始。但首先请先阅读有关压缩的内容。