부끄럼없이 다뤄보기로 하겠다.. 물론 Transparent Huge Pages 를 먼저 다뤄야 좋겠지만,
Huge Zero Page 컨셉에 대해서 먼저 간략하게 알아볼 것이다.
말그대로 Zero Page 는 Zero (Not null) 로 채워진 Page 를 나타내는데 이걸 특별히
크게 매핑하여 할당하는 것을 말한다.
Transparent Huge Pages 기능은 커다란 사이즈의 페이지를 응용프로그램이 요구할 때,
관리자의 특정한 설정 없이 가장 활성화되있는 프로세서로 부터 큰 사이즈의 페이지를
응용프로그램이 조금 더 쉽게 접근하는걸 허용하기 위한 기능인데,
이 기능은 사실상 거의, 퍼포먼스를 향상시기키 위한 기능이며,
시스템의 TLB 에대한 부담을 줄여주고 빠른 메모리 엑세싱을 가능하게 해준다.
(/proc/meminfo 의 HugeTLB 란이 바로 이것에 관련되어 있다. )
이 기능은 게다가, 메모리 비트를 절약 할 수 있고,
페이지 테이블에서의 완벽한 제거 (해제 )또한 손쉽게 가능하게 해준다.
하지만, 실제로 이 기능을 사용하는 경우 어떤 경우엔 오히려 응용프로그램의 메모리 사용량을
너무 크게 증가시켜버릴 수 있다는 단점이 있었고, 다행히도 이 것에 대한 솔루션이 매우 간단하게,
제시되었는데, 그것이 바로 Zero 로 초기화된 페이지를 사용하는 것!!
THP (Transparent Huge Pages) 는 주로 anonymous - Disk 에 의해 기록되지않는 - Page 를
사용하는데, Anonymous 메모리영역은 생성 또는 확장 될 때, 실재하지 않는 메모리 페이지를 할당하고,
이 과정에서 커널의 전통적인 메모리 할당절차 ( 찾고, 정리하고 따위 등등) 에
많은 시간을 할애하게 될 뿐만 아니라 ( Huge Page 니까 ),
Anonymous 의 특성때문에 메모리가 실재 할당되기 전에, Page fault 가 발생될 수 있다는 점..
(Anonymous 는 실재하지 않는 메모리페이지주소를 사용한다고 했잖아.. ㅡ.,ㅡ )
그래서 새로운 Anonymous page 를 무조건 Zero 로 채우는 방법이 고안되었던 것.
어찌보면 무식한거 아닌가 생각이 드는 이 방법은,
기존에 혹시 다른 user 나 이전 user 에 의해 할당되려는 페이지에 데이타가 남아있다고
하더라도 그 데이타가 노출될 위험또한없애는 효과도 가져온다.
또한, 프로그램들은 자주 사용하려는 메모리에 대한 초기화 과정이 수반되는데,
이미 zero 로 채워져있다는 것을 프로그램에서 미리 알 수 있다면 초기화 과정을 생략할 수 있다는,
또한, 해당 페이지영역을 쓸대없이 많이 기록 할 필요없이, 프로세스가 살아있는 동안은 계속
zero 로 채워진 페이지를 항상 가지고 있을 수 있다는것...
( 제로페이지를 Share 함으로 메모리에 대한 Access 회수를 줄일수 있기에. )
그런데 이 zero page 역시 다른 pages 처럼, 작은 값으로 너무 많이 생성이 되었고,
그것으로 부터 읽기 위해 새로 생성된 인스턴스가 수행되는 과정에서, 여전히 커널은
새로운 메모리 Page 할당을 위해 시간이 필요했다는 것..
이 과정에 대해서 Kirill Shutemov 가 간단한 메모리 할당 코드를 통해 보여주었고,
< 특별첨부 : Kirill 이 문제확인을 위해 사용한 코드와 Mem Mapping 결과.>
[mirr@Mirr-N MM]$ cat zero_page_lacking.c
/************************************************/
/* Observe to Zero Page Lacking */
/* coded by Mirr from Kirill's mail */
/* */
/* gcc -o zero_page_lacking zero_page_lacking.c */
/* */
/************************************************/
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#define MB 1024*1024
int main(int argc, char **argv)
{
char *p;
int i;
posix_memalign((void **)&p, 2 * MB, 200 * MB);
for (i=0; i<200; i+=4096)
assert(p[i] == 0);
pause();
return 0;
}
[mirr@Mirr-N MM]$ gcc -o zero_page_lacking zero_page_lacking.c
[mirr@Mirr-N MM]$ ./zero_page_lacking &
[1] 29619
[mirr@Mirr-N MM]$ pmap 29619 -x
29619: ./zero_page_lacking
Address Kbytes RSS Dirty Mode Mapping
0000000000400000 4 4 0 r-x-- zero_page_lacking
0000000000600000 4 4 4 rw--- zero_page_lacking
0000003c14400000 128 108 0 r-x-- ld-2.15.so
0000003c1461f000 4 4 4 r---- ld-2.15.so
0000003c14620000 4 4 4 rw--- ld-2.15.so
0000003c14621000 4 4 4 rw--- [ anon ]
0000003c14c00000 1712 208 0 r-x-- libc-2.15.so
0000003c14dac000 2048 0 0 ----- libc-2.15.so
0000003c14fac000 16 12 8 r---- libc-2.15.so
0000003c14fb0000 8 8 8 rw--- libc-2.15.so
0000003c14fb2000 20 12 12 rw--- [ anon ]
00007f58bd600000 206864 20 20 rw--- [ anon ]
00007f58ca035000 4 4 4 rw--- [ anon ]
00007ffff3e65000 136 12 12 rw--- [ stack ]
00007ffff3f75000 4 4 0 r-x-- [ anon ]
ffffffffff600000 4 0 0 r-x-- [ anon ]
---------------- ------ ------ ------
total kB 210964 408 80
Zero page 가 200 M 할당되어 작업되며, 여러번 실행해 보아도,
정확히 동일한 하나의 page 영역(map) 에 할당되어짐을 확인 할 수 있다.
키릴은 Huge page 를 이용할 때 발생하는 응용프로그램의 과다한 메모리 사용률을 해결하기 위해,
이 제로페이지를 Small ( Normal ) 과 Huge 로 나눠야 할필요성 을 느껴,
Huge page 를 위한 큰 제로페이지와 Huge Page 가 더이상 필요 없을 경우
그 제로페이지가 올바르게 반환 될 수 있도록 하는 reference counter 를 추가한 패치를 내놓았다.
하지만 이 항상 모든 패치들은 다른 문제들을 발생시키듯 패치역시 복잡한 문제를 수반하고 있었는데,
바로 THB 를 사용 하지 않는 경우에는 이 페이지가 할당되지 않는게 좋을 정도로
그 크기가 너무 컸다는 점, 그리고 반환을 위해 추가된 레퍼런스 카운터로 인해
Read-faulting 에 대한 벤치마크 결과가 무려 1% 정도나 느려졌기에,
이 레퍼런스 카운터가 유용한지에 대해서도 명확하지 않다는 점이이였다.
( 사실 난 1% 에 연연하고 싶지 않았는데말이야.. 키릴도 나와 같을듯 ㅋ )
결국 개발자들은 레퍼런스 카운팅 비용을 지불할 필요 없이, 그냥 Zero Huge Page 가
한번 할당 될때 그 주변을 유지시키는 것이 ( 일반적 해제방식을 말함 ) 더 좋겠다고 판단하였단다.
여기까지가 일단, small zero page 이후로 지금껏 진행되 온 상황에대한 설명이였다.
결과적으로, 이 HZP 패치에 대한 코멘트들은 별다른게 없었으며,
기존 Zero Page (small) 에 의거해서 큰 코드의 변화가 필요한 것도 없다고 보고 있는데다,
이 패치가 가져올 확실한 이점들을 감안했을때, 매우 가까운 미래에 추가되어야 하지 않을까 하는,
LWN 커널 담장기자인 Jonathan Corbet 의 견해가 담긴 기사를 확인 할 수 있었다..
사실, 기존 zero page mapping 을 그대로 유지하다가 아예 non-zero Huge page로
Switching 하는 방법을 사용하지 않느냐라는 의문도 있을만 하다. (LWN 댓글에 있다는)
하지만 역시나 이미 mapping 된 Page Area 를 변경하기 위해선 Page layer 를 모두 검색해야 할
필요가 있으며, 그것에 대한 temporary Translate mapping 작업이 TLB 쪽에서 이루어진다면,
처음부터 Huge Zero Page 를 사용한 것 보다 훨씬 더 큰 오버헤드를 내어 퍼포먼스가 떨어지는
일이 발생할 것이므로(답변도 있다.),
특별한 수정 없이 퍼포먼스에 큰 향상을 볼 수 있었다는 얘기도 있다는거..
기능이지 않을까 싶다..
그나마 가장 나은 방법이라고 생각하기 때문에, HZP 에 대해서만 얘기해 보았다.
PS. Transparent HugePages 에 대한 것은 2011 년에 이미 Corbet 에 의해, 커널문서로
작성되어있고, 관련된 파라메터들도 잘 설명되어 있다...
기회가 되면 이것에 대한 실제 벤치마킹등을 이용한 테스트도 포스트 해보고 싶다.
PS2. 아마 다음포스팅은 정말로 HugeTLB 에 대한 글이 되야 할것 같다.
PS3. 사실 부족한 이해력으로 LWN 기사만 보고 즉석테스트 정도로만 해서 정리한 글이므로,
언제든지 잘못 이해한 부분이 있으면 지적질 좀 해주라고.. 제발..