建议压缩库尽可能小地获取byte []而不考虑cpu费用?

时间:2012-04-29 04:22:34

标签: java compression

如果我接近这个错误,请纠正我,但我有一个队列服务器和一群我正在群集中运行的java工作者。我的队列有很小的工作单位,但有很多。到目前为止,我的基准测试和对工人的评估表明,我的速度大约为200mb /秒。

所以我想弄清楚如何通过我的带宽获得更多的工作单位。目前我的CPU使用率不是很高(40-50%),因为它可以比网络发送数据更快地处理数据。我希望通过队列获得更多的工作,我愿意通过昂贵的压缩/解压缩来支付它(因为现在每个核心的一半都是空闲的)。

我已经尝试过java LZO和gzip,但是想知道是否有更好的东西(即使它的cpu更贵)?

更新:数据是一个字节[]。基本上,队列只采用那种格式,因此我使用ByteArrayOutputStream来编写两个整数,将int []编写为byte []格式。 int []中的值都是0到100之间的整数(或1000,但绝大多数数字都是零)。列表非常大,从1000到10,000个项目(再次,多数为零...... int []中只有100多个非零数字

5 个答案:

答案 0 :(得分:6)

这听起来像使用自定义压缩机制,利用数据结构可能非常有效。

首先,使用short[](16位数据类型)而不是int[]会将发送的数据量减半(!),您可以这样做,因为数字很容易在{{1 (-32768)和-2^15(32767)。这非常容易实现。

其次,您可以使用类似于行程编码的方案:正数表示该字母数字,而负数表示许多零(在获取绝对值之后)。 e.g。

2^15-1

这很难实现,只需用[10, 40, 0, 0, 0, 30, 0, 100, 0, 0, 0, 0] <=> [10, 40, -3, 30, -1, 100, -4] 代替short,但在最坏的情况下会提供~80%的压缩(1000个数字,100个非零,其中没有一个是连续的)

我只是做了一些模拟来计算压缩比。我测试了上面描述的方法,以及Louis Wasserman和sbridges建议的方法。两者表现都很好。

假设数组的长度和非零数字的数量均在它们的边界之间均匀,则两种方法平均可以节省大约5400 int s(或int s)的压缩大小大约2.5%的原始!运行长度编码方法似乎可以节省大约1个额外short(或平均压缩大小减小0.03%),即基本上没有差异,因此您应该使用最容易实现的那个。以下是50000个随机样本的压缩比的直方图(它们非常相似!)。

histograms

摘要:使用int代替short和其中一种压缩方法,您可以将数据压缩到原始大小的1%左右!

对于模拟,我使用了以下R脚本:

int

答案 1 :(得分:2)

尝试将数据编码为两个varints,第一个varint是序列中数字的索引,第二个是数字本身。对于0的条目,不写入任何内容。

答案 2 :(得分:2)

我写了一个RLE算法的实现。它在字节数组上运行,因此可以用作现有代码的内联过滤器。如果您的数据将来发生变化,它应该可以安全地处理大值或负值。

它将一系列零编码为{0} {qty},其中{qty}在1..255的范围内。所有其他字节都存储为字节本身。你在发送之前挤压你的字节数组,并在接收时将它膨胀回完整大小。

public static byte[] squish(byte[] bloated) {
    int size = bloated.length;
    ByteBuffer bb = ByteBuffer.allocate(2 * size);
    bb.putInt(size);
    int zeros = 0;
    for (int i = 0; i < size; i++) {
        if (bloated[i] == 0) {
            if (++zeros == 255) {
                bb.putShort((short) zeros);
                zeros = 0;
            }
        } else {
            if (zeros > 0) {
                bb.putShort((short) zeros);
                zeros = 0;
            }
            bb.put(bloated[i]);
        }
    }
    if (zeros > 0) {
        bb.putShort((short) zeros);
        zeros = 0;
    }
    size = bb.position();
    byte[] buf = new byte[size];
    bb.rewind();
    bb.get(buf, 0, size).array();
    return buf;
}

public static byte[] bloat(byte[] squished) {
    ByteBuffer bb = ByteBuffer.wrap(squished);
    byte[] bloated = new byte[bb.getInt()];
    int pos = 0;
    while (bb.position() < bb.capacity()) {
        byte value = bb.get();
        if (value == 0) {
            bb.position(bb.position() - 1);
            pos += bb.getShort();
        } else {
            bloated[pos++] = value;
        }
    }
    return bloated;
}

答案 3 :(得分:1)

与7z和gzip相比,我对BZIP2印象深刻。我没有亲自尝试过这种Java实现,但看起来很容易用你的GZIP调用代替这个并验证结果。

http://www.kohsuke.org/bzip2

答案 4 :(得分:1)

您可能应该尝试数据流中的所有主要数据,并查看哪种方法效果最佳。您还应该考虑某些算法运行时间更长,从而为队列增加更多延迟。根据您的申请,这可能是也可能不是问题。

如果您对数据有所了解,有时可以获得更好的压缩效果。 (dbaupp的答案很好地涵盖了这种方法)

comparison of compression algorithms可能有用。来自文章:

compression ratio of popular compression algorithms