generated from Learning-Is-Vital-In-Development/study-template
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
221 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,221 @@ | ||
--- | ||
marp: true | ||
--- | ||
|
||
# Gradle 환경에서 JMH 세팅하고 피보나치 성능 측정하기 | ||
|
||
--- | ||
|
||
## Gradle 설정하기 | ||
|
||
build.gradle.kts | ||
|
||
```gradle | ||
plugins { | ||
id("me.champeau.jmh") version "0.7.2" | ||
} | ||
``` | ||
|
||
```gradle | ||
dependencies { | ||
jmh("org.openjdk.jmh:jmh-core:1.36") | ||
jmh("org.openjdk.jmh:jmh-generator-annprocess:1.36") | ||
jmhAnnotationProcessor("org.openjdk.jmh:jmh-generator-annprocess:1.36") | ||
} | ||
``` | ||
|
||
```gradle | ||
jmh { | ||
fork = 1 | ||
warmupIterations = 1 | ||
iterations = 1 | ||
} | ||
``` | ||
|
||
--- | ||
|
||
## 프로젝트 구조 | ||
|
||
 | ||
|
||
- src/jmh 하위 디렉토리에 벤치마크 코드 작성 | ||
|
||
--- | ||
|
||
## 실행 방법 | ||
|
||
```bash | ||
./gradlew jmh | ||
``` | ||
|
||
혹은 IDE에서 실행 버튼을 눌러서 실행 | ||
|
||
<br> | ||
|
||
👉 소스 코드 확인하기 | ||
|
||
--- | ||
|
||
```java | ||
@Fork(warmups = 2, value = 2) | ||
@Warmup(iterations = 2, time = 2, timeUnit = TimeUnit.SECONDS) | ||
@BenchmarkMode(value = Mode.AverageTime) | ||
@OutputTimeUnit(TimeUnit.NANOSECONDS) | ||
@Measurement(iterations = 2, time = 2, timeUnit = TimeUnit.SECONDS) | ||
public class FibonacciBenchmark | ||
``` | ||
|
||
- `@Fork`: 측정 반복 횟수 | ||
- `@Warmup`: 측정 전에 JVM을 미리 warmup하는 횟수와 시간 | ||
- `@BenchmarkMode`: 측정 방식 | ||
- `Mode.AverageTime`: 평균 시간 측정 | ||
- `Mode.Throughput`: 초당 처리량 측정 | ||
- `Mode.SampleTime`: 측정 대상 메서드의 실행 시간 측정 (최대/최소 시간 등) | ||
- `Mode.SingleShotTime`: 측정 대상 메서드의 단일 실행 시간 측정 (Cold Start) | ||
- `Mode.All`: 모든 방식으로 측정 | ||
- `@OutputTimeUnit`: 측정 결과를 출력할 시간 단위 | ||
- `@Measurement`: 측정 반복 횟수 | ||
|
||
--- | ||
|
||
그 밖의 어노테이션들 | ||
|
||
- `@State`: Argument 상태 지정 | ||
- `@State(Scope.Thread)`: 쓰레드마다 인스턴스 생성 | ||
- `@State(Scope.Benchmark)`: 벤치마크마다 인스턴스 생성 | ||
- `@State(Scope.Group)`: 같은 그룹에 속한 벤치마크마다 인스턴스 생성 | ||
- `@Threads`: 쓰레드 개수 지정 | ||
- `@Setup`: 측정 전에 실행할 메서드 지정 | ||
- `@TearDown`: 측정 후에 실행할 메서드 지정 | ||
- `@Benchmark`: 측정 대상 메서드로 지정 | ||
|
||
--- | ||
|
||
피보나치 벤치마크 수행 결과 | ||
|
||
```txt | ||
Benchmark Mode Cnt Score Error Units | ||
FibonacciBenchmark.bottomUp avgt 3 50.900 ± 0.649 ns/op | ||
FibonacciBenchmark.bottomUp:·gc.alloc.rate avgt 3 3746.501 ± 45.707 MB/sec | ||
FibonacciBenchmark.bottomUp:·gc.alloc.rate.norm avgt 3 200.000 ± 0.001 B/op | ||
FibonacciBenchmark.bottomUp:·gc.count avgt 3 18.000 counts | ||
FibonacciBenchmark.bottomUp:·gc.time avgt 3 17.000 ms | ||
FibonacciBenchmark.bottomUp:·stack avgt NaN --- | ||
FibonacciBenchmark.memoization avgt 3 99.987 ± 2.166 ns/op | ||
FibonacciBenchmark.memoization:·gc.alloc.rate avgt 3 1907.262 ± 41.570 MB/sec | ||
FibonacciBenchmark.memoization:·gc.alloc.rate.norm avgt 3 200.000 ± 0.001 B/op | ||
FibonacciBenchmark.memoization:·gc.count avgt 3 10.000 counts | ||
FibonacciBenchmark.memoization:·gc.time avgt 3 11.000 ms | ||
FibonacciBenchmark.memoization:·stack avgt NaN --- | ||
FibonacciBenchmark.naiveRecursive avgt 3 2239616569.333 ± 42666708.402 ns/op | ||
FibonacciBenchmark.naiveRecursive:·gc.alloc.rate avgt 3 ≈ 10⁻⁴ MB/sec | ||
FibonacciBenchmark.naiveRecursive:·gc.alloc.rate.norm avgt 3 408.000 ± 0.001 B/op | ||
FibonacciBenchmark.naiveRecursive:·gc.count avgt 3 ≈ 0 counts | ||
FibonacciBenchmark.naiveRecursive:·stack avgt NaN --- | ||
FibonacciBenchmark.stream avgt 3 634.686 ± 18.670 ns/op | ||
FibonacciBenchmark.stream:·gc.alloc.rate avgt 3 2872.475 ± 83.524 MB/sec | ||
FibonacciBenchmark.stream:·gc.alloc.rate.norm avgt 3 1912.000 ± 0.001 B/op | ||
FibonacciBenchmark.stream:·gc.count avgt 3 14.000 counts | ||
FibonacciBenchmark.stream:·gc.time avgt 3 15.000 ms | ||
FibonacciBenchmark.stream:·stack avgt NaN --- | ||
FibonacciBenchmark.tailRecursive avgt 3 26.291 ± 0.311 ns/op | ||
FibonacciBenchmark.tailRecursive:·gc.alloc.rate avgt 3 ≈ 10⁻⁴ MB/sec | ||
FibonacciBenchmark.tailRecursive:·gc.alloc.rate.norm avgt 3 ≈ 10⁻⁵ B/op | ||
FibonacciBenchmark.tailRecursive:·gc.count avgt 3 ≈ 0 counts | ||
FibonacciBenchmark.tailRecursive:·stack avgt NaN --- | ||
``` | ||
|
||
--- | ||
|
||
GCProfiler, StackProfiler 관련 정보들 | ||
|
||
- `gc.alloc.rate`: GC 이벤트로 인한 객체 할당 비율 | ||
- `gc.alloc.rate.norm`: `gc.alloc.rate`를 실행 시간으로 나눈 것 | ||
- `gc.count`: GC 이벤트 횟수 | ||
- `gc.time`: GC 이벤트에 소요된 시간 | ||
- `stack`: 메소드 호출 스택 트레이스 | ||
|
||
--- | ||
|
||
```java | ||
@Fork(warmups = 2, value = 2) | ||
@Warmup(iterations = 2, time = 2, timeUnit = TimeUnit.SECONDS) | ||
@BenchmarkMode(value = Mode.AverageTime) | ||
@OutputTimeUnit(TimeUnit.NANOSECONDS) | ||
@Measurement(iterations = 2, time = 2, timeUnit = TimeUnit.SECONDS) | ||
public class FibonacciBenchmark { | ||
public int fibNaiveRecursive(int x) { | ||
return (x == 1 || x == 2) ? 1 : fibNaiveRecursive(x - 1) + fibNaiveRecursive(x - 2); | ||
} | ||
|
||
public int fibTailRecursive(int x) { | ||
return fibTailRec(x, 0, 1); | ||
} | ||
|
||
private int fibTailRec(int n, int a, int b) { | ||
if (n == 0) return a; | ||
if (n == 1) return b; | ||
return fibTailRec(n - 1, b, a + b); | ||
} | ||
|
||
public int fibMemoization(int x, int[] mem) { | ||
if (mem[x] != 0) return mem[x]; | ||
if (x == 1 || x == 2) return 1; | ||
int n = fibMemoization(x - 1, mem) + fibMemoization(x - 2, mem); | ||
mem[x] = n; | ||
return n; | ||
} | ||
|
||
public int fibBottomUp(int x) { | ||
if (x == 1 || x == 2) return 1; | ||
int[] memory = new int[x + 1]; | ||
memory[1] = 1; | ||
memory[2] = 1; | ||
for (int i = 3; i <= x; i++) memory[i] = memory[i - 1] + memory[i - 2]; | ||
return memory[x]; | ||
} | ||
|
||
public int fibStream(int n) { | ||
return Objects.requireNonNull(Stream.iterate(new Integer[]{0, 1}, s -> new Integer[]{s[1], s[0] + s[1]}) | ||
.limit(n) | ||
.reduce((x, y) -> y).orElse(null))[1]; | ||
} | ||
|
||
@Benchmark | ||
public void naiveRecursive() { | ||
fibNaiveRecursive(45); | ||
} | ||
|
||
@Benchmark | ||
public void tailRecursive(){ | ||
fibTailRecursive(45); | ||
} | ||
|
||
@Benchmark | ||
public void memoization() { | ||
fibMemoization(45, new int[45 + 1]); | ||
} | ||
|
||
@Benchmark | ||
public void bottomUp() { | ||
fibBottomUp(45); | ||
} | ||
|
||
@Benchmark | ||
public void stream() { | ||
fibStream(45); | ||
} | ||
|
||
public static void main(String[] args) throws RunnerException, IOException { | ||
Options opt = new OptionsBuilder() | ||
.include(FibonacciBenchmark.class.getSimpleName()) | ||
.warmupIterations(10) | ||
.measurementIterations(3).forks(1) | ||
.jvmArgs("-server", "-Xms2G", "-Xmx2G") | ||
.addProfiler(GCProfiler.class) | ||
.addProfiler(StackProfiler.class) | ||
.build(); | ||
new Runner(opt).run(); | ||
} | ||
} | ||
``` |