멀티프로그래밍 위키로 바로가기 → http://www.devnote.net/wiki
얼마전 Windows 2003 Server (64비트 버전) 에서 실행 중인 테스트용 파일 서버가 매우 느려지며 시스템 리소스 부족현상이 나타났습니다. 이 서버는 4GB의 메모리를 가지고 있었으며, 파일서버 서비스 프로그램은 이중 최대 3.5GB의 메모리를 자체 캐쉬 메모리로 사용할 수 있도록 프로그램되어 있었습니다. 그런데, 문제는 64비트 윈도우즈가 파일 시스템 캐쉬로 모든 물리 메모리를 사용하려 한다는 하는 것이었습니다. 아래 SQL 서버와 관련하여 비슷한 문제가 이미 알려져 있었습니다.

http://sqlblogcasts.com/blogs/grumpyolddba/archive/2009/03/18/x64-memory-problems.aspx

윈도우즈와 어플리케이션이 메모리를 가지고 서로 경쟁하는 상황이 발생하는데, 윈도우즈는 어플리케이션의 워킹셋(working set)을 스왑파일로 페이지 아웃시킬 수도 있다는 것입니다. 이 시점에서 윈도우즈의 시스템 파일 캐쉬는 의미가 없어집니다. 왜냐하면, 어플리케이션이 사용하는 메모리가 하드디스크로 페이징 되면, 페이징 하는 시간 뿐만 아니라, 이 메모리를 액세스하기 위해 많은 시간이 소요됩니다. 특히, 수학적 데이터 프로세싱(수치해석, 이미지 프로세싱, 3D 랜더링과 같은)을 다루는 프로그램에서 이런 현상이 생기게되면, 프로그램의 속도는 엄청나게 떨어지게 됩니다. (메모리와 HDD속도 비유를 참고하시기 바랍니다.)

이 문제는 Windows 7에서 향상되었다라고 하는데 얼마나 어떻게 향상되었는지는 아직 알 수 없습니다. 다행히, SetSystemFileCacheSize()를 사용하여 시스템 캐쉬 메모리 크기를 조정할 수 있습니다. 그리고 SysInternals의 Cacheset.exe라는 유틸리티를 사용하여 변경 확인 가능합니다.

그런데, 여기서 좀 더 생각해보면, 64비트 Windows는 약 8TB라는 엄청난 메모리 주소공간을 가지기 때문에 64비트 OS나 어플리케이션이 한꺼번에 많은 물리메모리를 사용할 가능성은 얼마든지 있다는 것 입니다. (32비트 프로그램은 64비트 윈도우즈에서 4GB의주소공간을 가짐)

그럼 128GB를 가진 64비트 윈도우즈가 있다고 합시다. 처음드는 생각은 엄청난 메모리라는 것인데, 단순히 메모리 주소공간을 생각하면, 이는 32비트 윈도우즈에 48MB의 메모리를 설치한 정도 밖에는 되지 않습니다. 128GB 메모리는 거대 공룡에게 사과 한 쪽을 먹으라고 주는 것에 불과합니다. 물론, 아직 이렇게 많은 메모리를 사용할 프로그램은 드물 것이라 생각할 수도 있지만, 위의 파일서버 문제와 같이 윈도우즈가 파일 시스템 캐쉬를 계속 메모리에 쌓아두거나, 어떤 64비트 프로그램이 의도했든 아니면 버그이든 메모리를 많이 사용하기 시작하면 128GB는 아무 것도 아니라는 것 입니다.

여기서, 얻을 수 있는 점은 64비트 운영체제와 64비트 어플리케이션을 제작 (특히 서버 프로그램)함에 있어, 시스템메모리의 크기에 따라 실제 최대 얼마 만큼의 메모리를 사용할 것인지, 미리 잘 정해야만 한다는 것 입니다. 특히 파일 액세스가 많은 프로그램의 경우 윈도우즈 시스템의 파일 캐쉬도 염두해 두어야만 합니다. 하나에 1TB 싸이즈의 DIMM 메모리가 나온다면 상황은 달라지겠지만 말입니다.

크리에이티브 커먼즈 라이센스
Creative Commons License

Mark Russinovich가 최근에 발표한 VMMap은 놀라울 정도로 자세하게 프로세스의 가상메모리와 물리메모리 사용 상태를 빠르고 보기 쉽게 보여줍니다. 그 전에는 디버거를 사용하여 분석해야만 했던 것을 보기 쉽게 색깔별 종류별로 UI에 표시해 주는데 정말 유용한 메모리 분석 툴이라고 생각됩니다.

특히, 서버프로그램의 경우 가상/물리 메모리 사용량 뿐만 아니라 Free 메모리 리스트를 확인하므로써 얼마나 메모리가 단편화 (fragmentation)되어 있는지 쉽게 알 수있습니다. (아주 오랫동안 실행 중인 서버프로그램은 메모리 단편화로 결국 언젠가 재시작해야만 합니다)
크리에이티브 커먼즈 라이센스
Creative Commons License

아래 링크는 바로 전 포스팅에서 소개한 메모리 지연시간(latency)에 관해 아주 잘 정리된 문서를중에 실생활에 비유한 내용을 번역 정리해보았습니다.

what-your-computer-does-while-you-wait


성능 혹은 최적화를 생각할 때 항상 염두해 두어야할 것.

최신 CPU는 엄청나게 빠르다. 대부분의 심플한 연산을 수행하는 데에 단, 1 클럭 싸이클 밖에 소요되지 않는다(Core 2 CPU 기준). 이는 3GHz CPU 속도라면 1/3 나노초에 해당한다, (1/3 나노초동안 빛은 10cm 정도 진행할 수 있을 뿐이다)


메모리와 디스크 latency를 실생활에 비유한다면 다음과 같다.

1. L1 cache: 책상 위에서 종이 한 장을 잡기 (3초)

2. L2 cache: 책장에서 책 한 권을 꺼내기 (14초)

3. 메인 메모리: 복도로 걸어 나가 자판기에서 커피 한 잔을 뽑기 (4분)

4. 하드디스크: 건물을 나와서 1년 3개월 동안 세계일주 하기.


프로그램 최적화를 위해 무엇을 해야하는지 확실히 보여주는 좋은 비유인 것 같습니다.
크리에이티브 커먼즈 라이센스
Creative Commons License

온라인 서비스를 함에 항상 고려해야 할 점은 서비스의 업타임 (uptime) 입니다. 특히나, 24시간 서비스를 목적으로 하는 경우 1년에 얼마만큼의 다운타임을 가질 것이냐는 매우 중요한 문제이며 이러한 온라인 서비스를 회사간에 개발하거나 서비스할 때, 1년에 얼마만의 다운타임을 가질 것인가에 관해 회사간에 계약을 하곤합니다.

다섯 개의 9이라는 이른바 99.999%의 availability를 유지하는 것이 아마도 가장 이상적인 목표입니다. (이는 다시 말해 1년에 5.26분 정도의 다운타임만을 갖는 것). 하지만, five nines를 얻기위해 들어가는 비용이 너무 커서 실용성이 없는 것으로 알려져 있습니다.

온라인 게임도 분명 온라인 서비스 입니다. 그런데, 우리나라 온라인 업체들은 대부분 정기 점검이라는 명목으로 1주일에 무려 서너시간씩 서비스 다운 타임을 갖습니다. 다운타임이 커지는 것은 절대로 바람직한 일은 아닙니다. 특히 전세계를 상대로하는 글로벌 서비스를 하기 시작하면 많은 다운타임이 큰 문제가 될 수 있습니다. 즉, 한국의 수요일 오전이 미국의 오후가 되기 때문에, 이 때 서너시간의 정기점검은 용납되지 않습니다. 서버 개발자들의 큰 목표 중의 하나는 물론 365일 24시간 끊이지 않는 서비스를 제공하는 것입니다.

다운타임을 최소화 하는 데에 걸림돌은 여러 가지가 있으나, 해결이 어려운 것 중의 하나가 memeory fragmentation 입니다. 특히 작은 사이즈의 메모리를 아주 많이 할당/해제하는 경우 32비트 윈도우즈의 가상메모리 주소공간 (기본 2GB)은 단편화되기 쉽습니다. 윈도우즈의 LFH(Low Fragmentation Heap)을 사용하더라도, 중단 없이 실행중인 서버 어플리케이션은 결국 언젠가 memory fragmentation 문제를 만나게 됩니다.

최근 어느 한 서버에서 아래와 같이 Virtual Address Fragmentation 문제가 심하게 나타났는데, 이 서버는 사용 가능한 물리메모리를 충분히 가지고 있었지만, 메모리 단편화로 서비스를 재시작하여야만 하였습니다. (Uncommited range가 많아지는 것은 곧 Virtual Address Fragmentation이 많아 지는 것을 의미합니다)

64비트 프로그램의 경우는 이러한 현상을 매우 완화시킬 수 있을 것으로 생각됩니다.

0:020> !heap -s
  Heap     Flags   Reserv  Commit  Virt   Free  List   UCR  Virt  Lock  Fast
                   (k)     (k)    (k)     (k) length      blocks cont. heap
-----------------------------------------------------------------------------
006c0000 00001002 2565820   7232 658924   1302   160   200    0 44f3b4   LFH
   External fragmentation  18 % (160 free blocks)
   Virtual address fragmentation  98 % (200 uncommited ranges)

길드워 서버들의 uptime을 보면 100일이상 동안 실행 중인 서버들도 많습니다. 길드워 게임의 특징상 (인스턴스 맵) 게임 플레이어들에게 영향을 주지 않고 게임서버를 재부팅 할 수 있습니다. 따라서 최근에는 오랜동안 실행 중인 서버는 일정시간 disable 시킨 후 사용자가 모두 logoff 하게 되면 재부팅하고 윈도우즈 업데이트를 적용하고 있습니다.


크리에이티브 커먼즈 라이센스
Creative Commons License

"Advanced Windows Debugging"이란 책이 곧 발간될 예정입니다. 아래 링크에 샘플 챕터가 있는데, 윈도우즈 힙 메모리 내부 구조에 관해 상세히 다루고 있습니다. 언젠가 쓴 적이 있는데 Visual C의 동적 메모리 할당 함수인 malloc/free 는 결국 HeapAlloc/HeapFree를 기본적으로 호출합니다. 그리고 이 힙메모리는 상당히 효율적으로 만들어져 있습니다. 그 내부 구조가 알고 싶다면, 아래 챕터가 큰 도움이 될 것입니다.

http://advancedwindowsdebugging.com/ch06.pdf
크리에이티브 커먼즈 라이센스
Creative Commons License

위키에 "대역폭과 지연시간"에 관한 글을 쓰고 있었으나. 게으름을 피워 아직 완성이 되지는 않았습니다. 그런데, 오늘 Herb Sutter의 블로그에서 이와 관련된 흥미로운 프리젠테이션을 발견했습니다.

Herb는 이것을 모든 프로그래머가 꼭 보아야할 내용으로 추천하고 있으며, 아래와 같은 티저가 있군요.

CPU의 단 1%만이 실제 컴퓨팅에 사용되고 있고, 나머지 99%는 메모리 지연시간(Latency)을 숨기기 위해 사용된다.


특히, Herb는 여기서 Itanium CPU 칩을 현미경으로 확대해 보면, 99%의 면적이 결국 데이터를 읽거나 쓰는데 사용되며, L1, L2, L3와 같은 CPU 캐쉬 메모리가 CPU 전체 트랜지스터 개수의 85%를 차지한다는 사실을 보여주고 있습니다. 이것은 Patterson교수의 프리젠테이션 슬라이드로부터 참고한 내용들 입니다.

CPU의 집적도가 높아지고, 속도가 빨라질수록 그리고 코어의 개수가 증가할 수록, 결국 메모리  액세스 시간 혹은 지연시간 (Latency)을 단축시키는 것이 가장 중요하고, 또 가장 힘듭니다. 이것은 단순히 CPU 자체 설계의 문제가 아니라 프로그램의 성능에 직접적인 영향을 미치는 가장 중요한 요인이기도 합니다.

기술의 눈부신 발전으로 시간이 지나면 모든 것을 해결해 줄 것이란 믿음은 여기서 또 한 번 무너져 버리는 것을 느낄 수 있습니다. CPU가 발전될수록, 오히려 가장 기본적인 메모리 액세스가 더욱 중요해지며 이것은 빛의 속도라는 한계에 부딪히고 있는 것입니다.
크리에이티브 커먼즈 라이센스
Creative Commons License