내가 퍼온 출처 : egloos.zum.com/dstein/v/1785346 출처 : http://msdn.microsoft.com/library/kor/default.asp?url=/library/KOR/dntaloc/html/heap3.asp Murali R. Krishnan 1999년 2월 소개동적으로 할당되는 C/C++ 개체를 효율적으로 사용하고 계십니까? 모듈 간의 원활한 통신을 위해 자동화를 광범위하게 사용하고 계십니까? 혹시 힙 할당으로 프로그램 속도가 느려질 가능성은 없습니까? 이것은 혼자만의 문제가 아닙니다. 조만간 거의 모든 프로젝트가 이러한 힙 문제에 직면하게 될 것입니다. 사람들은 대개 "힙이 느릴 뿐이지 내 코드에는 전혀 문제가 없습니다."라고 말합니다. 이 말이 어느 정도 맞을 수는 있습니다. 힙과 힙의 사용법 및 사용 결과에 대해 좀더 이해하게 되면 이러한 힙 문제를 해결하는 데 도움이 될 것입니다. 힙의 정의(힙에 대해 이미 알고 있다면 "일반적인 힙 성능 문제" 단원으로 이동해도 됩니다.) 힙은 개체를 프로그램에서 사용하기 위해 동적으로 할당하고 해제할 때 사용됩니다. 다음과 같은 경우에 힙 작업이 호출됩니다.
힙은 실행되는 동안 코드와 스택에 할당되는 메모리의 바깥쪽 부분을 사용합니다. 다음 그래프에서는 여러 가지 계층의 힙 할당자를 보여 줍니다. GlobalAlloc/GlobalFree: 프로세스별 기본 힙과 직접 통신하는 Microsoft® Win32® 힙 호출 LocalAlloc/LocalFree: 프로세스별 기본 힙과 직접 통신하는 Win32 힙 호출(Microsoft Windows NT®에서의 호환용) COM's IMalloc allocator (또는 CoTaskMemAlloc / CoTaskMemFree): 함수에서는 프로세스별 기본 힙을 사용합니다. 자동화에서는 COM(Component Object Model) 할당자를 사용하며 요청 시 프로세스별 힙을 사용합니다. CRT(C/C++ 런타임) 할당자: malloc(), free(), new 연산자 및 delete 연산자를 제공합니다. Microsoft Visual Basic®, Java 등의 언어도 new 연산자를 제공하며 힙 대신 가비지 수집을 사용합니다. CRT에서는 Win32 힙의 맨 위에 위치하는 자체 전용 힙을 만듭니다. Windows NT에서 Win32 힙은 Windows NT 런타임 할당자를 둘러싸는 얇은 계층입니다. 모든 API는 요청을 NTDLL에 전달합니다. Windows NT 런타임 할당자는 Windows NT 내에 핵심 힙 할당자를 제공합니다. 이 할당자는 크기가 8바이트에서 1024바이트 범위인 128개의 사용 가능한 목록을 가지고 있는 프런트 엔드 할당자로 구성됩니다. 백 엔드 할당자는 가상 메모리를 사용하여 페이지를 예약하고 커밋합니다. 차트의 맨 아래쪽에 있는 가상 메모리 할당자는 운영 체제에서 사용하는 페이지를 예약하고 커밋합니다. 모든 할당자는 데이터를 액세스하는 데 가상 메모리 기능을 사용합니다. 블록 할당과 해제가 간단하게 수행되지 않습니까? 시간이 많이 드는 이유는 무엇입니까? 힙 구현에 대한 참고 사항힙은 운영 체제와 런타임 라이브러리에서 구현됩니다. 프로세스가 시작될 때 운영 체제에서는 프로세스 힙이라고 하는 기본 힙을 만듭니다. 다른 힙이 사용되지 않으면 프로세스 힙은 블록 할당에 사용됩니다. 또한 언어 런타임도 프로세스 안에서 별도의 힙을 만들 수 있습니다. 예를 들어, C 런타임은 자체 힙을 만듭니다. 응용 프로그램이나 로드된 여러 DLL(동적 연결 라이브러리) 중 하나는 이러한 전용 힙 외에도 별도의 힙을 만들어 사용할 수 있습니다. Win32에서는 전용 힙을 만들어 사용할 수 있는 풍부한 API를 제공합니다. 힙 함수에 대한 유용한 자습서를 보려면 MSDN Platform SDK 노드를 참조하십시오. 응용 프로그램이나 DLL에서 전용 힙을 만드는 경우, 힙은 프로세스 공간에 유지되며 프로세스 차원의 액세스가 가능합니다. 특정 힙으로부터 할당된 데이터는 동일한 힙에 대해 해제되어야 합니다. 특정 힙으로부터 할당하고 이와는 다른 힙에 대해 해제하는 것은 올바르지 않습니다. 힙은 모든 가상 메모리 시스템에서 운영 체제 가상 메모리 관리자의 맨 위에 위치합니다. 또한 언어 런타임 힙도 가상 메모리의 맨 위에 위치합니다. 이러한 힙은 경우에 따라 OS 힙에서 계층을 이루기도 하지만, 언어 런타임 힙은 큰 블록을 할당하여 자체 메모리 관리를 수행합니다. OS 힙을 건너 뛰어 가상 메모리 함수를 사용하면 힙에서는 블록을 할당하여 사용하는 작업을 더욱 잘 수행할 수 있습니다. 일반적인 힙 구현은 프런트 엔드 할당자와 백 엔드 할당자로 이루어집니다. 프런트 엔드 할당자는 사용 가능한 고정 크기 블록의 목록을 유지합니다. 할당이 호출되면 힙에서는 프런트 엔드 목록에서 사용 가능한 블록 찾기를 시도합니다. 빈 블록을 찾을 수 없으면 힙에서는 요청을 만족시키기 위해 가상 메모리를 예약하고 커밋하는 백 엔드에서 큰 블록을 강제로 할당하게 됩니다. 일반적인 구현에는 블록 당 할당 오버헤드가 있어 실행 주기가 소요될 뿐 아니라 사용할 수 있는 저장소가 줄어들기도 합니다. 이 항목에 대한 자세한 정보는 Knowledge Base 기사 Q10758, "Managing Memory with calloc() and malloc()"(기사 ID 번호로 검색)에 들어 있습니다. 또한, 힙 구현 및 설계에 대한 자세한 정보를 보려면 "Dynamic Storage Allocation: A Survey and Critical Review"(By Paul R. Wilson, Mark S. Johnstone, Michael Neely, and David Boles, In International Workshop on Memory Management, Kinross, Scotland, UK, September 1995, http://www.cs.utexas.edu/users/oops/papers.html)를 참고하십시오. Windows NT 구현(Windows NT 버전 4.0 이상)에서는 크기가 8바이트에서 1,024바이트 범위인 8바이트 단위로 정렬된 블록의 사용 가능한 목록 127개와 기타 용도의 목록 하나를 사용합니다. 기타 용도의 목록(사용 가능한 목록[0])에는 크기가 1,024바이트보다 큰 블록이 들어갑니다. 사용 가능한 목록에는 이중으로 연결된 목록에서 함께 연결된 개체가 포함됩니다. 프로세스 힙은 기본적으로 병합 작업을 수행합니다. 여기서 병합이란, 사용 가능한 인접한 블록을 결합하여 더 큰 블록으로 만드는 작업을 말합니다. 병합에는 다른 주기가 추가로 소요되지만 힙 블록의 내부 조각화를 줄일 수 있습니다. 전역 잠금을 한 번 수행하면 힙이 여러 개의 스레드로 사용되는 것을 막을 수 있습니다. 자세한 내용을 보려면 George Reilly의 Server Performance and Scalability Killers에서 첫째 명령문을 참조하십시오. 힙 데이터 구조가 여러 스레드에서 임의로 액세스되는 것을 방지하려면 반드시 이러한 잠금을 수행해야 합니다. 그러나 힙 작업이 너무 자주 수행될 경우에는 이러한 잠금이 성능에 나쁜 영향을 줄 수 있습니다. 일반적인 힙 성능 문제힙을 사용하여 작업할 경우 가장 많이 발생하는 문제는 다음과 같습니다.
경합으로 인해 해제 작업 뿐 아니라 할당에서도 속도가 저하될 수 있습니다. 이상적으로, 사용자는 경합이 발생하지 않으며 빠른 할당/해제 작업이 가능한 힙을 소유하고자 합니다. 하지만 그러한 힙은 언젠가는 가능할 지 모르나 아직까지는 존재하지 않습니다. IIS, MSProxy, DatabaseStacks, 네트워크 서버, Exchange 등의 모든 서버 시스템에서는 힙 잠금으로 인해 아주 극심한 병목 현상이 발생합니다. 프로세서 수가 많을수록 경합은 심해집니다. 힙 문제 방지이제까지는 힙 사용 문제에 대해 알아보았습니다. 여기에서는 이러한 문제를 모두 없앨 수 있는 멋진 방법이 있는지 알아볼 것입니다. 사용자는 그러한 방법이 있기를 기대합니다. 그러나 힙의 속도를 빠르게 하는 방법은 없으므로 제품을 출시하기 직전에 속도를 빠르게 하려는 기대는 하지 않아야 합니다. 대신, 힙 전략을 미리 세우면 힙 속도가 훨씬 빨라질 수 있습니다. 성능 향상을 위한 확실한 전략으로는 힙 사용 방법 변경 및 힙 작업 수 줄이기가 있습니다. 그렇다면, 힙 작업의 사용을 어떻게 줄일 수 있을까요? 사용자는 데이터 구조 안에서 장소를 활용하여 힙 작업 수를 줄일 수 있습니다. 다음 예제를 참조하십시오.
struct ObjectB { // Use of ObjectA and ObjectB together. // // // struct ObjectX {
이러한 기법을 통해 얻을 수 있는 효과는 개체 형식, 크기, 작업 로드에 따라 달라집니다. 그러나 성능과 확장성 면에서는 항상 좋은 효과를 얻을 수 있습니다. 아래쪽의 코드는 좀더 전문화될 것이나 잘 생각하면 쉽게 관리될 수 있습니다. 성능 향상을 위한 그 밖의 방법다음은 속도를 향상시키기 위한 몇 가지 추가적인 방법입니다.
요약힙 구현은 모든 플랫폼에 대해 일반적으로 적용되는 경향이 있으므로 오버로드가 매우 큽니다. 각 개인의 코드에는 특정 요구 사항이 있지만 디자인에는 이 기사에서 언급된 원리들을 모두 수용하여 힙 상호 작용을 줄일 수 있습니다.
|