UUID 生成器有多快

轉載自:https://lotabout.me/2019/UUID-Generator-Benchmark/

敘述

在 Java 中,我們常用 UUID.randomUUID() 來隨機生成一個 UUID。但在某些極端的情況下,它的性能可能滿足不了你的要求(雖然幾乎不可能出現)。這裏我們測試了 4 種 UUID 生成器的性能。

測試結果

最終測試的結果如下(雖然只看到 3 根線,但其實有 4 根,其中藍線 UUID.randomUUID 與綠線 jugWithSecureRandom 幾乎重合):

可以看到:

  • 在單線程時,jugWithRandom 遠遠超過其它的生成器,而 jugTime 次之。
  • 隨着線程的增加,各生成器的吞吐均有所下降。
    • randomUUID 中使用 SecureRandom 來獲取隨機數,而它是通過獲取操作系統的一些隨機噪聲來生成隨機數的,所以是安全的,但性能卻不是很好(相對)。
    • 所有這些生成器都是線程安全的,換句話說內部會做線程同步,因此線程增加,吞吐會下降。
    • 其中 Random 是用 CAS 來完成同步,其餘均使用 synchronized,理論上高併發下,線程數越多,Random 的性能越差,而其它則幾乎不變。
  • 注意 jugTime 在單線程時吞吐接近 1w/ms,這也是基於時間的 UUID 每毫秒能擁有的最大數值(參考uuid-timebased 說明)。
  • jugTime 的生成器的性能幾乎總是優於 randomUUID
  • 不過現實中,不太能遇到有場景需要有 1k/ms 這樣的吞吐需求。

測試設置

這裏我們測試了 java 內置的 UUID.randomUUID(), java-uuid-generator 的 TimeBasedGenerator 和 RandomBasedGenerator,而其中隨機數發生器分別選用 Random 和 SecureRandom測試代碼 如下:

@BenchmarkMode({Mode.Throughput})
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
@Warmup(iterations = 5)
public class MyBenchmark {

    private RandomBasedGenerator randomBasedGenerator;
    private RandomBasedGenerator jugRandomGenerator;
    private TimeBasedGenerator timeBasedGenerator;

    @Setup
    public void init() {
        randomBasedGenerator = Generators.randomBasedGenerator();
        timeBasedGenerator = Generators.timeBasedGenerator();
        jugRandomGenerator = Generators.randomBasedGenerator(new Random());
    }

    @Benchmark
    public void UUIDRandomUUID(Blackhole bh) {
        bh.consume(UUID.randomUUID());
    }

    @Benchmark
    public void jugWithRandom(Blackhole bh) {
        bh.consume(jugRandomGenerator.generate());
    }

    @Benchmark
    public void jugWithSecureRandom(Blackhole bh) {
        bh.consume(randomBasedGenerator.generate());
    }

    @Benchmark
    public void jugTime(Blackhole bh) {
        bh.consume(timeBasedGenerator.generate());
    }
}

測試框架使用 Jmh。測試使用 jdk 1.8 在 8C MacBook Pro 下完成,分別測試了 1,2,4,8 個線程下的吞吐。

寫在後面

這個測試的起因是產品在壓測的時候發現 UUID 生成佔 Running 線程較大的部分,且 JProfiler 的線程圖中有許多線程是 Blocking 的狀態,因此猜測是 UUID.randomUUID中的synchronized 導致線程同步慢,所以想找一些替代的生成器。最後發現 UUID.randomUUID 的吞吐並不是什麼大的問題,但也很慶幸做了這個測試,瞭解了 UUID 生成器的能力,還有 Time-Based UUID 也是一個不錯的選擇。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章