所以我一直在寻找一种更快的方法来计算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
答案 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毫秒)。
然后,要确定哪种算法更适合您,您需要比较m
和b
。如果您经常处理小型数据阵列,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的问题:
我真的不可能将基准标记作为“sscce” - 这是150行!你可以在这里下载:
http://juliusdavies.ca/base64bench/
像这样运行它(用ant构建它之后):
java ca.juliusdavies.base64bench.MD5BenchByte2Byte MD5.so
以下是基准源代码的直接链接:
答案 2 :(得分:0)
分析时,以下规则很重要:
您关心的是摊还的情况,而不是第一次运行。因此,在循环中重复运行测试并等待它稳定下来。
您需要注意分析本身。在您的情况下,System.currentTimeMill的前几次运行所花费的时间比以后的运行时间长得多,可能会完全扭曲您的性能指标。
始终对大量迭代进行测量,从不单独测量单个迭代。
迭代次数需要很大才有意义,并且您需要多次运行测试以获得无偏见的评估。
尝试运行更像以下内容:
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) );
}