Java の SecureRandom を利用して、以下の実装のスループットを比較します。
- Java 標準搭載
NativePRNG
NativePRNGBlocking
NativePRNGNonBlocking
- Intel 64 命令セット
RDRAND
RDSEED
- Linux システムコール
getramdom(2)
(ブロッキングモード)getramdom(2)
(ノンブロッキングモード)
- Java 22 以降
- Linux カーネル 3.17 以降
- glibc 2.25 以降
SecureRandom 数値生成アルゴリズム で紹介されている、Linux の random 系デバイスファイルをソースとする疑似乱数ジェネレータを使用します。 java.security.egd
システムプロパティや securerandom.source
セキュリティプロパティが設定されていないデフォルト状態では、各実装(アルゴリズム)は以下をソースとする組み合わせで初期化されます。
OpenJDK 22 GA のソースコード: https://github.com/openjdk/jdk/blob/jdk-22-ga/src/java.base/unix/classes/sun/security/provider/NativePRNG.java#L130
NativePRNG
- 乱数シード:
/dev/random
- 乱数生成(next 系メソッドで利用):
/dev/urandom
- 乱数シード:
NativePRNGBlocking
- 乱数シード:
/dev/random
- 乱数生成(next 系メソッドで利用):
/dev/random
- 乱数シード:
NativePRNGNonBlocking
- 乱数シード:
/dev/urandom
- 乱数生成(next 系メソッドで利用):
/dev/urandom
- 乱数シード:
RDRAND
と RDSEED
を使用します。どちらの命令も最大で 8 バイト(64 bit)の書き込みのサポートとなるため、今回実験する 256 バイトの乱数生成を行うために、繰り返し各命令を実行します。
命令実行のため ffmasm を用いて各命令実行用の機械語を生成します。
getrandom(2) で乱数を生成します。manpage より、256 バイトまではシグナル割り込みを受けず値を即返すことができるため、このベンチマークでは 256 バイトの乱数生成を試験条件とします。
Java では getrandom(2)
を呼び出す API は用意されないため、Foreign Function & Memory API で実行します。
- Java ヒープサイズ 8GB(
-Xms8g -Xmx8g
) - GC アルゴリズムは Epsilon
-XX:+AlwaysPreTouch
を付与- 256 バイトの乱数を 10 秒間に生成するスループットを 3 回測定する
- Intel 64 命令セットは ffmasm で直接実行する
- コード生成は FFMHelper.java で行う
getrandom(2)
は FFM で呼び出す- FFM で呼び出す
RDRAND
/RDSEED
/getrandom(2)
は critical function として定義し、生成した乱数の書き込み先は Java ヒープ上のbyte[]
とする
mvn package
FFM で呼び出す RDRAND
/ RDSEED
/ getrandom(2)
( GRND_RANDOM
つき) / getrandom(2)
( GRND_NONBLOCK
)については生成した乱数の一部を確認できます。
mvn exec:java
mvn package
で生成した JAR は JMH と結合済みのため、実行するだけでベンチマークできます。
java -jar target/randbench-0.1.0.jar