Chapter 1. 성능과 최적화
1.1 자바 성능: 잘못된 방법
- 자바 초창기 메서드 디스패치 성능은 최악이었다
- 메서드를 잘게 나누지 말고 하나의 큰 메서드로 작성하는 것이 좋다고 권고하는 개발자도 있었음
- 시간이 지나면서 디스패치 성능이 좋아졌기에 ‘모든 코드를 한 메서드에 넣기’는 현대 JIT 컴파일러와는 어울리지 않는다
자바 코드가 실행되는 속도는 변화무쌍하며 하부 JVM에 따라 다르다.
- 이 책에서는 성능 목표 달성을 위한 여러 단면을 종합적으로 집중 조명하고자 한다
- 전체 소프트웨어 수명 주기의 성능 방법론
- 성능과 연관된 테스트 이론
- 측정, 통계, 툴량 (도구 선정)
- (시스템 + 데이터) 분석 스킬
- 하부 기술과 메커니즘 (장치, 수단)
- 일반적인 원칙
- JVM을 더 빨리 작동시키는 ‘마법의 스위치’는 없다
- 자바를 더 빨리 실행하기 만드는 ‘팁, 트릭’은 없다
- ‘비밀 알고리즘’ 같은 것도 없다
1.2 자바 성능 개요
자바는 블루 칼라(육체 노동자) 언어입니다. 박사 학위 논문 주제가 아니라 일을 하려고 만든 언어죠. -제임스 고슬링 (자바 창시자)-
- 자바는 실용적인 언어
- 개발자 생산성을 대가로 어느 정도의 성능 희생을 감수
- 그러나 최근 핫스팟과 같은 정교한 JVM 덕에 고성능 컴퓨팅 애플리케이션에 적합
- 실용성을 추구하는 대표적인 예로 서브시스템이 있다.
- GC처럼 개발자가 수동으로 메모리를 관리하지 않아도 됨
- 저수준으로 제어 가능한 일부 기능을 포기하자는 발상
- JVM 전반에 걸쳐 서브시스템은 그 존재로 JVM 런타임 동작에 복잡도를 유발
- JVM 애플리케이션 성능 측정값은 정규 분포를 따르지 않는 경우가 많아 통계 기법(표준편차, 분산)만 갖고는 측정 결과를 처리하기 역부족
- 런타임 동작이 복잡하기에
- 측정하는 행위 자체도 오버헤드를 일으킴
- 너무 자주 샘플링하거나 결과를 기록하는 것 역시 성능 결과 수치에 영향을 줌
1.3 성능은 실험과학이다
- JVM 성능 튜닝은 기술, 방법론, 정량적 측정값, 툴을 망라한 개념
- 다음과 같은 활동을 하며 원하는 결과를 얻기 위한 일종의 실험과학
- 원하는 결과를 정의
- 기존 시스템 측정
- 요건을 충족시키기 위한 작업 결정
- 개선 활동 추진
- 다시 테스트
- 목표 달성 판단
- 성능 분석은 통계치에 근거해 적절히 결과를 처리하는 활동
1.4 성능 분류
- 대다수 성능 프로젝트에서 모든 지표가 동시에 최적화되지는 않는다.
- 많아야 2~3가지가 개선되지만 하나를 개선하면 하나가 악화되는 경우도 흔하다.
1.4.1 처리율 (throughput)
- (서브)시스템이 수행 가능한 작업 비율을 나타낸 지표
- 보통 일정 시간 동안 완료한 작업 단위 수로 표시
- 처리율이 의미 있는 지표가 되려면 수치를 얻은 기준 플랫폼에 대해서 기술해야 한다.
- ex) 하드웨어 스펙, OS, 소프트웨어 스택, 단일 서버인지 클러스터 환경인지 등
- 트랜잭션(또는 작업 단위)은 테스트마다 동일해야 한다
- 실행 간 워크로드 역시 일정하게 유지해야 한다.
- 워크로드: 시스템이 주어진 시간 내에 처리해야 할 작업 할당량
1.4.2 지연 (latency)
- 하나의 데이터를 한 지점에서 다른 지점으로 보내는데 소요되는 시간
- 일부에선 데이터를 보내고 그것이 송신자에게 되돌아올 때까지의 왕복시간을 말하기도 한다.
- 종단 시간이라고도 하며 대개 워크로드에 비례하는 함수로 표시된다.
- 1초에 100리터를 흘려보내는 수도관의 처리율이 1초에 처리되는 부피(100리터)라면 지연은 수도관 자체의 길이에 해당
1.4.3 용량 (capacity)
- 시스템이 보유한 작업 병렬성의 총량
- 즉 시스템이 동시 처리 가능한 작업 단위(트랜잭션) 개수
- 용량은 처리율과 밀접한 관련이 있다.
- 시스템 동시 부하가 증가할수록 처리율(그리고 지연)도 영향을 받기 때문
- 때문에 보통 용량은 어떤 처리율 또는 지연 값을 전제로 가능한 처리량으로 표시한다
1.4.4 사용률 (utilization)
- 성능 분석 업무 중 가장 흔한 태스크는 시스템 리소스를 효과적으로 활용하는 것
- CPU를 놀게 하는 것 보다 실제 작업을 하도록 해야 함
- 사용률은 워크로드에 따라 리소스별로 들쑥날쑥할 수 있다
- ex) 계산 집약적 워크로드를 주면 CPU 사용률은 100%에 육박하지만 메모리 사용률은 적다.
1.4.5 효율 (efficiency)
- 전체 시스템의 효율은 처리율을 리소스 사용률로 나눈 값으로 측정
- 직관적으로는 같은 처리율인데 더 많은 리소스를 사용했다면 효율이 낮은 것
- 대형 시스템에서는 원가 회계 형태로 효율을 측정하는 방법도 있다.
- 리소스 사용률이 아닌 총소유비용(total cost of ewnership, TCO)를 사용
1.4.6 확장성 (scalability)
- 리소스 추가에 따른 처리율 변화는 시스템/애플리케이션의 확장성을 가늠하는 척도
- 시스템 확장성은 궁극적으로 투입한 리소스만큼 처리율이 변경되는 형태를 지향
- 클러스터 기반 서버에서 클러스터를 2배 늘리자 처리율도 2배 늘었다면 완벽한 선형 확장을 달성한 것
- 그러나 현실적으로 선형 확장은 매우 어렵다
- 시스템 확장은 하나가 아닌 여러 인자들의 영향을 받기에 대부분 부하가 높아지면 완벽한 확장을 저해하는 한계에 봉착한다
1.4.7 저하 (degradation)
- 시스템이 더 많은 부하를 받을 때 지연, 처리율 측정값에 변화가 생긴다.
- 그 변화는 사용률에 따라 상이하다.
- 사용률이 낮으면 측정값이 느슨하게 변한다.
- 사용률이 높으면 처리율이 더는 늘어나지 않는, 즉 지연이 증가한다. → 부하 증가에 따른 저하 발생
1.5 성능 그래프 읽기
성능 테스트에서 자주 등장하는 패턴을 소개
성능 엘보
암달의 법칙
‘암달의 법칙(Amdahl’s law)’은 컴퓨터 프로그램은 프로세서를 아무리 병렬화 시켜도 병렬처리가 가능한 부분(전체 처리량의 약 5%)과 불가능한(순차 처리) 부분으로 구성되므로 더 이상 성능이 향상되지 않는 한계가 존재한다는 법칙이다.
- 하부 테스크를 세 가지 다른 비율로 병렬화한 결과를 나타낸 그래프
- 워크로드에 반드시 순차 실행되어야 할 작업 조각이 하나라도 있으면 선형 확장은 처음부터 불가능
- 순차 비율이 5%인 알고리즘도 12배 시간을 단축하려면 프로세서가 32개나 필요
- JVM GC 메모리 사용 패턴은 부하가 별로 없는 건강한 애플리케이션도 아래처럼 ‘톱니’모양을 나타낸다.