2013年3月6日水曜日

AndroidとOpenJDKのRandom.javaの実装を比較しました

JavaのRandomに同じseedを渡せば、どんな環境でも同じ値が得られるのか?という疑問から、AndroidとOpenJDKの実装を比較することにしました。

java.utilのRandom.javaです。
乱数を得るメソッドはいくつもありますが、これらは最終的にnext(int)を呼びます。
なので、このnext(int)について比較します。

これがAndroid4.2のnext(int)です。
private static final long multiplier = 0x5deece66dL;
 
protected synchronized int next(int bits) {
    seed = (seed * multiplier + 0xbL) & ((1L << 48) - 1);
    return (int) (seed >>> (48 - bits));
}

これがOpenJDK6のnext(int)です。
private final static long multiplier = 0x5DEECE66DL;
private final static long addend = 0xBL;
private final static long mask = (1L << 48) - 1;
 
protected int next(int bits) {
    long oldseed, nextseed;
    AtomicLong seed = this.seed;
    do {
        oldseed = seed.get();
        nextseed = (oldseed * multiplier + addend) & mask;
    } while (!seed.compareAndSet(oldseed, nextseed));
    return (int)(nextseed >>> (48 - bits));
}

肝となる処理はどちらも同じですが、OpenJDKの方が一部の値を定数として保持している分綺麗だと思います。
違いはOpenJDKがAtmicLong#compareAndSet(long, long)でdo whileを回していることです。
AtmicLong#compareAndSet(long, long)こちらの情報によると「この変数をこの新しい値に更新すること。ただし、このメソッドが最後に参照した後で他のスレッドによって値が変更された場合は、更新処理を失敗すること」とあります。
つまり、複数のスレッドから操作した場合にAndroidとOpenJDKで別の結果を得ることになるかもしれません。

0 件のコメント:

コメントを投稿