Skip to content

Commit

Permalink
ch 5 정리 (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
jemlog authored Jan 17, 2024
1 parent 8837916 commit 5072f16
Showing 1 changed file with 129 additions and 0 deletions.
129 changes: 129 additions & 0 deletions ch05/서제민.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# 5장 마이크로벤치마킹과 통계

**목표** : 테스트 진행 시 **Warm Up**을 진행하라는데, 실제 얼마나 영향을 미치는지 확인해보기!

### 벤치마킹 시 주의해야 하는 JVM Warm up


💡 **JVM Warm up이란?**

JVM은 코드를 1차적으로 바이트코드로 변환한 뒤, 기계어로 컴파일하는 2차 과정을 한번 더 거친다.
이때 기계어 컴파일을 실행하는게 **JIT Compiler**이다.

JIT Compiler는 총 5단계의 레벨을 가진다. 단계에 따라 내부적으로 동작하는 방식이 달라짐.
Level 0~3 까지는 **C1 Compiler**가 담당, Level4는 **C2 Compiler**가 담당

**코드의 복잡도**, **실행 빈도**에 따라 코드의 레벨이 결정된다. Level4의 코드는 C2 Compiler를 사용해서 **코드 캐시 영역에 캐싱**한다.



**JIT Compiler 구조**

<img width="500" alt="스크린샷 2024-01-10 오후 2 00 54" src="https://github.com/CMC11th-Melly/Melly_Server/assets/82302520/84c1df22-196d-47e1-86f0-da5ce436628d">

**자바 최적화 136 페이지 코드**

```java
public class MyBenchmark{

public static void main(String[] args) throws RunnerException{

Options opt = new OptionBuilder()
.include(SortBenchmark.class.getSimpleName())
.warmupIterations(100)
.measurementIterations(5).forks(1)
.jvmArgs("-server", "-Xms2048m", "-Xmx2048m").build(); // 해당 부분

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

**136p**의 main() 메서드 코드를 보면 jvmArgs의 인자로 `-server`를 주는걸 알 수 있다. java8 이전까지는 이 옵션을 줘야 C2 Compiler를 사용할 수 있었다. java8 이후로는 기본적으로 C2 Compiler를 사용하도록 설정되어 있음. ****

코드 캐시 사용 여부에 따라서 얼마나 성능 차이가 나는지 직접 스프링을 띄워서 확인해보자.

```java
@Service
@RequiredArgsConstructor
public class TestService {

public void test(){

List<Integer> testData = new ArrayList<>();
Random randomGenerator = new Random();

for(int i =0; i < 1_000; i++){
testData.add(randomGenerator.nextInt(Integer.MAX_VALUE));
}

/*
C2 컴파일러로 캐싱되려면 실행 빈도가 높아야하고, 극단적인 상황에서의 성능 차이를 보기 위해서
여러번 정렬하는 코드를 수행
*/
for(int i =0; i < 10_000; i++){
testData.stream().filter(test -> test % 2 == 0).sorted().toList();
}
}
}
```

- **Pinpoint**를 사용해서 메서드 실행 시간 측정
- jvmArgs로 `-XX:TieredStopAtLevel=N`을 설정하면 어느 레벨까지 사용할지 지정 가능하다

### -XX:TieredStopAtLevel=3 (컴파일 최적화는 하지만 코드 캐싱 안함) 테스트

---

**전체 응답 시간**

<img width="700" alt="스크린샷 2024-01-10 오후 2 00 54" src="https://github.com/CMC11th-Melly/Melly_Server/assets/82302520/f245028a-833d-4878-94f2-387d1a232aa3">


**메서드 실행 시간**

<img width="700" alt="스크린샷 2024-01-10 오후 2 00 54" src="https://github.com/CMC11th-Melly/Melly_Server/assets/82302520/45a0d245-187f-4e39-bafd-e16eb5c6b79b">

- 💡 코드 캐시를 미사용하는 경우 약 **1000ms**의 소요시간이 걸림. Level 3는 최적화된 컴파일을 하는 단계이기 때문에 더이상 시간 감축 불가능함.


### -XX:TieredStopAtLevel=4 (default) 테스트

---

**전체 응답 시간**
<img width="700" alt="스크린샷 2024-01-10 오후 2 00 54" src="https://github.com/CMC11th-Melly/Melly_Server/assets/82302520/a0ad2b9f-d524-4b22-a0df-c9cc768e2136">


**메서드 실행 시간**
<img width="700" alt="스크린샷 2024-01-10 오후 2 00 54" src="https://github.com/CMC11th-Melly/Melly_Server/assets/82302520/cd654f31-aee6-4942-8a97-d6fe7c70a9fa">



- 💡 코드 캐시를 사용하는 경우 약 **270ms**의 소요시간이 걸림. 코드 캐시 미사용시보다 약 **5배** 성능 개선


### C1 Compiler & C2 Compiler 장단점

---

C1 Compiler (TieredStopAtLevel=1 설정 시)

- 컴파일 시 최적화를 하지 않기 때문에 부팅 속도가 빠르다.
- 보통 모바일 클라이언트에서 사용. **빠르게 부팅되는게 중요하다**
- Intellij도 자세히 보면 어플리케이션 실행할때 TieredStopAtLevel=1로 동작. 설정에서 비활성화 가능
- 최적화 많이 하지 않기 때문에 **CPU 사용량 낮음**

C2 Compiler (default 옵션)

- 코드 캐시나 컴파일 최적화를 모두 진행하기 때문에 오래 동작하는 서버 어플리케이션에 적합
- 최적화를 많이 하기 때문에 **CPU 사용률 다소 높음**

### (추가) 스프링 첫 로딩 시 지연시간

---
<img width="700" alt="스크린샷 2024-01-10 오후 2 00 54" src="https://github.com/CMC11th-Melly/Melly_Server/assets/82302520/5c54e3ca-7885-49f4-b02c-c6edcdb88eea">

스프링 처음 띄운 후에 요청 보내보면 엄청 지연되는걸 경험했을 것이다.

스프링은 빠른 로딩을 위해서 어플리케이션이 부팅될때는 디스패처 서블릿을 초기화 하지는 않고, **첫 요청이 들어오면 초기화**를 하기 때문에 첫 요청의 응답속도가 느리다.

0 comments on commit 5072f16

Please sign in to comment.