자바 성능 최적화 3

     

     

    3.2 메모리 

    • CPU의 클록 속도는 증가함.
    • CPU 클록 속도만큼 데이터를 빠르게 가져오지 못함. 메모리 성능이 좋지 못함.
    • 메모리 성능 문제를 극복하기 위해 CPU에 캐시 메모리가 추가됨. 
    • 멀티 코어에서는 캐시 메모리의 일관성을 잘 처리하기 위해 Cache Consistencty Protocol을 함. 
      • MESI 프로토콜
      • 멀티 프로세서가 동시에 메모리를 Hit 하고 변경하는 경우라면, 다른 프로세서 관점에서 캐시는 invalid(무효)가 되어서 다시 처리됨. 
    • touchEveryItem() / touchEveryLine()은 i++ vs i += 16이기 때문에 16배의 일을 많이 할 것이라고 생각하지만, 실제 소요 시간은 거의 동일함. 
      • 캐싱 환경이기 때문에 이렇게 동작함. 이미 캐시에 모두 로딩되어 있기 때문임.
      • 하지만 실제로 코드는 16번 실행되었을 듯?

     

    3.3 최신 프로세서의 특성

    • TLB : 가상 메모리 주소를 물리 메모리 주소로 매핑하는 페이지 테이블의 캐시 역할을 수행함. 이 녀석이 있어야 빈번한 작업 속도가 매우 빨라짐. 
    • 분기 예측과 추측 실행 : If 결과가 나올 때 까지 이전에는 CPU가 대기했었는데, 지금은 대략적으로 이렇게 동작할 것이다 추측하는 휴리스틱한 방법으로 처리함. 

     

    3.4 운영체제 

    • 스케쥴러 
      • 일반적으로 코어는 라운드 로빈 방식으로 점유함. 실행 큐 안에 들어가서 가장 앞에 있는 경우에 CPU를 점유할 수 있음. 
      • OS는 어플리케이션에게 CPU 사용 시간을 분배한다. 따라서 어플리케이션은 코드가 실행되는 시간보다 기다리는 시간이 더 많아질 수 있음.
      • OS마다 이런 오버행 타임은 서로 다를 수 있음. 측정 방법은 다음과 같다.
      • 1밀리초씩 총 1,000회 스레드를 재운다. → 이 때 추가된 시간을 보면 됨. 
    • 시간 문제
      • 리눅스는 Unix 시간을 사용함. 
      • 윈도우는 1601년 이후 경과한 시간을 100나노초 단위로 기록함. 
      • 따라서 OS마다 사용되는 시간이 다를 수 있음. 
    • 컨텍스트 교환
      • 유저 쓰레드가 커널 모드로 바꿔서 어떤 기능을 실행해야하는 경우, 큰 컨텍스트 스위칭이 발생함.
      • 유저 공간에 있는 코드가 액세스 하는 메모리 영역 / 커널 영역의 메모리 영역은 공유할 부분이 없기 때문에 모드가 바뀌면 명령어와 다른 캐시를 어쩔 수 없이 강제로 비워야 함. 
      • 이런 부분을 최소화 하기 위해 리눅스는 VDOS라는 것을 추가 함.
        • 리눅스 커널 특권이 필요 없는 시스템 콜의 경우, 굳이 커널 영역의 메모리를 사용할 필요가 없음.
        • vDSO는 유저 공간의 메모리 영역임. 이 경우 시스템콜에서 커널로 컨텍스트 스위칭을 하지 않기 때문에 비용이 줄어듬. 

     

    3.6 기본 감지 전략

    • 어플리케이션이 잘 돌아간다는 건 CPU 사용량 / 메모리 / 네트워크 / IO 대역폭 등 시스템 리소스를 효율적으로 잘 이용하고 있다는 뜻임. 
    • CPU 사용률
      • vmstat, iostat을 이용해서 OS가 Raw하게 제공하는 정보를 살펴볼 필요가 있음. 
      • CPU 사용률이 100%에 근접하지 않았다면 왜 그럴까?를 따져야 함. 
        • 컨텍스트 교환 때문인가? I/O 경합이 일어나 블로킹이 발생했나? 
        • 유저 공간에서 CPU 사용률이 100% 사용하지 않은 채로, 컨텍스트 스위칭 비율이 높게 나타나면 I/O 블로킹 또는 쓰레드 락 경합 상황이 벌어졌을 가능성이 있음. 
    • 가비지 수집
      • 핫스팟 JVM은 시작 시 메모리를 유저 공간에 할당 / 관리함. 따라서 메모리를 할당하는데 필요한 시스템 콜이 필요없음. 따라서 가비지 수집을 위해 커널 스위칭을 할 필요가 없음. 
      • 따라서 GC 자체는 유저 공간의 CPU 사이클을 소비함. (커널 공간의 CPU 사용률은 영향 없음) 
      • 만약 JVM 프로세스가 유저 공간에서 CPU를 100%에 가깝게 사용하고 있다면 GC를 의심해야 함. 
        • 성능 분석 시, 단순 툴에서 CPU 사용률이 100% 일정하지만 모든 사이클이 유저 공간에서 소비되고 있으면, CPU를 차지하는 것이 1. 유저 코드 ? 2. GC 인지 찾아봐야 함. 
        • 이 분석을 위해서 GC 로그를 넣어줘야 함. 

     

    3.8 JVM과 운영체제 

    • JVM은 자바 코드에 공용 인터페이스를 제공해서 OS에 독립적인 실행 환경을 제공한다. 하지만 이를 위해서 스레드 스케쥴링 같은 기본적인 서비스 조차도 하부 OS에 반드시 액세스 해야함. 
      • 이런 기능들은 native 키워드를 붙인 네이티브 메서드로 구현함. 
      • native 메서드는 C 언어로 작성하지만, 자바 메서드처럼 액세스 할 수 있음. 
      • 이 작업을 대행하는 공통 인터페이스를 JNI (Java Native Interface)라고 함. 
    • 예를 들어 System.currentTimeMillis()라는 메서드는 OS의 시간을 불러오는 작업이다. 이 작업은 다음과 같은 방식으로 실행됨
      • Java : System.currntTimeMillis() (첫번째 호출)
      • C : JVM_CurrentTimeMillis() (두번째로 호출됨)
      • OS : OS::javaTimeMillis()  (세번째로 호출됨)
      • 플랫폼 : 플랫폼 특정 코드

     

     

     

     

     

     

     

    'etc > 리팩토링' 카테고리의 다른 글

    리팩토링 43. Assertions 추가하기  (1) 2023.05.10
    냄새 24. 주석  (0) 2023.05.10
    냄새 23. 상속포기  (0) 2023.05.10
    리팩토링 42. 레코드 캡슐화 하기  (0) 2023.05.10
    냄새 22. 데이터 클래스  (0) 2023.05.10

    댓글

    Designed by JB FACTORY