자바 병렬 프로그래밍 summary - 11. 성능, 확장성

프로그래밍/병렬프로그래밍 2010. 4. 16. 16:08



 개인적인 스터디를 위해 위 책의 내용을 정리한 내용입니다. 자세한 내용을 확인하고 싶으신 분은 위 책을 구매하셔서 확인하시기 바랍니다. http://www.yes24.com/24/goods/3015162?CategoryNumber=001001003

 

11. 성능, 확장성

스레드를 사용하는 가장 큰 목적은 성능을 향상시키기 위한 것이다.

 

11.1 성능에 대해

성능을 높인다는 것은 더 적은 자원을 사용하면서 더 많은 일을 하도록 만드는 것이다.

여러 개의 스레드를 사용하려 한다면 항상 단일 스레드를 사용할 때보다 성능상의 비용을 지불해야만 한다.

- 스레드간의 작업 내용 조율(, 신호 보내기, 메모리 동기화)

- 컨텍스트 스위칭

- 스레드를 생성하거나 제거하는일

- 스레드의 효율적인 스케줄링

 

11.1.1 성능 대 확장성

애플리케이션 성능을 측정하는 데이터

- 서비스 시간, 대기 시간, 처리량, 효율성, 확장성, 용량

단일 티어 애플리케이션이 다중 티어 애플리케이션 보다 성능이 나을 가능성이 많다. 하지만 단일 티어 애플리케이션이 처리할 수 있는 부하를 넘어서는 작업량이 주어지면 문제가 심각해진다.

따라서 서버 애플리케이션을 만들 떄는 얼마나 빠르게 보다는 얼마나 많이 라는 측면을 훨씬 중요하게 생각하는 경우가 많다.

 

11.1.2 성능 트레이드 오프 측정

최적화 기법을 너무 이른 시점에 적용하지 말아야 한다. 일단 제대로 동작하게 만들고 난 다음에 빠르게 동작하도록 최적화해야 하며, 예상한 것보다 심각하게 성능이 떨어지는 경우에만 최적화 기법을 적용하는 것으로도 충분하다.

 

11.2 암달의 법칙

암달의 법칙 Amdahl’s law 을 사용하면 병렬 작업과 순차 작업의 비율에 따라 하드웨어 자원을 추가로 투입했을 때 이론적으로 속도가 얼마나 빨라질지에 대한 예측 값을 얻을 수 있다.

속도 증가량 <= 1 / ( F + (1-F) / N )

F : 순차적으로 실행돼야 하는 작업의 비율

N : 하드웨어에 꽂혀 있는 프로세서의 개수

 

하드웨어에 CPU 10개 꽂혀 있을 때, 10% 의 순차 작업을 갖고 있는 프로그램은 최고 5.3배 만큼의 속도가 증가 할 수 있다.

1 / ( 0.1 + ( 1 – 0.1) / 10) = 5.26….

 

암달의 법칙을 적용해보려면 애플리케이션 내부에서 순차적으로 처리해야 하는 적업이 얼마나 되는지 확인해야 한다.

 

11.2.1 예제 : 프레임웍 내부에 감춰져 있는 순차적 실행 구조

LinkedList 보다는 LinkedBlockingQueue 를 사용하는 것이 성능 향상에 좋다.

 

11.2.2 정성적인 암달의 법칙 적용 방법

 

11.3 스레드와 비용

 

11.3.1 컨텍스트 스위칭

컨텍스트 스위칭이란 스레드 스케줄링에 의해 다음 스레드가 실행되야 할 때 현재 스레드의 실행 상태를 보관해 두고, 다음 스레드의 실행 상태를 읽어들이는 것을 말한다.

 

유닉스 시스템의 vmstat 명령이나 윈도우 시스템의 perfmon 유틸리티를 사용하면 컨텍스트 스위칭이 일어난 횟수를 확인할 수 있드며 커널 수준에서 얼마만큼의 시간을 소모했는지 확인할 수 있다. 커널 활용도가 10%가 넘는 높은 값ㅇ르 갖고 있다면 스케줄링에 부하가 걸린다는 의미이며, 애플리케이션 내부에서 I/O 작업이나 락 관련 동기화 부분에서 대기 상태에 들어가는 부분이 원인일 가능성이 높다.

 

11.3.2 메모리 동기화

락 생략(lock elision)

public String getStoogeNames(){

             List<String> stooges = new Vector<String>();

             stooges.add("Moe");

             stooges.add("Larry");

             stooges.add("Curly");

             return stooges.toString();

       }

유출 분석(escape analysis)을 통해 4번이나 락을 잡았다 놓는것 대신 한번에 빠르게 실행된다.

IBM JVM, 자바 7 핫스팟 VM 에서 적용

잘 만들어진 컴파일러라면 getStoogeNames 메소드가 항상 같은 값을 리턴하는 것을 파악한 후 매 실행시마다 처음 실행결과를 리턴하도록 재 컴파일하기도 한다.

 

락 확장(lock coarsening)

연달아 붙어 잇는 여러 개의 synchronized 블록을 하나의 락으로 묶는 방법

 

11.3.3 블로킹

경쟁 조건이 발생하는 동기화 작업에는 운영체제가 관여하는데 해당 부분은 일정량의 자원을 소모한다. 락을 확보하지 못한 스레드는 대기상태에 들어가도록 하는데 JVM 은 두가지 방법을 사용한다.

- 스핀 대기(spin waiting) : 락을 확보할 때까지 계속해서 재시도하는 방법

- 운영체제가 제공하는 기능을 사용해 스레드를 실제 대기 상태로 두는 방법

대기 시간이 짧을 때는 스핀 대기가 효과적이고, 길 때는 운영체제의 기능을 호출하는게 효율적이다.

 

11.4 락 경쟁 줄이기

락으로 사용 제한이 걸려 있는 독점적인 자원을 사용하려는 모든 스레드는 해당 자원을 한 번에 하나의 스레드만 사용할 수 있기 때문에 순차적으로 처리될 수 밖에 없다.

(병렬 애플리케이션에서 확장성에 가장 큰 위협이 되는 존재는 바로 특정 자원을 독점적으로 사용하도록 제한하는 락이다.)

 

락 경쟁 조건을 줄일 수 있는 방법

- 락을 확보한 채로 유지되는 시간을 최대한 줄인다.

- 락을 확보하고자 요청하는 횟수를 최대한 줄인다.

- 독점적인 락 대신 병렬성을 크게 높여주는 여러 가지 조율 방법을 사용한다.

 

11.4.1 락 구역 좁히기

 

11.4.2 락 정밀도 높이기

다수의 락을 사용해 각 객체별로 필요한 만큼만 락을 확보하도록 하면 스레드 간의 경쟁을 크게 줄일 수 있다. 따라서 경쟁이 줄어들면 락을 확보하고자 대기하는 경우 역시 줄어들기 때문에 애플리케이션의 확장성이 늘어날 것이라고 예상할 수 있다. 하지만 그만큼 데드락에 빠질 위험도 높아진다.

 

11.4.3 락 스트라이핑 lock striping

락 스트라이핑 은 독립적인 객체를 여러 가지 크기의 단위로 묶어내고, 묶인 블록 단위로 락을 나누는 방법을 말한다. 예를 들어 ConcurrentHashMap 클래스는 16개의 락을 배열로 담은후 16개의 락 각자가 전체 해시 범위의 1/16에 대한 락을 담당한다. 따라서 락 경쟁이 발생할 확률을 1/16로 낮춰준다.

 

11.4.4 핫 필드 최소화

자주 계산하고 사용하는 값을 캐시에 저장해 두도록 최적화한다면 확장성을 떨어뜨릴 수 밖에 없는 핫 필드(hot fields) 가 나타난다.

HashMap 에서 락 스트라이핑을 사용해 락을 분할하여 확장성을 향상시킬 수 있지만 데이터를넣고 뺄 때마다 size 메소드에서 사용하는 값을 매번 변경해야 한다면 해당 값을 변경하느라 경쟁이 발생하게 된다. 이와 같은 size 변수를 핫 필드라고 한다.

ConcurrentHashMap 은 락으로 분할된 부분마다 카운트 변수를 두어 size 메소드 호출시에는 모든 카운트 변수를 더해 돌려준다.

 

11.4.5 독점적인 락을 최소화하는 다른 방법

 

11.4.6 CPU 활용도 모니터링

애플리케이션의 확장성을 테스트할 때 그 목적은 대부분 CPU를 최대한 활용하는 데 있다.

 

11.4.7 객체 풀링은 하지 말자

최근의 자바 프로그램은 메모리를 항당하는 작업이 C 언어의 malloc 함수보다 빨라졌다.

병렬 애플리캐이션에서 객체 풀링을 사용했을 경우에는 해당 풀에서 객체를 얻기 위한 경쟁이 발생하게 된다. 또 스레드가 대기상태에 들어가는 비용보다는 새로운 객체를 생성하는게 성능상 좋다.

 

11.5 예제 : Map 객체의 성능 분석

 

11.6 컨텍스트 스위치 부하 줄이기

: