3

そのため、MD5チェックサムを計算するためのより高速な方法を探していて、Fast MD5ライブラリに出くわしましたが、マシンで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

コメントによると、私はベンチアムクを何度も実行し、最も奇妙な結果を得ました。FastMD5の計算は同じままですが、Java7バージョンは遅くなります。????

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

4

3 に答える 3

15

まず、質問の簡単な部分に答えましょう。

コードを再度実行すると、Java 7の実行時間は約2倍になると思います。これは、投稿したコードをforループに入れるだけでは、2、3、4 total、...の実行前に0にリセットするのを忘れてしまうためです。 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

現在、状況は非常に一貫しているように見え、他の応答の1つで言及されている「JVMウォームアップ」も示しており、違いは約1%にすぎません。

では、なぜJava7のパフォーマンスがFastMD5よりも優れているのでしょうか。

彼らはおそらく、後でJavaコンパイラによって実行される最適化にさらに調整されたさらに優れたアルゴリズムを使用していました。

たとえば、nioByteBuffersは、DMAなどのネイティブ機能を使用してメモリへのアクセスを高速化するように特別に設計されています。したがって、MD5のJava 7実装がByteBuffer入力としてではなく、を使用しているという事実byte[]は、実際にこれらの機能を使用していると私に思わせます(そうでなければ、おそらくそれらも使用しているでしょうbyte[])。

Utilitiesさらに言えば、たとえば、オブジェクトが正確に何をしているのかを知り、FastMD5とJava実装のソースコードを比較する必要があるでしょう。

しかし、私はこう言います:あなたの結果(total = 0の修正が与えられた場合)は私にとって完全に理にかなっており、おそらく外部ライブラリへの依存度が1つ少ないという事実を楽しむことができます!;)

ところで:あなたが見ているパフォーマンスの違いは、3.5GHz CPUで処理されたデータのバイトあたり約2〜3 CPUクロックサイクル(バイトあたり合計約15クロックサイクルのうち)に対応します。したがって、違いが非常に小さいことを考えると、使用される正確なプラットフォームとJVMに依存する可能性が非常に高く、2つのうちのどちらかが高速になります。

添加

ベンチマークの数値は、2つのMD5実装で約220〜260MB /秒を処理できることを示しています。これは、Google検索で明らかになった他の主張された仕様を見ると合理的に聞こえます(例:http: //www.zorinaq.com/papers/md5 - 「結果の実装」の下のamd64.html )。それで、あなたが受け取った他のすべての返事とは反対に、私はあなたの番号を信頼するように感じます。

さらに確実にしたい場合は、byte []のサイズを変更し、結果として生じる処理時間の変化を確認してください。正常に機能する場合は、次の関数に適合できる線形関係が表示されます。

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

ここで、mは1バイトあたりの処理時間であり、約1 / 250MB / s = 4ns / byteでbある必要があり、関数がローカル変数などを初期化するために使用するセットアップ時間と、System.currentTimeMillis();かかる時間です。この数はかなり小さいはずです(おそらく1ms未満)。

次に、2つのアルゴリズムのどちらが自分に適しているかを判断するには、mANDを比較する必要がありますb。通常、小さなデータ配列を処理する場合は、どちらのアルゴリズムが優れているかを判断するbよりも重要になる可能性がmありますが、大きなデータセットの場合は、小さい方のアルゴリズムの方mが優れています。

于 2013-02-10T06:19:15.170 に答える
6

私は自分のベンチマークを書きました。私の答え:

It Depends!

これが私の結果です(3.4-trunk-amd64linuxとJava1.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以上のデータから、NativeFast-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以上)で勝者です。

ガンダルフへの質問:

  • ネイティブコードを適切に使用するように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

于 2013-02-12T20:07:27.520 に答える
0

プロファイリングを行う場合、次のルールが重要です。

  1. あなたは最初の実行ではなく、償却されたケースを気にします。したがって、ループでテストを繰り返し実行し、それが落ち着くのを待ちます。

  2. プロファイリング自体に注意する必要があります。あなたの場合、System.currentTimeMillisの最初の数回の実行は、それ以降の実行よりもはるかに時間がかかるため、パフォーマンスメトリックが完全に歪む可能性があります。

  3. 常に多数の反復を測定し、単一の反復を単独で測定しないでください。

  4. 重要性を持たせるには、反復回数を多くする必要があります。また、偏りのない評価を行うには、テストを何度も実行する必要があります。

次のようなものをもう少し実行してみてください。

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) );
}
于 2013-02-07T03:28:22.110 に答える