快速MD5库并不比Java 7 MD5快?

时间:2013-01-31 23:29:52

标签: java performance md5 benchmarking

所以我一直在寻找一种更快的方法来计算MD5校验和并在Fast MD5 library上运行 - 但是当我在我的机器上使用Java 7进行基准测试时,它比Java版本慢得多。

我要么做一些愚蠢的事情(非常可能),要么Java 7已经实现了更好的算法(也很可能)。这是我的超级简单"基准" - 也许我今天没有足够的咖啡......

    MD5 digest = new MD5();
    System.out.println(MD5.initNativeLibrary(true));
    byte[] buff = IOUtils.readFully(new FileInputStream(new File("blahblah.bin")), 64000000, true);
    ByteBuffer buffer = ByteBuffer.wrap(buff);
    for (int j = 0; j < 100; j++) {
        start = System.currentTimeMillis();
        String md5Base64 = Utilities.getDigestBase64(buffer);
        end = System.currentTimeMillis();
        total = total + (end-start);
    }
    System.out.println("Took " + ((total)/100.00) + " ms. for " + buff.length+" bytes");
    total = 0;
    for (int i = 0; i < 100; i++) {
        start = System.currentTimeMillis();
        digest.Init();
        digest.Update(buff);
        digest.Final();
        end = System.currentTimeMillis();
        total = total + (end-start);
    }
    System.out.println("Took " + ((total)/100.00) + " ms. for " + buff.length+" bytes");

我得到了:

Took 247.99 ms. for 64000000 bytes
Took 295.16 ms. for 64000000 bytes

根据评论,我一遍又一遍地运行benchamrk,得到了最奇怪的结果。 FastMD5计算保持不变,但Java 7版本变慢。 ????

Took 246.54 ms. for 64000000 bytes
Took 294.69 ms. for 64000000 bytes
************************************
Took 540.55 ms. for 64000000 bytes
Took 292.69 ms. for 64000000 bytes
************************************
Took 537.07 ms. for 64000000 bytes
Took 292.12 ms. for 64000000 bytes

3 个答案:

答案 0 :(得分:13)

让我们首先回答您问题的简单部分:

我认为当你再次运行代码时,你的Java 7执行时间会大致加倍,因为如果你只是将你发布的代码放到for循环中,你就会忘记在{2}之前将total重置为0 ,4,...运行Java 7测试(对于第一个,它可能从变量初始化设置为0)。

因此,通过简单地减去未设置为0的偏移来修复表格给出:

Took 246.54 ms. for 64000000 bytes
Took 294.69 ms. for 64000000 bytes              <---.
************************************                |
Took 245.86 ms. for 64000000 bytes   (subtracting 294.69)
Took 292.69 ms. for 64000000 bytes              <---.
************************************                |
Took 244.38 ms. for 64000000 bytes   (subtracting 292.69)
Took 292.12 ms. for 64000000 bytes

现在,事情似乎非常一致,甚至显示其他一个回复中提到的“JVM热身”,它只会产生约1%的差异。

现在,为什么Java 7的性能优于FastMD5?

他们可能使用了更好的算法,后者更适合Java编译器执行的优化。

例如,nio ByteBuffers专门设计用于通过使用DMA等本机内容来更快地访问内存。因此,MD5的Java 7实现使用ByteBuffer作为输入而不是byte[]这一事实使我认为他们实际上正在利用这些功能(否则他们可能也会采取这些功能) byte[]。)

再说一遍,我们可能需要知道你的Utilities对象到底做了什么,然后比较FastMD5的源代码和Java实现。

但是我会说:你的结果(总计= 0修复)对我来说非常有意义,你可能只是喜欢这样一个事实:你可以减少对外部库的依赖! ;)

BTW:在3.5GHz CPU上,您看到的性能差异仅相当于每个处理的数据字节大约2-3个CPU时钟周期(每个字节总共约15个时钟周期)。因此,鉴于差异非常小,很可能,它将取决于所使用的确切平台和JVM,其中一个最终会更快。

<强>加成

您的基准测试数据表明,您可以使用两个MD5实施处理大约220-260MB / s,如果您查看Google搜索显示的其他声称规格(例如{“3}}”生成实施“下,这听起来是合理的)。所以,与你收到的所有其他回复相反,我觉得我相信你的数字。

如果您想要更加确定,请改变字节[]的大小,并查看处理时间的结果变化。如果事情按预期工作,您将看到线性关系,您可以使用此函数:

total/100.0 = m * buff.length + b           (your usual y = mx + b)

这里,m是每字节的处理时间,应该是大约1 / 250MB / s = 4ns /字节,b是函数用来初始化局部变量等的设置时间,以及System.currentTimeMillis();所花费的时间。这个数字应该相当小(可能小于1毫秒)。

然后,要确定哪种算法更适合您,您需要比较mb。如果您经常处理小型数据阵列,b在确定哪种算法更好时可能比m更重要,而对于大型数据集,m更小的算法更好< / p>

答案 1 :(得分:4)

我写了自己的基准。我的回答:

It Depends!

以下是我的结果(在3.4-trunk-amd64 linux和Java 1.7.0_05上运行):

1。)对于少量数据,Java获胜。

TINY DATA new byte[12]      SMALL DATA new byte[123]

Java builtin MD5...         Java builtin MD5...
encode 55 MB/s              encode 217 MB/s
encode 55 MB/s              encode 215 MB/s

Java Fast-MD5...            Java Fast-MD5...
encode 31 MB/s              encode 150 MB/s
encode 32 MB/s              encode 159 MB/s

Native Fast-MD5...          Native Fast-MD5...
encode 22 MB/s              encode 133 MB/s
encode 22 MB/s              encode 133 MB/s

2。)从1KB的数据开始,Native Fast-MD5总是获胜:

MEDIUM DATA new byte[1234]  LARGE DATA new byte[12345]

Java builtin MD5...         Java builtin MD5...
encode 351 MB/s             encode 366 MB/s
encode 351 MB/s             encode 369 MB/s

Java Fast-MD5...            Java Fast-MD5...
encode 300 MB/s             encode 325 MB/s
encode 298 MB/s             encode 322 MB/s

Native Fast-MD5...          Native Fast-MD5...
encode 434 MB/s             encode 582 MB/s
encode 450 MB/s             encode 574 MB/s

3。)12KB后,速度似乎稳定。 123KB没有真正的变化:

X-LARGE DATA new byte[123456]

Java builtin MD5...
encode 367 MB/s
encode 370 MB/s

Java Fast-MD5...
encode 325 MB/s
encode 324 MB/s

Native Fast-MD5...
encode 571 MB/s
encode 599 MB/s

结论:

  • Java的内置MD5总是在我的设置中击败Fast-MD5的后备(非本机)实现。

  • 随着数据量变大,所有实施都会加快。

  • Fast-MD5的原生实现是拥有更大数据(1KB或更大)的赢家。

Gandalf的问题:

  • 您确定要设置Fast-MD5安装以正确使用本机代码(例如,Fast-MD5能够找到MD5.so或MD5.dll)吗?

我真的不可能将基准标记作为“sscce” - 这是150行!你可以在这里下载:

http://juliusdavies.ca/base64bench/

像这样运行它(用ant构建它之后):

java ca.juliusdavies.base64bench.MD5BenchByte2Byte MD5.so

以下是基准源代码的直接链接:

http://juliusdavies.ca/base64bench/exploded/base64bench/src/java/ca/juliusdavies/base64bench/MD5BenchByte2Byte.java.html

答案 2 :(得分:0)

分析时,以下规则很重要:

  1. 您关心的是摊还的情况,而不是第一次运行。因此,在循环中重复运行测试并等待它稳定下来。

  2. 您需要注意分析本身。在您的情况下,System.currentTimeMill的前几次运行所花费的时间比以后的运行时间长得多,可能会完全扭曲您的性能指标。

  3. 始终对大量迭代进行测量,从不单独测量单个迭代。

  4. 迭代次数需要很大才有意义,并且您需要多次运行测试以获得无偏见的评估。

  5. 尝试运行更像以下内容:

    MD5 digest = new MD5();
    System.out.println(MD5.initNativeLibrary(true));
    byte[] buff = IOUtils.readFully(new FileInputStream(new File("blahblah.bin")), 64000000, true);
    ByteBuffer buffer = ByteBuffer.wrap(buff);
    
    int iterations = 10000;
    
    while(true)
    {
       //
       // 1. Run the first test:
       //
       start = System.currentTimeMillis();
       for (int j = 0; j < iterations; j++) {
           String md5Base64 = Utilities.getDigestBase64(buffer);
       }
       end = System.currentTimeMillis();
       System.out.println("(1) " + (start - end) );
    
       //
       // 2. Run the second test:
       //
       start = System.currentTimeMillis();
       for (int i = 0; i < iterations; i++) {
          digest.Init();
          digest.Update(buff);
          digest.Final();
       }
       end = System.currentTimeMillis();
    
       System.out.println("(2) " + (start - end) );
    }