MPHEAP.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:19k
源码类别:

Windows编程

开发平台:

Visual C++

  1. /*++
  2. Copyright (c) 1995  Microsoft Corporation
  3. Module Name:
  4.     mpheap.c
  5. Abstract:
  6.     This DLL is a wrapper that sits on top of the Win32 Heap* api.  It
  7.     provides multiple heaps and handles all the serialization itself.
  8.     Many multithreaded applications that use the standard memory allocation
  9.     routines (malloc/free, LocalAlloc/LocalFree, HeapAlloc/HeapFree) suffer
  10.     a significant a significant performance penalty when running on a
  11.     multi-processor machine.  This is due to the serialization used by the
  12.     default heap package.  On a multiprocessor machine, more than one
  13.     thread may simultaneously try to allocate memory.  One thread will
  14.     block on the critical section guarding the heap.  The other thread must
  15.     then signal the critical section when it is finished to unblock the
  16.     waiting thread.  The additional codepath of blocking and signalling adds
  17.     significant overhead to the frequent memory allocation path.
  18.     By providing multiple heaps, this DLL allows simultaneous operations on
  19.     each heap.  A thread on processor 0 can allocate memory from one heap
  20.     at the same time that a thread on processor 1 is allocating from a
  21.     different heap.  The additional overhead in this DLL is compensated by
  22.     drastically reducing the number of times a thread must wait for heap
  23.     access.
  24.     The basic scheme is to attempt to lock each heap in turn with the new
  25.     TryEnterCriticalSection API.  This will enter the critical section if
  26.     it is unowned.  If the critical section is owned by a different thread,
  27.     TryEnterCriticalSection returns failure instead of blocking until the
  28.     other thread leaves the critical section.
  29.     Another trick to increase performance is the use of a lookaside list to
  30.     satisfy frequent allocations.  By using InterlockedExchange to remove
  31.     lookaside list entries and InterlockedCompareExchange to add lookaside
  32.     list entries, allocations and frees can be completed without needing a
  33.     critical section lock.
  34.     The final trick is the use of delayed frees.  If a chunk of memory is
  35.     being freed, and the required lock is already held by a different
  36.     thread, the free block is simply added to a delayed free list and the
  37.     API completes immediately.  The next thread to acquire the heap lock
  38.     will free everything on the list.
  39.     Every application uses memory allocation routines in different ways.
  40.     In order to allow better tuning of this package, MpHeapGetStatistics
  41.     allows an application to monitor the amount of contention it is
  42.     getting.  Increasing the number of heaps increases the potential
  43.     concurrency, but also increases memory overhead.  Some experimentation
  44.     is recommended to determine the optimal settings for a given number of
  45.     processors.
  46.     Some applications can benefit from additional techniques.  For example,
  47.     per-thread lookaside lists for common allocation sizes can be very
  48.     effective.  No locking is required for a per-thread structure, since no
  49.     other thread will ever be accessing it.  Since each thread reuses the
  50.     same memory, per-thread structures also improve locality of reference.
  51. --*/
  52. #include <windows.h>
  53. #include "mpheap.h"
  54. #define MPHEAP_VALID_OPTIONS  (MPHEAP_GROWABLE                 | 
  55.                                MPHEAP_REALLOC_IN_PLACE_ONLY    | 
  56.                                MPHEAP_TAIL_CHECKING_ENABLED    | 
  57.                                MPHEAP_FREE_CHECKING_ENABLED    | 
  58.                                MPHEAP_DISABLE_COALESCE_ON_FREE | 
  59.                                MPHEAP_ZERO_MEMORY              | 
  60.                                MPHEAP_COLLECT_STATS)
  61. //
  62. // Flags that are not passed on to the Win32 heap package
  63. //
  64. #define MPHEAP_PRIVATE_FLAGS (MPHEAP_COLLECT_STATS | MPHEAP_ZERO_MEMORY);
  65. //
  66. // Define the heap header that gets tacked on the front of
  67. // every allocation. Eight bytes is a lot, but we can't make
  68. // it any smaller or else the allocation will not be properly
  69. // aligned for 64-bit quantities.
  70. //
  71. typedef struct _MP_HEADER {
  72.     union {
  73.         struct _MP_HEAP_ENTRY *HeapEntry;
  74.         PSINGLE_LIST_ENTRY Next;
  75.     };
  76.     ULONG LookasideIndex;
  77. } MP_HEADER, *PMP_HEADER;
  78. //
  79. // Definitions and structures for lookaside list
  80. //
  81. #define LIST_ENTRIES 128
  82. typedef struct _MP_HEAP_LOOKASIDE {
  83.     PMP_HEADER Entry;
  84. } MP_HEAP_LOOKASIDE, *PMP_HEAP_LOOKASIDE;
  85. #define NO_LOOKASIDE 0xffffffff
  86. #define MaxLookasideSize (8*LIST_ENTRIES-7)
  87. #define LookasideIndexFromSize(s) ((s < MaxLookasideSize) ? ((s) >> 3) : NO_LOOKASIDE)
  88. //
  89. // Define the structure that describes the entire MP heap.
  90. //
  91. // There is one MP_HEAP_ENTRY structure for each Win32 heap
  92. // and a MP_HEAP structure that contains them all.
  93. //
  94. // Each MP_HEAP structure contains a lookaside list for quick
  95. // lock-free alloc/free of various size blocks.
  96. //
  97. typedef struct _MP_HEAP_ENTRY {
  98.     HANDLE Heap;
  99.     PSINGLE_LIST_ENTRY DelayedFreeList;
  100.     CRITICAL_SECTION Lock;
  101.     DWORD Allocations;
  102.     DWORD Frees;
  103.     DWORD LookasideAllocations;
  104.     DWORD LookasideFrees;
  105.     DWORD DelayedFrees;
  106.     MP_HEAP_LOOKASIDE Lookaside[LIST_ENTRIES];
  107. } MP_HEAP_ENTRY, *PMP_HEAP_ENTRY;
  108. typedef struct _MP_HEAP {
  109.     DWORD HeapCount;
  110.     DWORD Flags;
  111.     DWORD Hint;
  112.     DWORD PadTo32Bytes;
  113.     MP_HEAP_ENTRY Entry[1];     // variable size
  114. } MP_HEAP, *PMP_HEAP;
  115. VOID
  116. ProcessDelayedFreeList(
  117.     IN PMP_HEAP_ENTRY HeapEntry
  118.     );
  119. //
  120. // HeapHint is a per-thread variable that offers a hint as to which heap to
  121. // check first.  By giving each thread affinity towards a different heap,
  122. // it is more likely that the first heap a thread picks for its allocation
  123. // will be available.  It also improves a thread's locality of reference,
  124. // which is very important for good MP performance
  125. //
  126. __declspec(thread) DWORD HeapHint;
  127. HANDLE
  128. WINAPI
  129. MpHeapCreate(
  130.     DWORD flOptions,
  131.     DWORD dwInitialSize,
  132.     DWORD dwParallelism
  133.     )
  134. /*++
  135. Routine Description:
  136.     This routine creates an MP-enhanced heap. An MP heap consists of a
  137.     collection of standard Win32 heaps whose serialization is controlled
  138.     by the routines in this module to allow multiple simultaneous allocations.
  139. Arguments:
  140.     flOptions - Supplies the options for this heap.
  141.         Currently valid flags are:
  142.             MPHEAP_GROWABLE
  143.             MPHEAP_REALLOC_IN_PLACE_ONLY
  144.             MPHEAP_TAIL_CHECKING_ENABLED
  145.             MPHEAP_FREE_CHECKING_ENABLED
  146.             MPHEAP_DISABLE_COALESCE_ON_FREE
  147.             MPHEAP_ZERO_MEMORY
  148.             MPHEAP_COLLECT_STATS
  149.     dwInitialSize - Supplies the initial size of the combined heaps.
  150.     dwParallelism - Supplies the number of Win32 heaps that will make up the
  151.         MP heap. A value of zero defaults to three + # of processors.
  152. Return Value:
  153.     HANDLE - Returns a handle to the MP heap that can be passed to the
  154.              other routines in this package.
  155.     NULL - Failure, GetLastError() specifies the exact error code.
  156. --*/
  157. {
  158.     DWORD Error;
  159.     DWORD i;
  160.     HANDLE Heap;
  161.     PMP_HEAP MpHeap;
  162.     DWORD HeapSize;
  163.     DWORD PrivateFlags;
  164.     if (flOptions & ~MPHEAP_VALID_OPTIONS) {
  165.         SetLastError(ERROR_INVALID_PARAMETER);
  166.         return(NULL);
  167.     }
  168.     flOptions |= HEAP_NO_SERIALIZE;
  169.     PrivateFlags = flOptions & MPHEAP_PRIVATE_FLAGS;
  170.     flOptions &= ~MPHEAP_PRIVATE_FLAGS;
  171.     if (dwParallelism == 0) {
  172.         SYSTEM_INFO SystemInfo;
  173.         GetSystemInfo(&SystemInfo);
  174.         dwParallelism = 3 + SystemInfo.dwNumberOfProcessors;
  175.     }
  176.     HeapSize = dwInitialSize / dwParallelism;
  177.     //
  178.     // The first heap is special, since the MP_HEAP structure itself
  179.     // is allocated from there.
  180.     //
  181.     Heap = HeapCreate(flOptions,HeapSize,0);
  182.     if (Heap == NULL) {
  183.         //
  184.         // HeapCreate has already set last error appropriately.
  185.         //
  186.         return(NULL);
  187.     }
  188.     MpHeap = HeapAlloc(Heap,0,sizeof(MP_HEAP) +
  189.                               (dwParallelism-1)*sizeof(MP_HEAP_ENTRY));
  190.     if (MpHeap==NULL) {
  191.         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  192.         HeapDestroy(Heap);
  193.         return(NULL);
  194.     }
  195.     //
  196.     // Initialize the MP heap structure
  197.     //
  198.     MpHeap->HeapCount = 1;
  199.     MpHeap->Flags = PrivateFlags;
  200.     MpHeap->Hint = 0;
  201.     //
  202.     // Initialize the first heap
  203.     //
  204.     MpHeap->Entry[0].Heap = Heap;
  205.     InitializeCriticalSection(&MpHeap->Entry[0].Lock);
  206.     MpHeap->Entry[0].DelayedFreeList = NULL;
  207.     ZeroMemory(MpHeap->Entry[0].Lookaside, sizeof(MpHeap->Entry[0].Lookaside));
  208.     //
  209.     // Initialize the remaining heaps. Note that the heap has been
  210.     // sufficiently initialized to use MpHeapDestroy for cleanup
  211.     // if something bad happens.
  212.     //
  213.     for (i=1; i<dwParallelism; i++) {
  214.         MpHeap->Entry[i].Heap = HeapCreate(flOptions, HeapSize, 0);
  215.         if (MpHeap->Entry[i].Heap == NULL) {
  216.             Error = GetLastError();
  217.             MpHeapDestroy((HANDLE)MpHeap);
  218.             SetLastError(Error);
  219.             return(NULL);
  220.         }
  221.         InitializeCriticalSection(&MpHeap->Entry[i].Lock);
  222.         MpHeap->Entry[i].DelayedFreeList = NULL;
  223.         ZeroMemory(MpHeap->Entry[i].Lookaside, sizeof(MpHeap->Entry[i].Lookaside));
  224.         ++MpHeap->HeapCount;
  225.     }
  226.     return((HANDLE)MpHeap);
  227. }
  228. BOOL
  229. WINAPI
  230. MpHeapDestroy(
  231.     HANDLE hMpHeap
  232.     )
  233. {
  234.     DWORD i;
  235.     DWORD HeapCount;
  236.     PMP_HEAP MpHeap;
  237.     BOOL Success = TRUE;
  238.     MpHeap = (PMP_HEAP)hMpHeap;
  239.     HeapCount = MpHeap->HeapCount;
  240.     //
  241.     // Lock down all the heaps so we don't end up hosing people
  242.     // who may be allocating things while we are deleting the heaps.
  243.     // By setting MpHeap->HeapCount = 0 we also attempt to prevent
  244.     // people from getting hosed as soon as we delete the critical
  245.     // sections and heaps.
  246.     //
  247.     MpHeap->HeapCount = 0;
  248.     for (i=0; i<HeapCount; i++) {
  249.         EnterCriticalSection(&MpHeap->Entry[i].Lock);
  250.     }
  251.     //
  252.     // Delete the heaps and their associated critical sections.
  253.     // Note that the order is important here. Since the MpHeap
  254.     // structure was allocated from MpHeap->Heap[0] we must
  255.     // delete that last.
  256.     //
  257.     for (i=HeapCount-1; i>0; i--) {
  258.         DeleteCriticalSection(&MpHeap->Entry[i].Lock);
  259.         if (!HeapDestroy(MpHeap->Entry[i].Heap)) {
  260.             Success = FALSE;
  261.         }
  262.     }
  263.     return(Success);
  264. }
  265. BOOL
  266. WINAPI
  267. MpHeapValidate(
  268.     HANDLE hMpHeap,
  269.     LPVOID lpMem
  270.     )
  271. {
  272.     PMP_HEAP MpHeap;
  273.     DWORD i;
  274.     BOOL Success;
  275.     PMP_HEADER Header;
  276.     PMP_HEAP_ENTRY Entry;
  277.     MpHeap = (PMP_HEAP)hMpHeap;
  278.     if (lpMem == NULL) {
  279.         //
  280.         // Lock and validate each heap in turn.
  281.         //
  282.         for (i=0; i < MpHeap->HeapCount; i++) {
  283.             Entry = &MpHeap->Entry[i];
  284.             try {
  285.                 EnterCriticalSection(&Entry->Lock);
  286.                 Success = HeapValidate(Entry->Heap, 0, NULL);
  287.                 LeaveCriticalSection(&Entry->Lock);
  288.             } except (EXCEPTION_EXECUTE_HANDLER) {
  289.                 return(FALSE);
  290.             }
  291.             if (!Success) {
  292.                 return(FALSE);
  293.             }
  294.         }
  295.         return(TRUE);
  296.     } else {
  297.         //
  298.         // Lock and validate the given heap entry
  299.         //
  300.         Header = ((PMP_HEADER)lpMem) - 1;
  301.         try {
  302.             EnterCriticalSection(&Header->HeapEntry->Lock);
  303.             Success = HeapValidate(Header->HeapEntry->Heap, 0, Header);
  304.             LeaveCriticalSection(&Header->HeapEntry->Lock);
  305.         } except (EXCEPTION_EXECUTE_HANDLER) {
  306.             return(FALSE);
  307.         }
  308.         return(Success);
  309.     }
  310. }
  311. UINT
  312. WINAPI
  313. MpHeapCompact(
  314.     HANDLE hMpHeap
  315.     )
  316. {
  317.     PMP_HEAP MpHeap;
  318.     DWORD i;
  319.     DWORD LargestFreeSize=0;
  320.     DWORD FreeSize;
  321.     PMP_HEAP_ENTRY Entry;
  322.     MpHeap = (PMP_HEAP)hMpHeap;
  323.     //
  324.     // Lock and compact each heap in turn.
  325.     //
  326.     for (i=0; i < MpHeap->HeapCount; i++) {
  327.         Entry = &MpHeap->Entry[i];
  328.         EnterCriticalSection(&Entry->Lock);
  329.         FreeSize = HeapCompact(Entry->Heap, 0);
  330.         LeaveCriticalSection(&Entry->Lock);
  331.         if (FreeSize > LargestFreeSize) {
  332.             LargestFreeSize = FreeSize;
  333.         }
  334.     }
  335.     return(LargestFreeSize);
  336. }
  337. LPVOID
  338. WINAPI
  339. MpHeapAlloc(
  340.     HANDLE hMpHeap,
  341.     DWORD flOptions,
  342.     DWORD dwBytes
  343.     )
  344. {
  345.     PMP_HEADER Header;
  346.     PMP_HEAP MpHeap;
  347.     DWORD i;
  348.     PMP_HEAP_ENTRY Entry;
  349.     DWORD Index;
  350.     DWORD Size;
  351.     MpHeap = (PMP_HEAP)hMpHeap;
  352.     flOptions |= MpHeap->Flags;
  353.     Size = ((dwBytes + 7) & (ULONG)~7) + sizeof(MP_HEADER);
  354.     Index=LookasideIndexFromSize(Size);
  355.     //
  356.     // Iterate through the heap locks looking for one
  357.     // that is not owned.
  358.     //
  359.     i=HeapHint;
  360.     if (i>=MpHeap->HeapCount) {
  361.         i=0;
  362.         HeapHint=0;
  363.     }
  364.     Entry = &MpHeap->Entry[i];
  365.     do {
  366.         //
  367.         // Check the lookaside list for a suitable allocation.
  368.         //
  369.         if ((Index != NO_LOOKASIDE) &&
  370.             (Entry->Lookaside[Index].Entry != NULL)) {
  371.             if ((Header = (PMP_HEADER)InterlockedExchange((PLONG)&Entry->Lookaside[Index].Entry,
  372.                                                           (LONG)NULL)) != NULL) {
  373.                 //
  374.                 // We have a lookaside hit, return it immediately.
  375.                 //
  376.                 ++Entry->LookasideAllocations;
  377.                 if (flOptions & MPHEAP_ZERO_MEMORY) {
  378.                     ZeroMemory(Header + 1, dwBytes);
  379.                 }
  380.                 HeapHint=i;
  381.                 return(Header + 1);
  382.             }
  383.         }
  384.         //
  385.         // Attempt to lock this heap without blocking.
  386.         //
  387.         if (TryEnterCriticalSection(&Entry->Lock)) {
  388.             //
  389.             // success, go allocate immediately
  390.             //
  391.             goto LockAcquired;
  392.         }
  393.         //
  394.         // This heap is owned by another thread, try
  395.         // the next one.
  396.         //
  397.         i++;
  398.         Entry++;
  399.         if (i==MpHeap->HeapCount) {
  400.             i=0;
  401.             Entry=&MpHeap->Entry[0];
  402.         }
  403.     } while ( i != HeapHint );
  404.     //
  405.     // All of the critical sections were owned by someone else,
  406.     // so we have no choice but to wait for a critical section.
  407.     //
  408.     EnterCriticalSection(&Entry->Lock);
  409. LockAcquired:
  410.     ++Entry->Allocations;
  411.     if (Entry->DelayedFreeList != NULL) {
  412.         ProcessDelayedFreeList(Entry);
  413.     }
  414.     Header = HeapAlloc(Entry->Heap, 0, Size);
  415.     LeaveCriticalSection(&Entry->Lock);
  416.     if (Header != NULL) {
  417.         Header->HeapEntry = Entry;
  418.         Header->LookasideIndex = Index;
  419.         if (flOptions & MPHEAP_ZERO_MEMORY) {
  420.             ZeroMemory(Header + 1, dwBytes);
  421.         }
  422.         HeapHint = i;
  423.         return(Header + 1);
  424.     } else {
  425.         return(NULL);
  426.     }
  427. }
  428. LPVOID
  429. WINAPI
  430. MpHeapReAlloc(
  431.     HANDLE hMpHeap,
  432.     LPVOID lpMem,
  433.     DWORD dwBytes
  434.     )
  435. {
  436.     PMP_HEADER Header;
  437.     PCRITICAL_SECTION Lock;
  438.     Header = ((PMP_HEADER)lpMem) - 1;
  439.     Lock = &Header->HeapEntry->Lock;
  440.     dwBytes = ((dwBytes + 7) & (ULONG)~7) + sizeof(MP_HEADER);
  441.     EnterCriticalSection(Lock);
  442.     Header = HeapReAlloc(Header->HeapEntry->Heap, 0, Header, dwBytes);
  443.     LeaveCriticalSection(Lock);
  444.     if (Header != NULL) {
  445.         Header->LookasideIndex = LookasideIndexFromSize(dwBytes);
  446.         return(Header + 1);
  447.     } else {
  448.         return(NULL);
  449.     }
  450. }
  451. BOOL
  452. WINAPI
  453. MpHeapFree(
  454.     HANDLE hMpHeap,
  455.     LPVOID lpMem
  456.     )
  457. {
  458.     PMP_HEADER Header;
  459.     PCRITICAL_SECTION Lock;
  460.     BOOL Success;
  461.     PMP_HEAP_ENTRY HeapEntry;
  462.     PSINGLE_LIST_ENTRY Next;
  463.     PMP_HEAP MpHeap;
  464.     Header = ((PMP_HEADER)lpMem) - 1;
  465.     HeapEntry = Header->HeapEntry;
  466.     MpHeap = (PMP_HEAP)hMpHeap;
  467.     HeapHint = HeapEntry - &MpHeap->Entry[0];
  468.     if (Header->LookasideIndex != NO_LOOKASIDE) {
  469.         //
  470.         // Try and put this back on the lookaside list
  471.         //
  472.         if (InterlockedCompareExchange((PVOID *)&HeapEntry->Lookaside[Header->LookasideIndex],
  473.                                        (PVOID)Header,
  474.                                        NULL) == NULL) {
  475.             //
  476.             // Successfully freed to lookaside list.
  477.             //
  478.             ++HeapEntry->LookasideFrees;
  479.             return(TRUE);
  480.         }
  481.     }
  482.     Lock = &HeapEntry->Lock;
  483.     if (TryEnterCriticalSection(Lock)) {
  484.         ++HeapEntry->Frees;
  485.         Success = HeapFree(HeapEntry->Heap, 0, Header);
  486.         LeaveCriticalSection(Lock);
  487.         return(Success);
  488.     }
  489.     //
  490.     // The necessary heap critical section could not be immediately
  491.     // acquired. Post this free onto the Delayed free list and let
  492.     // whoever has the lock process it.
  493.     //
  494.     do {
  495.         Next = HeapEntry->DelayedFreeList;
  496.         Header->Next = Next;
  497.     } while ( InterlockedCompareExchange(&HeapEntry->DelayedFreeList,
  498.                                          &Header->Next,
  499.                                          Next) != Next);
  500.     return(TRUE);
  501. }
  502. VOID
  503. ProcessDelayedFreeList(
  504.     IN PMP_HEAP_ENTRY HeapEntry
  505.     )
  506. {
  507.     PSINGLE_LIST_ENTRY FreeList;
  508.     PSINGLE_LIST_ENTRY Next;
  509.     PMP_HEADER Header;
  510.     //
  511.     // Capture the entire delayed free list with a single interlocked exchange.
  512.     // Once we have removed the entire list, free each entry in turn.
  513.     //
  514.     FreeList = (PSINGLE_LIST_ENTRY)InterlockedExchange((LPLONG)&HeapEntry->DelayedFreeList, (LONG)NULL);
  515.     while (FreeList != NULL) {
  516.         Next = FreeList->Next;
  517.         Header = CONTAINING_RECORD(FreeList, MP_HEADER, Next);
  518.         ++HeapEntry->DelayedFrees;
  519.         HeapFree(HeapEntry->Heap, 0, Header);
  520.         FreeList = Next;
  521.     }
  522. }
  523. DWORD
  524. MpHeapGetStatistics(
  525.     HANDLE hMpHeap,
  526.     LPDWORD lpdwSize,
  527.     MPHEAP_STATISTICS Stats[]
  528.     )
  529. {
  530.     PMP_HEAP MpHeap;
  531.     PMP_HEAP_ENTRY Entry;
  532.     DWORD i;
  533.     DWORD RequiredSize;
  534.     MpHeap = (PMP_HEAP)hMpHeap;
  535.     RequiredSize = MpHeap->HeapCount * sizeof(MPHEAP_STATISTICS);
  536.     if (*lpdwSize < RequiredSize) {
  537.         *lpdwSize = RequiredSize;
  538.         return(ERROR_MORE_DATA);
  539.     }
  540.     ZeroMemory(Stats, MpHeap->HeapCount * sizeof(MPHEAP_STATISTICS));
  541.     for (i=0; i < MpHeap->HeapCount; i++) {
  542.         Entry = &MpHeap->Entry[i];
  543.         Stats[i].Contention = Entry->Lock.DebugInfo->ContentionCount;
  544.         Stats[i].TotalAllocates = (Entry->Allocations + Entry->LookasideAllocations);
  545.         Stats[i].TotalFrees = (Entry->Frees + Entry->LookasideFrees + Entry->DelayedFrees);
  546.         Stats[i].LookasideAllocates = Entry->LookasideAllocations;
  547.         Stats[i].LookasideFrees = Entry->LookasideFrees;
  548.         Stats[i].DelayedFrees = Entry->DelayedFrees;
  549.     }
  550.     *lpdwSize = RequiredSize;
  551.     return(ERROR_SUCCESS);
  552. }