Zlib Java与C的基准测试比较

7
我正在尝试将最初用Java编写的TIFF编码器切换到C语言以加快速度,并使用定义了Z_SOLOZlib 1.2.8和一组最小的C文件:adler32.ccrc32.cdeflate.ctrees.czutil.c。Java正在使用java.util.zip.Deflater

我编写了一个简单的测试程序来评估压缩级别和速度方面的性能,并惊讶于不管我需要什么级别,压缩效果都不会太明显,而较高级别需要更长时间。我还惊讶地发现,在压缩和速度方面,Java实际上比Visual Studio Release-compile(VC2010)表现更好:

Java:

Level 1 : 8424865 => 6215200 (73,8%) in 247 cycles.
Level 2 : 8424865 => 6178098 (73,3%) in 254 cycles.
Level 3 : 8424865 => 6181716 (73,4%) in 269 cycles.
Level 4 : 8424865 => 6337236 (75,2%) in 334 cycles.
Level 5 : 8424865 => 6331902 (75,2%) in 376 cycles.
Level 6 : 8424865 => 6333914 (75,2%) in 395 cycles.
Level 7 : 8424865 => 6333350 (75,2%) in 400 cycles.
Level 8 : 8424865 => 6331986 (75,2%) in 437 cycles.
Level 9 : 8424865 => 6331598 (75,2%) in 533 cycles.

C:

Level 1 : 8424865 => 6215586 (73.8%) in 298 cycles.
Level 2 : 8424865 => 6195280 (73.5%) in 309 cycles.
Level 3 : 8424865 => 6182748 (73.4%) in 331 cycles.
Level 4 : 8424865 => 6337942 (75.2%) in 406 cycles.
Level 5 : 8424865 => 6339203 (75.2%) in 457 cycles.
Level 6 : 8424865 => 6337100 (75.2%) in 481 cycles.
Level 7 : 8424865 => 6336396 (75.2%) in 492 cycles.
Level 8 : 8424865 => 6334293 (75.2%) in 547 cycles.
Level 9 : 8424865 => 6333084 (75.2%) in 688 cycles.

我是唯一目击这种结果的人吗?我的猜测是JVM中的Zlib使用了汇编类型的优化,而我在我的C项目中没有包含,或者在编译Zlib(或Visual Studio编译器很差)时我错过了一个明显的配置步骤。

以下是两个片段:

Java:

public static void main(String[] args) throws IOException {
    byte[] pix = Files.readAllBytes(Paths.get("MY_MOSTLY_UNCOMPRESSED.TIFF"));
    int szin = pix.length;
    byte[] buf = new byte[szin*101/100];
    int szout;
    long t0, t1;

    for (int i = 1; i <= 9; i++) {
        t0 = System.currentTimeMillis();
        Deflater deflater = new Deflater(i);
        deflater.setInput(pix);
        szout = deflater.deflate(buf);
        deflater.finish();
        t1 = System.currentTimeMillis();
        System.out.println(String.format("Level %d : %d => %d (%.1f%%) in %d cycles.", i, szin, szout, 100.0f*szout/szin, t1 - t0));
    }
}

C:

#include <time.h>
#define SZIN 9000000
#define SZOUT 10000000
void main(void)
{
    static unsigned char buf[SZIN];
    static unsigned char out[SZOUT];
    clock_t t0, t1;
    int i, ret;
    uLongf sz, szin;
    FILE* f = fopen("MY_MOSTLY_UNCOMPRESSED.TIFF", "rb");
    szin = fread(buf, 1, SZIN, f);
    fclose(f);

    for (i = 1; i <= 9; i++) {
        sz = SZOUT;
        t0 = clock();
        compress2(out, &sz, buf, szin, i); // I rewrote compress2, as it's not available when Z_SOLO is defined
        t1 = clock();
        printf("Level %d : %d => %d (%.1f%%) in %ld cycles.\n", i, szin, sz, 100.0f*sz/szin, t1 - t0);
    }
}

编辑:

在 @MarkAdler 的评论后,我尝试了不同的压缩策略,通过 deflateInit2()(即 Z_FILTEREDZ_HUFFMAN_ONLY):

Z_FILTERED

Level 1 : 8424865 => 6215586 (73.8%) in 299 cycles.
Level 2 : 8424865 => 6195280 (73.5%) in 310 cycles.
Level 3 : 8424865 => 6182748 (73.4%) in 330 cycles.
Level 4 : 8424865 => 6623409 (78.6%) in 471 cycles.
Level 5 : 8424865 => 6604616 (78.4%) in 501 cycles.
Level 6 : 8424865 => 6595698 (78.3%) in 528 cycles.
Level 7 : 8424865 => 6594845 (78.3%) in 536 cycles.
Level 8 : 8424865 => 6592863 (78.3%) in 595 cycles.
Level 9 : 8424865 => 6591118 (78.2%) in 741 cycles.

Z_HUFFMAN_ONLY

Level 1 : 8424865 => 6803043 (80.7%) in 111 cycles.
Level 2 : 8424865 => 6803043 (80.7%) in 108 cycles.
Level 3 : 8424865 => 6803043 (80.7%) in 106 cycles.
Level 4 : 8424865 => 6803043 (80.7%) in 106 cycles.
Level 5 : 8424865 => 6803043 (80.7%) in 107 cycles.
Level 6 : 8424865 => 6803043 (80.7%) in 106 cycles.
Level 7 : 8424865 => 6803043 (80.7%) in 107 cycles.
Level 8 : 8424865 => 6803043 (80.7%) in 108 cycles.
Level 9 : 8424865 => 6803043 (80.7%) in 107 cycles.

按照他的评论所述,Z_HUFFMAN_ONLY不会改变压缩效果,但速度会快得多。在我的数据中,Z_FILTERED并没有比Z_DEFAULT_STRATEGY更快,而且压缩效果稍差一些。

我很惊讶等级3是最小的。你确定你的数据没有什么奇怪的地方吗? - Peter Lawrey
@PeterLawrey 这是一个“标准”的TIFF文件,大小为2800x2900,包含2页,第一页未压缩,第二页使用deflate压缩。我可以理解为“压缩已经被压缩的数据会使它们膨胀”。不过,如果我这个周末有时间的话,我可以尝试压缩已经被压缩的数据,看看会发生什么。 - Matthieu
1
在Java程序中,请注意fis.read(pix)可能无法读取整个文件,这种情况下,pix的剩余部分将为零。我建议用pix = Files.readAllBytes(Paths.get("MY_MOSTLY_UNCOMPRESSED.TIFF"))替换FileInputStream的使用。 - VGR
你所得到的压缩量取决于数据。大多数图像文件已经被压缩了相当程度,因此你不太可能看到更多的压缩。 - Hot Licks
1
毫不奇怪。每个“级别”都是一个不同的算法,有些算法在给定数据上表现更好(或者在这种情况下,更可能具有较少的开销)而其他算法则不然。 - Hot Licks
显示剩余4条评论
1个回答

5
压缩量和增量对于基本没有匹配字符串的未压缩图像数据并不令人惊讶。被压缩的数据部分不会进一步压缩,而是会略微扩展一些恒定的量,因此变化都在未压缩的部分。
在3级和4级之间有一种算法变化,其中3级会找到它发现的第一个匹配项。当几乎没有匹配字符串时,这将倾向于最小化发送字符串匹配的开销,从而获得更好的压缩效果。如果完全关闭字符串匹配,可以使用FILTEREDHUFFMAN_ONLY来实现更好的压缩效果。HUFFMAN_ONLY还具有不查找匹配字符串,显着加快压缩速度的优点。
至于速度差异,我只能猜测使用了不同的编译器或不同的编译器优化。

在Java中,HUFMAN_ONLY是否可设置? - AlexWien
1
“HUFFMAN_ONLY” 实际上是 Java 中 “setStrategy” 的选项。在 zlib 中,它被称为 “Z_HUFFMAN”。 - Mark Adler
谢谢,这解释了原因。如果我有时间,我会尝试使用FILTERED和HUFFMAN_ONLY,并发布结果。 - Matthieu
我更新了我的帖子,其中包含我使用“Z_FILTERED”和“Z_HUFFMAN_ONLY”获得的结果。我喜欢“Z_HUFFMAN_ONLY”策略仍然可以被JAI(我正在使用的TIFF阅读器)解码,尽管它在测试斜坡图像(0、1、2、3、...、254、255、0、...)上没有任何压缩,但它仍然可以在实际示例中提供80%的压缩。感谢您在开发中的辛勤工作! - Matthieu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接