힙 메모리 할당 - hib memoli haldang

내가 퍼온 출처 : egloos.zum.com/dstein/v/1785346


출처 : http://msdn.microsoft.com/library/kor/default.asp?url=/library/KOR/dntaloc/html/heap3.asp

Murali R. Krishnan
Microsoft Corporation

1999년 2월

소개

동적으로 할당되는 C/C++ 개체를 효율적으로 사용하고 계십니까? 모듈 간의 원활한 통신을 위해 자동화를 광범위하게 사용하고 계십니까? 혹시 힙 할당으로 프로그램 속도가 느려질 가능성은 없습니까? 이것은 혼자만의 문제가 아닙니다. 조만간 거의 모든 프로젝트가 이러한 힙 문제에 직면하게 될 것입니다. 사람들은 대개 "힙이 느릴 뿐이지 내 코드에는 전혀 문제가 없습니다."라고 말합니다. 이 말이 어느 정도 맞을 수는 있습니다. 힙과 힙의 사용법 및 사용 결과에 대해 좀더 이해하게 되면 이러한 힙 문제를 해결하는 데 도움이 될 것입니다.

힙의 정의

(힙에 대해 이미 알고 있다면 "일반적인 힙 성능 문제" 단원으로 이동해도 됩니다.)

힙은 개체를 프로그램에서 사용하기 위해 동적으로 할당하고 해제할 때 사용됩니다. 다음과 같은 경우에 힙 작업이 호출됩니다.

  1. 프로그램에 필요한 개체의 개수나 크기를 미리 알 수 없는 경우
  2. 개체가 너무 커서 스택 할당자에 맞지 않는 경우

힙은 실행되는 동안 코드와 스택에 할당되는 메모리의 바깥쪽 부분을 사용합니다. 다음 그래프에서는 여러 가지 계층의 힙 할당자를 보여 줍니다.

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에서 첫째 명령문을 참조하십시오. 힙 데이터 구조가 여러 스레드에서 임의로 액세스되는 것을 방지하려면 반드시 이러한 잠금을 수행해야 합니다. 그러나 힙 작업이 너무 자주 수행될 경우에는 이러한 잠금이 성능에 나쁜 영향을 줄 수 있습니다.

일반적인 힙 성능 문제

힙을 사용하여 작업할 경우 가장 많이 발생하는 문제는 다음과 같습니다.

  • 할당 작업으로 인헤 속도가 저하됩니다. 단지 할당하는 데 시간이 많이 소요될 수 있습니다. 이러한 속도 저하의 가장 큰 원인은 블록이 사용 가능한 목록에 없으므로 런타임 할당자 코드에서 사용 가능한 더 큰 블록을 찾거나 백 엔드 할당자로부터 새 블록을 할당하는 데 주기가 소요되기 때문입니다.
  • 해제 작업으로 인해 속도가 저하됩니다. 주로 병합을 사용할 때 해제 작업에 더 많은 주기가 소요됩니다. 병합하는 동안 각 해제 작업에서는 해당 인접 항목을 "찾아내어" 더 큰 블록을 만들고, 그 블록을 해제 목록에 다시 삽입합니다. 그러한 찾기가 수행되는 동안에는 메모리가 임의의 순서로 액세스되어 캐시 누락이 발생하고 성능이 저하될 수 있습니다.
  • 힙 경합으로 인해 속도가 저하됩니다. 두 개 이상의 스레드에서 동시에 데이터에 액세스하려고 하면 경합이 발생하여 한 쪽 스레드의 작업이 완료되어야 다른 쪽 스레드의 작업이 진행될 수 있습니다. 경합으로 인해 항상 문제가 발생하며, 이 문제는 현재 다중 프로세서 시스템에서 일어나는 문제 중 가장 큰 문제입니다. 메모리 블록을 아주 많이 사용하는 응용 프로그램이나 DLL이 여러 개의 스레드로 실행되거나 다중 프로세서 시스템에서 실행되면 속도가 느려집니다. 이 문제를 해결하려면 일반적으로 단일 잠금 방법을 사용하여 해당 힙을 사용하는 모든 작업을 serialize합니다. 이러한 serialization으로 인해 스레드에서는 잠금을 기다리는 동안 컨텍스트를 전환할 수 있습니다. 깜빡이는 적색 정지 신호등의 신호 규제로 인해 속도가 저하되는 것을 상상해 보십시오.

    경합은 일반적으로 스레드와 프로세스의 컨텍스트 전환을 가져옵니다. 컨텍스트 전환에도 리소스가 많이 소모되지만, 프로세서 캐시에서 데이터가 손실되어 나중에 해당 스레드가 다시 살아날 때 이 데이터를 다시 작성하는 데에 리소스가 훨씬 많이 소모됩니다.

  • 힙 손상으로 인해 속도가 저하됩니다. 응용 프로그램에서 힙 블록을 적절하게 사용하지 않을 경우 힙이 손상됩니다. 가장 많이 발생할 수 있는 힙 손상 문제로는 이중 해제, 해제 후 블록 사용, 블록 경계를 벗어나 덮어쓰기 등이 있습니다. 이 기사에서는 손상에 대해 다루지 않습니다. 자세한 내용을 보려면 Microsoft Visual C++® 디버깅 문서에서 메모리 덮어쓰기와 메모리 누수에 대한 내용을 참조하십시오.
  • allocs와 reallocs의 잦은 사용으로 인해 속도가 저하됩니다. 이러한 현상은 스크립트 언어를 사용하는 경우 많이 발생할 수 있습니다. 문자열은 반복하여 할당되고 해제되며 재할당될 경우 그 길이가 늘어날 수 있습니다. 그러나 이렇게 해서는 안 됩니다. 될 수 있으면 큰 문자열을 할당하도록 하고 버퍼를 사용해야 합니다. 문자열 연결 작업을 최소화하는 것도 하나의 방법이 될 수 있습니다.

경합으로 인해 해제 작업 뿐 아니라 할당에서도 속도가 저하될 수 있습니다. 이상적으로, 사용자는 경합이 발생하지 않으며 빠른 할당/해제 작업이 가능한 힙을 소유하고자 합니다. 하지만 그러한 힙은 언젠가는 가능할 지 모르나 아직까지는 존재하지 않습니다.

IIS, MSProxy, DatabaseStacks, 네트워크 서버, Exchange 등의 모든 서버 시스템에서는 힙 잠금으로 인해 아주 극심한 병목 현상이 발생합니다. 프로세서 수가 많을수록 경합은 심해집니다.

힙 문제 방지

이제까지는 힙 사용 문제에 대해 알아보았습니다. 여기에서는 이러한 문제를 모두 없앨 수 있는 멋진 방법이 있는지 알아볼 것입니다. 사용자는 그러한 방법이 있기를 기대합니다. 그러나 힙의 속도를 빠르게 하는 방법은 없으므로 제품을 출시하기 직전에 속도를 빠르게 하려는 기대는 하지 않아야 합니다. 대신, 힙 전략을 미리 세우면 힙 속도가 훨씬 빨라질 수 있습니다. 성능 향상을 위한 확실한 전략으로는 힙 사용 방법 변경 및 힙 작업 수 줄이기가 있습니다.

그렇다면, 힙 작업의 사용을 어떻게 줄일 수 있을까요? 사용자는 데이터 구조 안에서 장소를 활용하여 힙 작업 수를 줄일 수 있습니다. 다음 예제를 참조하십시오.


struct ObjectA {
   // data for objectA
}

struct ObjectB {
   // data for objectB
}

// Use of ObjectA and ObjectB together.

//
// Use pointers
//
struct ObjectB {
   struct ObjectA * pObjA;
   // data for objectB
}

//
// Use embedding
//
struct ObjectB {
   struct ObjectA pObjA;
   // data for objectB
}

//
// Aggregation – use ObjectA and ObjectB inside another object
//

struct ObjectX {
   struct ObjectA  objA;
   struct ObjectB  objB;
}

  1. 두 개의 데이터 구조를 연결하기 위해 포인터를 사용하지 마십시오. 두 개의 데이터 구조를 연결하기 위해 포인터를 사용하면 위 예제의 개체 A와 B가 별도로 할당되고 해제됩니다. 이것은 불필요하게 리소스가 소모되는 작업이므로 피해야 합니다.
  2. 포인터로 가리킨 자식 개체를 부모 개체에 포함하십시오. 개체에 포인터를 사용할 경우에는 항상 동적인 요소(80%)와 역참조할 새 위치가 있습니다. 자식 개체를 포함하게 되면 장소가 늘어나고 더 이상의 할당/해제에 대한 필요성이 줄어듭니다. 따라서 응용 프로그램의 성능이 향상됩니다.
  3. 작은 개체를 결합하여 큰 개체(집합체)를 만드십시오. 집합체를 사용하면 할당되고 해제되는 블록 수가 줄어듭니다. 디자인의 다양한 부분에 대해 여러 명의 개발자가 작업하는 경우, 결합할 수 있는 많은 작은 개체가 있는 채로 작업이 끝날 수 있습니다. 이러한 통합의 어려움은 정확한 집합체 경계를 찾는 것입니다.
  4. 사용자 요구의 80%를 충족시킬 수 있는 버퍼를 인라인하십시오(80-20 규칙이라고도 함). 일부 경우에는 문자열/이진 데이터를 저장하기 위해 메모리 버퍼가 필요하지만 전체 바이트 수는 미리 알지 못할 수도 있습니다. 필요한 메모리 버퍼를 측정하여 사용자 요구의 80%를 충족시킬 수 있는 크기의 버퍼를 인라인합니다. 나머지 20%에 대해서는 새로운 버퍼를 할당하고 그 버퍼에 대해 포인터를 사용할 수 있습니다. 이렇게 하면 할당 및 해제 호출이 줄어들 뿐 아니라 데이터의 공간적인 장소가 늘어나게 되어 결과적으로 코드 성능이 향상됩니다.
  5. 개체를 청크로 할당하십시오(청킹). 청킹은 개체를 한 번에 두 개 이상의 그룹으로 할당하는 방법입니다. 예를 들어, {이름, 값} 쌍의 목록과 같은 항목 목록을 추적해야 하는 경우에는 다음 두 가지 옵션을 사용할 수 있습니다. 옵션 1은 이름-값 쌍 하나 당 노드 하나를 할당하는 것이고 옵션 2는 이름-값 쌍 다섯 개를 보유할 수 있는 구조를 할당하는 것입니다. 예를 들어, 일반적으로 네 개의 쌍을 저장하는 경우가 많다면 노드 수와 추가 연결 목록 포인터에 필요한 여분의 공간 크기를 줄일 수 있습니다.

    청킹은 데이터 블록의 일부가 청크 분할 할당을 위한 동일한 가상 페이지에 있을 뿐만 아니라 제공되는 장소가 늘어나기 때문에 프로세서 캐시, 특히 L1-캐시에 친숙합니다.

  6. _amblksiz를 적절히 사용하십시오. CRT(C 런타임)에는 백 엔드(Win32 힙)에서 _amblksiz 크기로 블록을 할당하는 자체의 사용자 지정 프런트 엔드 할당자가 있습니다. _amblksiz를 더 높은 값으로 설정하면 백 엔드에 발생하는 호출 수가 줄어듭니다. 이것은 CRT를 광범위하게 사용하는 프로그램에만 적용됩니다.

이러한 기법을 통해 얻을 수 있는 효과는 개체 형식, 크기, 작업 로드에 따라 달라집니다. 그러나 성능과 확장성 면에서는 항상 좋은 효과를 얻을 수 있습니다. 아래쪽의 코드는 좀더 전문화될 것이나 잘 생각하면 쉽게 관리될 수 있습니다.

성능 향상을 위한 그 밖의 방법

다음은 속도를 향상시키기 위한 몇 가지 추가적인 방법입니다.

  1. Windows NT5 힙 사용

    몇몇 개발자들의 수고와 노력으로 인해 1998년 초 Microsoft Windows® 2000에 다음과 같이 중요한 기능 향상이 이루어졌습니다.

    • 힙 코드 안에서의 잠금 향상 힙 코드에서는 힙 당 하나의 잠금을 사용합니다. 이러한 전역 잠금은 여러 스레드에서 힙 데이터 구조를 사용하는 것을 막기 위해 사용됩니다. 그러나, 소통량이 많은 경우에는 힙이 이러한 전역 잠금에서도 꼼짝 못하게 되어 경합이 치열해지고 성능이 저하될 수 있습니다. Windows 2000에서는 잠금 안의 중요한 부분이 줄어들어 경합이 발생할 가능성이 최소화되므로 확장성이 향상됩니다.
    • Lookaside 목록 사용 힙 데이터 구조는 크기가 8바이트에서 1,024바이트 사이인(8바이트씩 증가) 블록의 모든 사용 가능한 항목에 대해 빠른 캐시를 사용합니다. 기존에는 빠른 캐시가 전역 잠금 안에서 보호되었지만, 이제는 사용 가능한 빠른 캐시 목록을 액세스하는 데 lookaside 목록을 사용합니다. 이러한 목록에는 잠금이 필요 없는 대신, 64비트 연동 작업이 사용되므로 성능이 향상됩니다.
    • 내부 데이터 구조 알고리즘 향상

    이러한 향상으로 인해 할당 캐시가 필요 없게 되었지만 그 밖의 최적화가 배제되지는 않습니다. Windows NT5 힙을 사용하여 코드를 평가하십시오. 1,024바이트(1KB) 미만의 블록(프런트 엔드 할당자의 블록)에 대해서는 최적화되어야 합니다. GlobalAlloc() LocalAlloc()은 같은 힙 상에 만들어지며 프로세스별 힙을 액세스하는 데 가장 많이 사용되는 메커니즘입니다. 지역화된 고성능이 필요하다면 할당 작업에 대해 자체 힙을 직접 만들거나 프로세스별 힙을 액세스하는 데 Heap* API를 사용하십시오. VirtualAlloc()/VirtualFree() 작업이 큰 블록 작업에 필요하다면 이를 직접 사용할 수도 있습니다.

    이러한 기능 향상은 Windows 2000 베타 2와 Windows NT 4.0 SP4에 반영되었으며, 이로 인해 힙 잠금 경합 비율이 현저하게 떨어졌습니다. 이것은 Win32 힙 사용자에게 도움을 줍니다. CRT 힙은 Win32 힙의 맨 위에 만들어지지만 자체의 작은 블록 힙을 사용하므로 Windows NT의 기능 향상으로 인한 이점이 없습니다. 또한 Visual C++ 버전 6.0에도 향상된 힙 할당자가 있습니다.

  2. 할당 캐시 사용

    할당 캐시를 사용하여 할당된 블록을 나중에 다시 사용할 수 있도록 캐시할 수 있습니다. 이렇게 하면 프로세스 힙 또는 전역 힙에 대한 할당/해제 호출 수를 줄일 수 있을 뿐 아니라 이전에 할당된 블록을 최대한 다시 사용할 수 있게 됩니다. 또한, 더 높은 수준에서의 개체 사용법을 더욱 잘 이해하기 위해 통계 자료를 모을 수도 있습니다.

    일반적으로, 사용자 지정 힙 할당자는 프로세스 힙의 맨 위에서 구현됩니다. 사용자 지정 힙 할당자는 시스템 힙과 아주 비슷하게 동작합니다. 중요한 차이점은 사용자 정의 힙 할당자가 할당된 개체에 대한 프로세스 힙의 맨 위에 캐시를 제공한다는 것입니다. 캐시는 고정된 크기 집합(예: 32바이트, 64바이트, 128바이트 등)에 대해 만들어집니다. 이것이 좋은 전략이긴 하지만, 이러한 종류의 사용자 지정 힙 할당자는 할당 및 해제되는 개체에 관한 의미 정보를 놓치게 됩니다.

    "Alloc-캐시"는 사용자 지정 할당자와는 반대로 클래스별 할당 캐시로 구현됩니다. 이 캐시는 사용자 지정 힙 할당자의 주요 기능을 모두 제공할 뿐 아니라 수많은 의미 정보도 보유할 수 있습니다. 각각의 할당 캐시 처리기는 이진 대상의 개체 하나에 연관됩니다. 이것은 동시성 수준, 개체 크기, 사용 가능한 목록에 유지할 요소 수 등을 나타내는 일련의 매개 변수로 초기화될 수 있습니다. 할당 캐시 처리기 개체는 자체의 전용 풀에 지정된 임계값을 초과하지 않는 사용 가능한 항목을 유지 관리하며, 보호를 위해 전용 잠금을 사용합니다. 이렇게, 할당 캐시와 전용 잠금은 기본 시스템 힙에 대한 소통량을 함께 줄여 주므로 동시성이 향상되고, 재사용성이 최대화되며, 확장성이 높아집니다.

    모든 할당 캐시 처리기의 동작을 확인하고 사용되지 않은 리소스를 회수하는 데 스캐빈저가 주기적으로 필요합니다. 어떠한 동작도 찾을 수 없는 경우에는 할당된 개체 풀을 해제하여 성능을 향상시킬 수 있습니다.

    각 할당/해제 동작을 감사할 수 있습니다. 첫 번째 수준의 정보에는 개체의 전체 개수, 할당 및 해제 호출이 포함됩니다. 이러한 통계를 사용하여 다양한 개체 간의 의미 관계를 끌어 낼 수 있습니다. 이 관계는 방금 설명한 많은 기법들 중 하나를 사용하여 메모리 할당을 줄이는 데 사용될 수 있습니다.

    또한 할당 캐시는 디버깅 지원 역할도 수행하여 제대로 정리되지 않은 개체 수를 추적하는 것을 도와줍니다. 정리되지 않은 개체는 물론 동적 스택 역추적과 서명을 확인함으로써 문제가 있는 호출자를 정확하게 찾을 수도 있습니다.

  3. MP 힙

    MP 힙은 다중 프로세서에 친숙한 분산 할당을 위한 패키지로서, Win32 SDK(Windows NT 4.0 이상)에서 사용할 수 있습니다. 기존에는 JVert로 구현되었던 힙 추상화가 여기서는 Win32 힙 패키지의 맨 위에 만들어집니다. MP 힙은 Win32 힙을 여러 개 만들며, 모든 단일 잠금의 경합을 줄이기 위해 할당 호출을 여러 힙에 분산하려고 시도합니다.

    이 패키지는 유용한 단계로서, 향상된 MP에 친숙한 일종의 사용자 지정 힙 할당자입니다. 그러나, 의미 정보가 제공되지 않으며, 통계 자료도 부족합니다. MP 힙은 SDK 라이브러리로 가장 많이 사용됩니다. 이러한 SDK로 다시 사용할 수 있는 구성 요소를 만들면 큰 도움이 되지만, SDK 라이브러리를 모든 DLL 안에 만들면 작업 집합이 커지게 됩니다.

  4. 알고리즘과 데이터 구조에 대한 신중한 고려

    다중 프로세서 시스템에서 조정하려면 알고리즘, 구현, 데이터 구조, 하드웨어 등이 동적으로 조정되어야 합니다. 가장 많이 할당되고 해제되는 데이터 구조를 보십시오. 다른 데이터 구조로 그 작업을 수행할 수 있는지 생각해 보십시오. 예를 들어, 응용 프로그램 초기화 시점에 로드되는 읽기 전용 항목의 목록이 있을 경우, 이 목록은 선형으로 연결된 목록이 아니어도 됩니다. 이 목록은 동적으로 할당된 배열일 수 있습니다. 동적으로 할당된 배열의 경우는 메모리의 힙 블록과 조각화가 줄어들게 되므로 성능이 향상될 수 있습니다.

    필요한 작은 개체 수가 줄어들면 힙 할당자의 로드가 줄어듭니다. 예를 들어, 여기서는 서버의 중요한 처리 경로에서 다섯 개의 서로 다른 개체를 사용하여 각각 별도로 할당하고 해제했습니다. 개체를 함께 캐시하면 힙 호출이 다섯 번에서 한 번으로 줄어들고 힙에 대한 로드가 엄청나게 줄어듭니다. 특히, 초 당 1,000개가 넘는 요청을 처리할 때는 더욱 그렇습니다.

    자동화 구조를 광범위하게 사용하는 경우에는 주요 코드에서 자동화 BSTR을 분리하거나 최소한 BSTR에 대해 반복되는 작업을 피하도록 하십시오. BSTR 연결로 인해 재할당 및 할당/해제 작업이 과도하게 발생합니다.

요약

힙 구현은 모든 플랫폼에 대해 일반적으로 적용되는 경향이 있으므로 오버로드가 매우 큽니다. 각 개인의 코드에는 특정 요구 사항이 있지만 디자인에는 이 기사에서 언급된 원리들을 모두 수용하여 힙 상호 작용을 줄일 수 있습니다.

  1. 코드의 힙 사용 부분을 평가합니다.
  2. 중요한 경로를 분석하고 데이터 구조를 수정함으로써 힙 호출을 적게 사용하도록 코드를 간단하게 만듭니다.
  3. 사용자 지정 래퍼를 구현하기 전에 힙 호출에 소모되는 자원을 파악하기 위한 측정을 합니다.
  4. 성능이 만족스럽지 않으면 힙을 향상시켜 줄 것을 OS 그룹에 요청합니다. 이러한 종류의 요구가 많다는 것은 힙 향상에 그만큼 관심이 많다는 의미가 됩니다.
  5. 할당자를 OS에서 제공한 힙의 씬 래퍼로 만들어 줄 것을 C 런타임 그룹에 요청합니다. 결과적으로 C 런타임 힙 호출에 소모되는 자원은 OS 힙이 향상됨에 따라 줄어듭니다.
  6. 힙 향상은 운영 체제(Windows NT 패밀리)에서 지속적으로 이루어지고 있는 부분입니다. 그러므로Windows NT 패밀리를 계속 사용하면 많은 도움이 될 것입니다.