「原創宣告:保留所有權利,禁止轉載」
前文分享了幾種效能測試中常用到的生成全域性唯一標識的案例,雖然在文中我猜測了幾種方案設計的效能,並根據自己的經驗給出了適用的場景。
但對於一個效能測試工程師來講,有真是測試資料才更有說服力。這讓我想起來之前學過的 Java 微基準測試框架 JMH
,所以不妨一試。
JMH 簡介
JMH (Java Microbenchmark Harness) 是一個用於編寫和執行 Java 基準測試的工具。它被廣泛用於評估 Java 應用程式的效能,並幫助開發人員發現和最佳化效能瓶頸。
JMH 的主要特點包括:
- 高可信度:JMH 提供了多種機制來消除測試過程中的噪音和偏差,確保測試結果的可靠性。
- 易用性:JMH 提供了豐富的註解和 API,使編寫和執行基準測試變得相對簡單。
- 靈活性:JMH 支援多種測試模式,如簡單的吞吐量測試、微基準測試以及更復雜的測試場景。
- 可擴充套件性:JMH 允許使用者自定義測試環境,如 GC 策略、編譯器選項等,以滿足特定的效能評估需求。
- 廣泛應用:JMH 被廣泛應用於 Java 生態系統中,包括 JDK 自身的效能最佳化、第三方開源庫的效能評估等。
JMH 是 Java 開發者評估應用程式效能的強大工具,有助於提高 Java 應用程式的整體質量和效能。同樣地對於效能測試而言,也可以透過 JMH
測試評估一段程式碼在實際執行當中的表現。
實測
除了 使用分散式服務生成GUID
這個方案以外,其他四種方案(其中兩種是我自己常用的)均參與測試。原因是分散式服務需要網路互動,這個一聽就不高效能,還有我暫時沒條件測試這個。
下面有限展示實測結果,總結使用執行緒共享和執行緒獨享的方案效能均遠遠高於 UUID
和 雪花演算法
。為了省事兒以下測試均預熱 2 次,預熱批次大小 2,測試迭代次數 1 次,迭代批次大小也是 1 次。配置如下:
.warmupIterations(2)//預熱次數
.warmupBatchSize(2)//預熱批次大小
.measurementIterations(1)//測試迭代次數
.measurementBatchSize(1)//測試批次大小
.build();
PS:JMH
貌似還不支援 Groovy
所以我用 Java
寫了這個用例。
下面是執行 1 個執行緒的測試結果:
UniqueNumberTest.exclusive thrpt 203.146 ops/us
UniqueNumberTest.share thrpt 99.860 ops/us
UniqueNumberTest.snow thrpt 4.096 ops/us
UniqueNumberTest.uuid thrpt 11.758 ops/us
下面是執行 10 個執行緒的測試結果:
Benchmark Mode Cnt Score Error Units
UniqueNumberTest.exclusive thrpt 1117.347 ops/us
UniqueNumberTest.share thrpt 670.141 ops/us
UniqueNumberTest.snow thrpt 10.925 ops/us
UniqueNumberTest.uuid thrpt 3.608 ops/us
PS:此時機器的效能基本跑滿了。
下面是 40 個執行緒的測試結果:
Benchmark Mode Cnt Score Error Units
UniqueNumberTest.exclusive thrpt 1110.273 ops/us
UniqueNumberTest.share thrpt 649.350 ops/us
UniqueNumberTest.snow thrpt 8.908 ops/us
UniqueNumberTest.uuid thrpt 4.205 ops/us
可以看出跟 10 個執行緒結果差不多。
本機配置 12 核心,以上的測試結果單位是微秒,把結果乘以 100 萬就是每秒的處理量,各位在使用不同方案時可以適當參考。
測試用例
下面是我的測試用例,測試結果我就不進行視覺化了。
package com.funtest.jmh;
import com.funtester.utils.SnowflakeUtils;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.results.format.ResultFormatType;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@BenchmarkMode(Mode.Throughput)
//@Warmup(Ω = 3, time = 2, timeUnit = TimeUnit.SECONDS)//預熱次數,含義是每個測試會跑多久
//@Measurement(iterations = 3, time = 5, timeUnit = TimeUnit.SECONDS)//測試迭代次數,含義是每個測試會跑多久
//@Threads(1)//測試執行緒數
//@Fork(2)//fork表示每個測試會fork出幾個程序,也就是說每個測試會跑幾次
@State(value = Scope.Thread)//預設為Scope.Thread,含義是每個執行緒都會有一個例項
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public class UniqueNumberTest {
SnowflakeUtils snowflakeUtils = new SnowflakeUtils(1, 1);
ThreadLocal<Integer> exclusive = ThreadLocal.withInitial(() -> 0);
AtomicInteger share = new AtomicInteger(0);
@Benchmark
public void uuid() {
UUID.randomUUID();
}
@Benchmark
public void snow() {
snowflakeUtils.nextId();
}
@Benchmark
public void exclusive(Blackhole blackhole) {
Integer i = exclusive.get();
i++;
blackhole.consume(i + "");
}
@Benchmark
public void share(Blackhole blackhole) {
blackhole.consume(share.incrementAndGet() + "");
}
public static void main(String[] args) throws RunnerException {
Options options = new OptionsBuilder()
.include(UniqueNumberTest.class.getSimpleName())//測試類名
.result("long/result.json")//測試結果輸出到result.json檔案
.resultFormat(ResultFormatType.JSON)//輸出格式
.forks(1)//fork表示每個測試會fork出幾個程序,也就是說每個測試會跑幾次
.threads(40)//測試執行緒數
.warmupIterations(2)//預熱次數
.warmupBatchSize(2)//預熱批次大小
.measurementIterations(1)//測試迭代次數
.measurementBatchSize(1)//測試批次大小
.build();
new Runner(options).run();
}
}
- 2021 年原創合集
- 2022 年原創合集
- 2023 年原創合集
- 服務端功能測試
- 效能測試專題
- Java、Groovy、Go、Python
- 單元&白盒&工具合集
- 測試方案&BUG&爬蟲&UI 自動化
- 測試理論雞湯
- 社群風采&影片合集
如果覺得我的文章對您有用,請隨意打賞。您的支援將鼓勵我繼續創作!
打賞支援
暫無回覆。