본문 바로가기
About Computer/서버관리

[번역] Memory allocation mechanisms in AIX / AIX: Throughput problems when malloc is called often

by leanu 2015. 9. 25.

Memory allocation mechanisms in AIX


AIX는 기본적으로 Yorktown 기법으로 memory allocation 이 이루어지며, Watson이나 Malloc 3.1 방법으로도 변경이 가능하다.
각각의 Memory allocation사용방법은 다음과 같다.

  • export MALLOCTYPE=Yorktown
  • export MALLOCTYPE=Watson
  • export MALLOCTYPE=3.1
  • export MALLOCTYPE=3.1_64BIT

Yorktown

Yorktown 은 메모리를 Cartesian binary search tree로 힙 노드들을 관리한다. 

사용시기
  • 대부분의 케이스에서 널리 사용되는 안정적인 방법.
  • 다양한 size 가 기입된 tree로 관리되기 때문에 block size 에 제약이 없음

Yorktown Allocation algorithm
  • free tree에 있는 주소값이 낮은 node(크기는 요청한 사이즈와 같거나 크다)가 삭제 된다. 만약 요청크기보다 큰 경우 두 조각으로 나뉘어 반환된다.

Yorktown free algorithm
  • free tree에 해당 free node를 삽입한다. 삽입시 표준 root insertion algorithm을 따른다. 만약 인접한 메모리영역이 발견된 경우 하나의 노드로 합쳐진다. 이부분은 메모리 파편화를 예방한다.

Yorktown reallocation algorithm
  • 재할당 사이즈가 기존보다 큰 경우, 해당 node는 free tree로 반환되며, 요청된 새로운 크기의 노드로 할당된다. 할당된 새로운 노드에 이전 노드의 데이터들이 옮겨져 반환된다. 이전 블록은 해지되며 더이상 사용가능하지 않다. 만약 재할당 사이즈가 기존보다 작은 경우 노드는 두 조각으로 나뉘며 남는 조각은 free tree로 반환된다.


Watson

Watson 할당 정책은 두개의 분리된 red-black tree(address로 정렬 / size로 정렬)로 관리한다.

사용시기
  • Watson은 red-black tree를 사용하므로, 삽입이나 탐색이 다른 정책에 비해 효과적이다.

Watson Allocation algorithm
  • Yorktown과 비슷하게 요청한 크기와 비슷한 가장 작은 블럭을 탐색한다. 요청한 크기와 일치하는 블럭을 찾은경우 size, address tree에서 제거되며 반환된다. 만약 요청한 사이즈가 발견되지 않으면, sbrk() system call을 호출하여 힙을 확장하며 확장된 블록이 size와 address tree로 삽입된다. 그리고 이전단계의 탐색을 반복한다.

Watson free algorithm
  •  해지할 노드는 먼저 adress tree의 루트에 리턴되며 인접한 노드가 있는지 찾는다. 인접한 노드가 있는경우 하나로 합쳐진다. 

Watson reallocation algorithm
  • 재할당 블럭 크기가 기존보다 큰 경우, 기존 노드는 free tree로 반환되어 인접한 노드가 있는경우 합쳐진다. 요청한 사이즈의 새로운 블럭이 할당되며, 데이터가 기존노드로부터 이동되어 반환된다. 만약 재할당 블럭 크기가 기존보다 작은 경우 블록이 나뉘며 남은 블럭은 free tree로 반환된다. 


malloc 3.1

malloc 3.1에서는 hash bucket으로 관리되며 각각의 bucket포인트에서는 연결리스트로 특정 사이즈의 노드나 블록이 포함된다. 특정 bucket에는 특정 사이즈의 블록이 있다. ( size = 2 ^ (i + 4) ) 

malloc 3.1 allocation algorithm
  • 요청한 사이즈를 보고 bucket 번호가 결정된다. 해당 bucket 번호에 노드들이 없으면 sbrk()를 이용하여 새로운 블록을 집어넣는다. 만약 free list에 노드가 있는 경우 해당 노드가 반환되며 그 다음 노드가 bucket의 list head가 된다.

malloc 3.1 free allocation
  • 메모리 블록이 free pool 로 넘겨질때, 기존에 할당되었던 방법으로 bucket index를 계산하여 해당 버켓의 free list 의 head에 삽입한다.

malloc 3.1 reallocation algorithm
  • 재할당시 재할당 사이즈가 기존보다 커지더라도 해당 블록안에 들어갈 수 있는지 판단한다. 해당 블록 안에서 충족할 수 있는 경우 그 블록이 다시 리턴되며, 큰 사이즈의 블록이 필요한 경우 해당 블록은 해지 되며 새로운 버켓에서 생성한 블록이 데이터 이관 이후 리턴된다.

malloc 3.1 limitations
  • malloc 3.1 은 기본 정책인 Yorktown 보다 효율적이지 못하며 대부분의 상황에서 권장하지 않는다. 고정된 블록사이즈로 인하여 매 블록마다 우리가 원하는 사이즈보다 더 많은 메모리를 할당해야 한다. 
  • 때때로 특정 어플리케이션에서는 malloc 3.1 할당 정책에 따른 사이드 이펙트에 영향을 받기도 한다. 예를들어 특정 요청 사이즈보다 더 쓰는 경우 malloc 3.1에서는 정상적으로 동작하던 코드가(물론 메모리를 잘못 사용하는 것이지만) 다른 할당 정책에서는 실패한다.
  • reallocation 시 데이터 이동을 최소화 하기위해 malloc 3.1을 사용하기도 한다.


Malloc 부가 옵션들

부가 옵션들은 다음과 같이 사용하면 된다.

Malloc multiheap
  • 기본적으로 malloc 은 전체 프로세스 heap을 하나의 속성으로 간주한다. multi thread 환경에서는 각 멀티 스레드가 같은 heap 을 접근하게 된다. 이 경우 single heap은 thread lock 으로 인하여 매우 비효율적이다. 이런 케이스의 경우 malloc multiheap 옵션이 유용하다.
  • export MALLOCOPTIONS=[multiheap:n] | [considersize]
    • multiheap:n : n에는 1 ~ 32의 숫자를 사용할 수 있다. 사용할 힙의 개수를 정의한다.
    • considersize : 기본적으로 multiheap은 사용가능한 다음 heap을 선택하는데, considersize가 활성화된 경우 해당 요청을 처리할만큼 충분한 여유공간이 있는 heap을 선정한다.
    • ex) export MALLOCOPTIONS=multiheap:3,considersize
  • limitations
    • 여러개의 heap을 사용하므로 메모리 파편화를 야기하며, 메모리 부족 현상이 발생할 수도 있다.
    • considersize는 부가적인 처리시간을 필요로 하기 때문에 특정 상황에서는 느려질 수 있다.
    • multiheap옵션은 malloc 3.1 이나 사용자가 정의한 할당 정책에서는 사용할 수 없다.

Pool allocation
  • 메모리 크기가 512 byte 와 같거나 작은 경우 AIX는 효율적으로 malloc의 frontend 와 backend를 제어하는 기능을 제공한다. Pool allocation은 각 스레드마다 고유의 malloc pool을 생성하여 스레드들 간의 malloc locking 현상을 방지한다. 현재 한 프로세스당 설정할 수 있는pool 의 최대 크기는 512MB이다.
  • Pool allocation은 한 쓰레드에서 메모리를 할당하고 다른 스레드들이 해지하는 경우 효과적이지 않다.
  • Pool 개수는 max_size에 따라 결정된다. 32bit에서는 128개의 풀을 가질 수 있는 하나의 머신에서는 64bit에서 64개의 풀을 가질 수 있다.
  • export MALLOCOPTIONS=pool<:max_size>

Malloc buckets
  • Malloc bucket은 Yorktown 할당 정책의 확장 기능이다. 만약 작은 메모리 할당 요청이 굉장히 많은 어플리케이션의 경우 malloc bucket이 더 일반적으로 사용된다. Yorktown 모드에서만 동작한다.
  • 512 byte 이하의 요청에 대해서만 bucket allocator에 의해 서비스되며, 더 큰 경우 Yorktown malloc 으로 넘긴다.
  • export MALLOCOPTIONS=buckets,number_of_buckets:n





AIX: Throughput problems when malloc is called often

최근 고객 사이트에서 MALLOCOPTION=multiheap  환경변수를 재설정하는것 만으로 50% 성능 향상을 이뤄냈다. 무겁고, 동시다발적으로 일어나는 malloc 상황에서만 적용가능하겠지만 대부분의 WAS/Java 를 보면 특별한 케이스가 아니다. 
multiheap 옵션은 가상/물리적 메모리를 증가시키는 만큼 비용이 있다. 각 힙의 free tree가 독립적이기 때문에 파편화가 더 자주 발생한다. 아래에서 오버헤드에 대한 추가적인 설명을 볼 수 있다.

malloc 은 이따금 어플리케이션 성능(특히 AIX에서)의 병목이 된다.  기본적으로 AIX는 하나의 힙을 사용하기 때문에 멀티 쓰레드 어플리케이션에서 malloc lock 상황이 자주 발생한다. multiheap 옵션을 사용함으로서 allocator가 여러 힙들을 병렬로 사용할 수 있다. 

이 옵션이 영향을 미치는지 어떻게 알 수 있는가? 쉽지만은 않다.

pthread library나 kernel locking, routine에서의 실행시간의 집중현상은 locking 이슈와 관련이 있다. locking 현상은 결국 시스템 레벨이나(AIX 의 malloc locking issue같은) Java code의 어플리케이션 레벨(Java code 내 synchronized block이나 method)로 올라온다. Locking 이슈의 근원은 프로파일을 보는것 만으로는 즉시 나타나지 않는다. 예를들어, AIX malloc loking 이슈의 경우 malloc과 free routine에 의해 사용된 시간은 커널 locking 루틴에서의 시간 소비가 대부분인 경우 ​매우 적을 수 있다. 

그럼에도 불구하고 아래는 이 문제를 보여주는 tprof의 한 예이다. 실행은 아래와 같이 하였다.
  • tprof -ujeskzl -A -I -X -E -r report -x sleep 60

Process                          FREQ  Total Kernel   User Shared  Other   Java
=======                          ====  ===== ======   ==== ======  =====   ====
/usr/java5/jre/bin/java           174  22557  11850      0   7473     86   3148

Shared Object                                  Ticks    %    Address  Bytes
=============                                  ===== ======  =======  =====
/usr/lib/libc.a[shr_64.o]                       3037   9.93 900000000000d00 331774
/usr/lib/libpthread.a[shr_xpg5_64.o]            1894   6.19 9000000007fe200  319a8

  Total Ticks For All Processes (KERNEL) = 15045
Subroutine                 Ticks    %   Source                Address  Bytes
==========                 ===== ====== ======                =======  =====
._check_lock                2103   6.88 low.s                    3420     40

  Total Ticks For All Processes (/usr/lib/libc.a[shr_64.o]) = 3037
Subroutine                 Ticks    %   Source                Address  Bytes
==========                 ===== ====== ======                =======  =====
.malloc_y                    856   2.80 ../../../../../../../src/bos/usr/ccs/lib/libc/malloc_y.c    41420    840
.free_y                      669   2.19 ../../../../../../../src/bos/usr/ccs/lib/libc/malloc_y.c    3f980    9a0

  Total Ticks For All Processes (/usr/lib/libpthread.a[shr_xpg5_64.o]) = 1894

Subroutine                 Ticks    %   Source                Address  Bytes
==========                 ===== ====== ======                =======  =====
.global_unlock_ppc_mp        634   2.07 pth_locks_ppc_mp.s      2d714     6c
._global_lock_common         552   1.81 ../../../../../../../../src/bos/usr/ccs/lib/libpthreads/pth_spinlock.c     2180    5e0
.global_lock_ppc_mp_eh       321   1.05 pth_locks_ppc_mp_eh.s    2d694     6c

위의 상황에서 키포인트는 다음과 같다.
  • Process 섹션에서 Kernel 시간이 매우 높다 (Total 시간의 거의 반).  
  • Shared Object 에서 libc와 libpthread 수치가 높다.
  • KERNEL 색션에서 ._check_lock 수치가  높다.
  • libc.a 섹션에서 .malloc_y와 .free_y 수치가 높다.
  • libpthread.a 섹션에서 .global_unlock_ppc_mp와 다른 비슷한 이름의 함수들의 수치가 높다.

AIX에서는 유용한 다른 Allocator 정책과 옵션을 제공한다.

Pool malloc
  • malloc system 의 frontend 격인 pool은 512byte나 그 이하의 메모리 블록 할당을 최적화한다.
  • 일반적으로 애플리케이션은 작은 메모리 블록들을 많이 할당하며, pool을 사용하게 되면 할당 패턴에 비추어 보았을때 일반적으로 시공간적 효율성이 높다.
  • pool malloc 방식은 단일/멀티 스레드 모두 좋은 선택이 될 수 있다. pool 과 multi heap을 조합하는 경우 멀티 스레드 애플리케이션의 충분한 대체제로 사용할 수 있다. 굉장히 일반적인 작은 메모리 블록 할당은 pool을 통해서 굉장히 효율적으로 관리된다. 더 큰 할당들은 multiheap malloc으로 확장성이 용이하게 관리된다. 아래의 설정으로 두 옵션을 복합적으로 사용할 수 있다.
    • export MALLOCOPTIONS=pool,multiheap

Buckets
  • 이 세부 옵션은 Watson allocator의 버킷 allocator 버전이라 할 수 있다. 그러나 이 옵션으로 bucket 수(버킷당 블럭수, 각 버킷 사이즈)를 조절할 수 있다. 이 옵션은 또한 각 버킷의 사용 통계를 볼 수 있는 방법을 제시하는데, 버킷 설정을 최적화 하는데 도움이 된다. 애필르케이션에서 특정 사이즈의 메모리할당 요청이 많이 발생한 경우 bucket allocator는 bucket옵션을 정확하게 기술함으로서 미리 요구한 크기를 할당하도록 설정할 수 있다. Watson 이나 malloc pool 옵션과 비교되는 점은, 512 바이트 미만이어야 한다. 

결론

  • 32bit 단일 스래드 어플리케이션의 경우 기본 할당자를 사용한다.
  • 64bit 어플리케이션의 경우 Watson allocator를 사용한다.
  • 멀티 쓰레드 어플리케이션의 경우 multi-heap을 사용한다. 힙 개수는 사용한 스레드 개수만큼 설정한다.
  • 513 바이트 미만의 작은 블럭에 대한 잦은 할당/해제가 발생하는 프로그램의 경우 malloc pool 옵션을 사용한다.
  • 특정 크기의 메모리 블록이 빈번하게 사용되고, 그 크기가 512바이트보다 크다면 malloc bucket 옵션을 설정한다.
  • 고성능을 요하며 메모리 파편화 이슈가 없는 오래된 어플리케이션의 경우 malloc 3.1을 사용한다.
  • 이상적으로 multiheap과 malloc pool option을 사용한 Watson allocator가 대부분의 multi-thread 어플리케이션에 좋다. front end에서 사용되는 pool 은 빠르며, multiheap은 크거나 작은 할당에 대한 확장성이 용이하게 한다. 
  • free이후에도 특정 어플리케이션의 메모리 사용량이 높은경우 disclaim 옵션이 도움이 된다. (export MALLOCDISCLAME=true)




참조



번역에 오류나 수정사항이 필요한 경우 덧글로 요청 바랍니다.


댓글