WebベースのJavaセッション情報用のランダムUUIDを生成するアプリケーションがあります。テスターの1人が、独自のプロファイリングに基づいてUUIDを生成するために最大350msを要求していますが、まだできません。彼の結果を再現します。彼はこの記事を参照しています http://www.cowtowncoder.com/blog/archives/2010/10/entry_429.html 彼の結果をバックアップするのに役立ちます。それ以外の場合は、Java 6またはJava 7アプリケーションのいずれかでJavaの組み込みUUID生成機能を使用して、この制限に遭遇しました。
私はそれをテストしました
for (;;) {
long t0 = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
UUID.randomUUID();
}
System.out.println(System.currentTimeMillis() - t0);
}
私のPCでは、それはかなり遅い1100 msです。 UUID.randomUUID()は内部でSecureRandomを使用して、通常のJava.util.Randomをより速く使用できるようにします
Random r = new Random();
for (;;) {
..
new UUID(r.nextLong(), r.nextLong());
それは〜80ミリ秒です
これはベータ127で実行したテストです。
このテストは非現実的であり、私が想像できる最悪のシナリオを超えていることに注意してください。私の目標は、批判を裏付ける事実なしに [〜#〜] uuid [〜#〜] sの悪口を言う人を黙らせることでした。
シナリオ:
Java.util.UUID.randomUUID()
1つのスレッドで1つのループを実行するため、同期されたメソッド/クラスの競合はありません。
// Warm the random generator.
Java.util.UUID uuid;
uuid = Java.util.UUID.randomUUID();
long stop = 0;
long start = System.nanoTime();
int loops = 1000000; // One million.
for ( int i = 0; i < loops; i++ ) {
uuid = Java.util.UUID.randomUUID();
}
stop = System.nanoTime();
long elapsed = ( stop - start );
System.out.println( "UUIDs: " + loops );
System.out.println( "Nanos: " + elapsed );
System.out.println( "Nanos per uuid: " + ( elapsed / loops ) + " ( micros per: " + ( elapsed / loops / 1000 ) + " )" );
UUIDあたり約2マイクロ秒。
上記と同様ですが、100万回の呼び出しのループを実行している間、それぞれがten100万回の呼び出しを行う2つのスレッドが実行されています。
// Warm the random generator.
Java.util.UUID uuid;
uuid = Java.util.UUID.randomUUID();
int pass = 10_000_000 ; // Ten million.
MyThread t1 = new MyThread( pass );
MyThread t2 = new MyThread( pass );
t1.start();
t2.start();
t3.start();
long stop = 0;
long start = System.nanoTime();
int loops = 1_000_000 ; // One million.
for ( int i = 0; i < loops; i++ ) {
uuid = Java.util.UUID.randomUUID();
}
stop = System.nanoTime();
long elapsed = ( stop - start );
System.out.println( "UUIDs: " + loops );
System.out.println( "Nanos: " + elapsed );
System.out.println( "Nanos per uuid: " + ( elapsed / loops ) + " ( micros per: " + ( elapsed / loops / 1000 ) + " )" );
そして、各スレッドを定義するクラス…
class MyThread extends Thread {
private int loops;
public MyThread( int loops ) {
this.loops = loops;
}
@Override
public void run() {
Java.util.UUID uuid;
for ( int i = 0; i < this.loops; i++ ) {
uuid = Java.util.UUID.randomUUID();
}
}
}
UUIDあたり約20マイクロ秒。
実行は、UUIDあたり14、20、20、23、および24マイクロ秒でした(この順序ではありません)。したがって、極端な競合下では約10倍悪くなり、20マイクロ秒は、私が知っている実際の使用状況では受け入れられます。
UUIDのランダムな形式は、通常、「暗号強度」の乱数のソースを使用します。
(それができなかった場合、いわゆるランダムUUIDが予測可能になり、指定されたUUIDが再発行される可能性が心配なレベルまで増加する可能性があります。別の回答が示唆するように、高速(ただし弱い)PRNGをUUID
コンストラクタに渡します。しかし、それは悪い考えです。)
一般的な暗号強度の乱数ジェネレータは、アプリケーションの外部にあるエントロピーのソースを使用します。それはハードウェア乱数ジェネレーターかもしれませんが、より一般的には、通常の操作でオペレーティングシステムによって収集される「ランダムさ」が蓄積されます。問題は、エントロピーのソースにレート制限があることです。一定の期間にわたってそのレートを超えた場合、ソースを排出できます。次に何が起こるかはシステムに依存しますが、システムによっては、エントロピーを読み取るためのシステムコールが...が利用可能になるまで停止します。
それがクライアントのシステムで起こっていることだと思います。 (仮想マシンでは珍しくありません...)
(Linuxシステムの)ハックな回避策の1つは、rngd
デーモンをインストールし、適切な疑似乱数ジェネレータを使用してエントロピープールを「補充」するようにデーモンを構成することです。セキュリティの専門家は次のように指摘します。
このハックが実際にどれほど安全かはわかりません。
遅い乱数生成についての別のQ&Aは次のとおりです。
スレッドの数は、UUIDの生成のパフォーマンスに大きな影響を与えます。これは、UUID.randomUUID()
の乱数を生成するSecureRandom#nextBytes(byte[]
の実装を見ると説明できます。
synchronized public void nextBytes(byte[] bytes) {
secureRandomSpi.engineNextBytes(bytes);
}
nextBytes
はsynchronized
であり、異なるスレッドからアクセスされるとパフォーマンスが大幅に低下します。
バージョン1タイプのUUIDを使用するのはどうですか?
バージョン1 は MACアドレス と現在の時刻(「空間と時刻」)に基づいています。バージョン4よりも衝突の可能性ははるかに低くなります。
バージョン4 は、暗号学的に強力なランダムジェネレーターを使用して乱数から完全に生成されることに基づいています。
Oracle JVMはバージョン1ジェネレーターを提供していません。これは明らかにセキュリティとプライバシーの問題のためです。 JVMは、ホストマシンのMACアドレスへのアクセスを提供しません。
バージョン1 UUIDとその他のバージョンを提供するサードパーティライブラリが少なくとも1つあります。 JUG – Java UUID Generator 。 Java 6で導入された機能により、MACアドレスにアクセスできると彼らは言っています。
2010記事のJava UUID Generatorバージョン3を使用したテスト結果によるパフォーマンスの説明を読んでください More on Java UUIDジェネレーター(JUG)、パフォーマンスの言葉 。 Tatu Salorantaは、MacBookでさまざまな種類のUUIDをテストしました。
アップショット:MAC + Timeバージョンは、ランダムバージョンの20倍高速です。
時間ベースのバリアント(イーサネットアドレスとタイムスタンプ)ははるかに高速です-ほぼランダムベースの20倍の速度デフォルトのバリアント-毎秒約500万のUUIDを生成します。
Jdk 1.7.0_40で実行されるjunitテスト:
package org.corba.util;
import org.junit.Test;
import org.springframework.util.StopWatch;
import Java.util.UUID;
/**
* Test of performance of Java's UUID generation
* @author Corba Da Geek
* Date: 1/6/14
* Time: 3:48 PM
*/
public class TestRandomUUID {
private static final int ITERATIONS = 1000000;
@Test
public void testRandomUUID() throws Exception {
// Set up data
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// Run test
for (int i = 0; i < ITERATIONS; i++)
UUID.randomUUID();
// Check results
stopWatch.stop();
final long totalTimeMillis = stopWatch.getTotalTimeMillis();
System.out.println("Number of milliseconds: " + totalTimeMillis + " for " + ITERATIONS + " iterations.");
System.out.println(String.format("Average time per iteration: %.7f ms", (float)totalTimeMillis/ITERATIONS));
}
}
そして私のi5ラップトップでの結果は:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running org.corba.util.TestRandomUUID
Number of milliseconds: 677 for 1000000 iterations.
Average time per iteration: 0.0006770 ms
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.746 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
呼び出しごとに0.0006770ミリ秒。
私は他の人と同じテストを行いました私の結果はUUID生成ごとに300 NANOsecondsに似ています。結果は、i7クアッドコアWIN7 64 PCでの結果です。 jdk1.7.0_67とjdk1.8.0_40 64ビットJVMを試してみました。
私は困惑しているようです。私の結果は他のすべてのものとはとても異なります...しかし、乱数を生成するための1 msはLOTのように見えました!
public static void main(String[] args) throws Exception {
long start = System.nanoTime();
int loops = 1000000; // One million.
long foo = 0;
for (int i = 0; i < loops; i++) {
UUID uuid = Java.util.UUID.randomUUID();
//this is just to make sure there isn't some kind of optimization
//that would prevent the actual generation
foo += (uuid.getLeastSignificantBits()
+ uuid.getMostSignificantBits());
}
long stop = System.nanoTime();
long elapsed = (stop - start);
System.out.println(String.format("UUIDs : %,d", loops));
System.out.println(String.format("Total time (ns) : %,d", elapsed));
System.out.println(String.format("Time per UUID (ns) : %,d", (elapsed / loops)));
System.out.println();
System.out.println(foo);
}
出力 :
UUIDs : 1 000 000
Total time (ns) : 320 715 288
Time per UUID (ns) : 320
5372630452959404665