Java - 微觀效能測試:初學者指南

你好,未來的 Java 巫師們!? 今天,我們將進入 Java 微觀效能測試的精彩世界。如果你從未寫過一行代碼,也別擔心 - 我們將從最基本的開始,並一起逐步進階。所以,來一杯咖啡(或茶,如果你喜歡),我們一起深入探討!

Java - Microbenchmark

什麼是微觀效能測試?

在我們深入 Java 微觀效能測試的細節之前,先來了解微觀效能測試到底是什麼。

想像你是一位正在嘗試完善一道食譜的廚師。你不可能只是嘗嘗最後的菜餚來看看它好不好吃,對吧?你會嘗試每一種食材,測試不同的烹飪時間,並嘗試各種技術。這正是程式設計中微觀效能測試的精髓 - 它是一種衡量你的代碼中小型、獨立部分效能的方式。

為什麼 Java 效能測試很重要?

現在,你可能在想,“我為什麼要關心效能測試?”讓我告訴你一個小故事。

當我還是個初級開發者時,我寫了一個在我的電腦上運行完美的程式。但是當我們把它部署到公司的伺服器上時,它的運行速度比背著沉重背包的烏龜還要慢!那時我才知道效能測試的重要性。它幫助我們:

  1. 識別效能瓶頸
  2. 比較不同的實現方式
  3. 確保我們的代碼在不同的系統上高效運行

Java 效能測試技巧

讓我們看看一些常見的 Java 效能測試技巧:

1. 手動計時

最簡單的效能測試方式是手動計時。以下是一個基本的例子:

public class SimpleTimingExample {
public static void main(String[] args) {
long startTime = System.nanoTime();

// 在這裡放你的代碼
for (int i = 0; i < 1000000; i++) {
Math.sqrt(i);
}

long endTime = System.nanoTime();
long duration = (endTime - startTime);
System.out.println("執行時間: " + duration + " 納秒");
}
}

在這個例子中,我們使用 System.nanoTime() 來測量計算 0 到 999,999 數字的平方根需要多長時間。

2. 使用 JMH (Java 微觀效能測試馬車)

雖然手動計時很簡單,但它不總是準確。這就是 JMH 登場的時候了。JMH 是用於構建、運行和分析納秒/微秒/毫秒/宏觀效能測試的 Java 馬車。

要使用 JMH,你需要將它添加到你的項目中。如果你使用 Maven,請在 pom.xml 中添加以下依賴:

<dependencies>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.35</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.35</version>
</dependency>
</dependencies>

現在,讓我們寫一個簡單的 JMH 測試:

import org.openjdk.jmh.annotations.*;
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.concurrent.TimeUnit;

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
@Fork(value = 2, jvmArgs = {"-Xms2G", "-Xmx2G"})
@Warmup(iterations = 3)
@Measurement(iterations = 3)
public class JMHExample {

@Benchmark
public void benchmarkMathSqrt() {
Math.sqrt(143);
}

public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(JMHExample.class.getSimpleName())
.forks(1)
.build();

new Runner(opt).run();
}
}

這個測試衡量計算 143 的平方根的平均時間。讓我們解釋一下這些註解:

  • @BenchmarkMode:指定要測量什麼(在這裡是平均時間)
  • @OutputTimeUnit:指定結果的單位
  • @State:定義“狀態”對象將被共享的作用域
  • @Fork:單個測試要進行多少次分叉
  • @Warmup@Measurement:定義要進行多少次溫度和測量迭代

Java 集合算法

在我們討論效能測試的話題上,讓我們快速岔開一下,談談 Java 集合算法。這些是非常有用的工具,可以顯著影響你的程式的效能。

以下是一些常見算法的表格:

算法 描述 使用案例
Collections.sort() 排序列表 當你需要對元素進行排序時
Collections.binarySearch() 在排序的列表中搜索 在大型、排序的列表中查找元素
Collections.reverse() 反轉列表 當你需要反轉元素的順序時
Collections.shuffle() 隨機排列列表 隨機化元素的順序
Collections.fill() 使用指定的元素替換所有元素 使用特定值初始化列表

讓我們對使用 Collections.sort() 排序列表的效能進行測試:

import org.openjdk.jmh.annotations.*;
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.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Thread)
@Fork(value = 2, jvmArgs = {"-Xms2G", "-Xmx2G"})
@Warmup(iterations = 3)
@Measurement(iterations = 3)
public class SortingBenchmark {

@Param({"100", "1000", "10000"})
private int listSize;

private List<Integer> list;

@Setup
public void setup() {
list = new ArrayList<>(listSize);
Random rand = new Random();
for (int i = 0; i < listSize; i++) {
list.add(rand.nextInt());
}
}

@Benchmark
public void benchmarkCollectionsSort() {
Collections.sort(list);
}

public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(SortingBenchmark.class.getSimpleName())
.forks(1)
.build();

new Runner(opt).run();
}
}

這個測試衡量不同大小列表(100、1000 和 10000 個元素)的排序時間。運行此測試將給你一個有關排序時間隨列表大小增加的 good idea。

結論

就是這樣,各位!我們只是觸及了 Java 微觀效能測試的皮毛。請記住,效能測試不僅僅是關於编写快速的代碼 - 它是關於理解你的代碼的效能特性和做出明智的決策。

當你繼續你的 Java 過程時,請將效能測試作為你的工具箱中的一員。它就像一個值得信賴的指南針,將幫助你在軟件效能的波濤洶湧之海中導航。

編程愉快,願你的效能測試總是富有啟發性!??‍??‍?

Credits: Image by storyset