Java - Microbenchmark: Panduan untuk Pemula

Hai di sana, rakit penyihir Java masa depan! ? Hari ini, kita akan melakukan perjalanan yang menarik ke dunia microbenchmarking Java. Jangan khawatir jika anda belum pernah menulis satu baris kode sebelum ini - kita akan mulakan dari permulaan dan maju bersama-sama. Jadi, ambillah satu raksa kopi (atau teh, jika itu yang anda suka), dan mari kita melompat masuk!

Java - Microbenchmark

Apa itu Microbenchmarking?

Sebelum kita masuk ke perincian Java microbenchmarking, mari kita faham apa itu microbenchmarking.

Bayangkan anda adalah seorang juru masak yang mencuba untuk memastikan resepi yang sempurna. Anda tidak akan hanya mencuba hidangan akhir untuk melihat jika itu baik, kan? Anda akan mencuba setiap bahan, menguji masa memasak yang berbeza, dan mencuba teknik yang berbeza. Itu adalah persis apa yang microbenchmarking adalah dalam pengaturcaraan - ia adalah cara untuk mengukur prestasi bagi bahagian kecil, terpisah daripada kod anda.

Mengapa Benchmarking Java Penting?

Sekarang, anda mungkin berfikir, "Mengapa saya perlu pedulikan benchmarking?" Well, izinkan saya untuk memberitahu anda satu cerita kecil.

Pada masa saya adalah seorang pengembang junior, saya sekali menulis sebuah program yang berfungsi sempurna... di atas komputer saya. Tetapi apabila kami meyakinkanannya ke atas pelayan syarikat, ia adalah lebih lambat dari seekor kura-kura yang membawa rakit penyimpanan yang berat! Itu adalah apabila saya belajar tentang kepentingan benchmarking. Ia membantu kita:

  1. Mengenal pasti bottleneck prestasi
  2. Membandingkan pelaksanaan yang berbeza
  3. Memastikan kod kita dijalankan dengan efisiensi di atas sistem yang berbeza

Teknik Benchmarking Java

Mari kita lihat beberapa teknik benchmarking Java yang umum:

1. Pemasaan Manual

Cara termudah untuk melakukan benchmarking adalah pemasaan manual. Ini adalah contoh asas:

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

// Kod anda di sini
for (int i = 0; i < 1000000; i++) {
Math.sqrt(i);
}

long endTime = System.nanoTime();
long duration = (endTime - startTime);
System.out.println("Masa eksekusi: " + duration + " nanosekon");
}
}

Dalam contoh ini, kita menggunakan System.nanoTime() untuk mengukur berapa lama ia mengambil untuk mengira akar kuadrat bagi nombor dari 0 hingga 999,999.

2. Menggunakan JMH (Java Microbenchmark Harness)

Walaupun pemasaan manual adalah mudah, ia tidak selalu akurat. Itu adalah di mana JMH masuk. JMH adalah rakit Java untuk membangun, menjalankan, dan menganalisis nano/micro/milli/macro benchmarks.

Untuk menggunakan JMH, anda perlu untuk menambahkannya ke atas projek anda. Jika anda menggunakan Maven, tambahkan dependensi ini ke pom.xml anda:

<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>

Sekarang, mari kita tulis satu benchmark JMH yang mudah:

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 ContohJMH {

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

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

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

Benchmark ini mengukur masa rata-rata yang diambil untuk mengira akar kuadrat bagi 143. Mari kita kongsi penggunaan anotasi:

  • @BenchmarkMode: Tetapkan apa yang hendak diukur ( masa rata-rata dalam kes ini)
  • @OutputTimeUnit: Tetapkan unit bagi hasil
  • @State: Definisikan skop di mana "state" objek akan dikongsi
  • @Fork: Berapa kali hendak untuk fork satu benchmark
  • @Warmup dan @Measurement: Tetapkan berapa kali iterasi penyedut dan pengukuran untuk dibuat

Algoritma Java Collections

Apabila kita berbicara tentang benchmarking, mari kita buat satu detour cepat untuk membicarakan tentang Algoritma Java Collections. Ini adalah alat yang sangat berguna yang boleh memberikan kesan yang besar ke atas prestasi program anda.

Berikut adalah rakaman beberapa algoritma yang umum:

Algoritma Keterangan Kasus Penggunaan
Collections.sort() Mengurutkan senarai Apabila anda perlu untuk mengurutkan elemen
Collections.binarySearch() Mencari di atas senarai yang diurutkan Mencari elemen di atas senarai besar, diurutkan
Collections.reverse() Membalikkan senarai Apabila anda perlu untuk mengubah arahan elemen
Collections.shuffle() Mengacak secara rawak senarai Mengacak arahan elemen
Collections.fill() Mengganti semua elemen dengan elemen yang dinyatakan Menginisialisasi senarai dengan nilai tertentu

Mari kita benchmark prestasi untuk mengurutkan senarai menggunakan 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 BenchmarkPengurutan {

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

private List<Integer> senarai;

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

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

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

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

Benchmark ini mengukur berapa lama ia mengambil untuk mengurutkan senarai dengan ukuran yang berbeza (100, 1000, dan 10000 elemen). Menjalankan ini akan memberikan anda gambaran yang baik tentang bagaimana masa pengurutan meningkat dengan ukuran senarai.

Penutup

Dan itu adalah untuk anda, rakyat! Kita baru saja mencecah permukaan microbenchmarking Java. Ingat, benchmarking bukan hanya tentang menulis kod yang cepat - ia tentang memahami karakteristik prestasi kod anda dan membuat keputusan yang berdasarkan.

Apabila anda teruskan perjalanan anda di atas Java, simpan benchmarking di atas rakit anda. Ia seperti kompas yang dapat dipercayai yang akan membantu anda untuk menavigasi atas lautan yang kadang-kadang kencang dari prestasi perisian.

Happy coding, dan may your benchmarks always be insightful! ??‍??‍?

Credits: Image by storyset