/*++ Copyright (C) Microsoft Corporation, 1997 - 1999 Module Name: bcache.hxx Abstract: RPC's buffer cache class Author: Mario Goertzel [MarioGo] Revision History: MarioGo 9/25/1997 Bits 'n pieces --*/ /*++ Copyright (C) Microsoft Corporation, 1997 - 1999 Module Name: bcache.hxx Abstract: Cached buffer allocation class Author: Mario Goertzel [MarioGo] Revision History: MarioGo 9/7/1997 Bits 'n pieces --*/ #ifndef __BCACHE_HXX #define __BCACHE_HXX // // The RPC buffer cache uses several levels of caching to improve // performance. The cache has either 2 or 4 fixed buffer sizes that // it caches. The first level of cache is per-thread. The second // level is process wide. // // Their are two performance goals: make a single alloc/free loop // execute with a minimum of cycles and scale well on MP machines. // This implementation can do 8000000 allocs in 761ms using 8 threads // on a 4xPPro 200. // // In the default mode, elements which are the right size for various // runtime allocations as cached. // // In paged bcache mode (used for testing) 1 and 2 page buffers are cached. // In this mode we allocate each buffer at the end of the page, and we // add a read only page after that. This allows NDR to temporarily read // past the end of the buffer without raising exceptions, but writes // will AV. That's we we can't use paged heap BTW - we need a page // after the buffer with RO access, not without any access. // struct BUFFER_CACHE_HINTS { // When the thread cache is empty, this many blocks are moved // from the global cache (if possible) to the thread cache. // When freeing from the thread cache, this many buffers // are left in the thread cache. UINT cLowWatermark; // When per thread cache will reach mark due to a free, blocks // will be moved to the global cache. UINT cHighWatermark; // Summary: The difference between high and low is the number // of blocks allocated/freed from the global cache at a time. // // Lowwater should be the average number of free buffers - 1 // you expect a thread to have. Highwater should be the // maximum number of free buffers + 1 you expect a thread // to need. // // **** Note: The difference must be two or more. *** // Example: 1 3 // Alloc called with the thread cache empty, two blocks are removed // from the global list. One is saved the thread list. The other // is returned. // // Free called with two free buffers in the thread list. A total // of three buffers. Two buffers are moved to the global cache, one // stays in the thread cache. // // // The size of the buffers UINT cSize; }; extern CONST BUFFER_CACHE_HINTS gCacheHints[4]; extern BUFFER_CACHE_HINTS gPagedBCacheHints[4]; extern BUFFER_CACHE_HINTS *pHints; struct PAGED_BCACHE_SECTION_MANAGER { ULONG NumberOfSegments; ULONG NumberOfUsedSegments; ULONG SegmentSize; // doesn't include guard page. In bytes. I.e. size // of read-write committed segment void *VirtualMemorySection; LIST_ENTRY SectionList; // all sections are chained. This both makes leak // tracking easier and it allows us to maintain // good locality by allocating off the first section // first. BOOLEAN SegmentBusy[1]; // actually the array size is the number of segments // we use boolean as arbitrary tradeoff b/n speed (ULONG) // and size (true bit vector). }; // Used in all modes, sits at the front of the buffer allocation. struct BUFFER_HEAD { union { BUFFER_HEAD *pNext; // Valid only in free lists INT index; // Valid only when allocated // 1-4 for cachable, -1 for big }; union { // Used in paged bcache mode only. SIZE_T size; // if index == -1, this is the size. Used only // for debugging. PAGED_BCACHE_SECTION_MANAGER *SectionManager; // points to a small heap block // containing control information }; }; typedef BUFFER_HEAD *PBUFFER; // This structure is imbedded into the RPC thread object struct BCACHE_STATE { BUFFER_HEAD *pList; ULONG cBlocks; }; // The strucutre parrallels the global cache struct BCACHE_STATS { UINT cBufferCacheCap; UINT cAllocationHits; UINT cAllocationMisses; }; class THREAD; class BCACHE { private: BCACHE_STATE _bcGlobalState[4]; BCACHE_STATS _bcGlobalStats[4]; MUTEX _csBufferCacheLock; LIST_ENTRY Sections; // used in guard page mode only PVOID AllocHelper(size_t, INT, BCACHE_STATE *); VOID FreeHelper(PVOID, INT, BCACHE_STATE *); VOID FreeBuffers(PBUFFER, INT, UINT); PBUFFER AllocBigBlock(IN size_t); VOID FreeBigBlock(IN PBUFFER); PBUFFER BCACHE::AllocPagedBCacheSection ( IN UINT size, IN ULONG OriginalSize ); ULONG GetSegmentIndexFromBuffer ( IN PBUFFER pBuffer, IN PVOID Allocation ); #if DBG void VerifyPagedBCacheState ( void ); void VerifySectionState ( IN PAGED_BCACHE_SECTION_MANAGER *Section ); void VerifySegmentState ( IN PVOID Segment, IN BOOL IsSegmentBusy, IN ULONG SegmentSize, IN PAGED_BCACHE_SECTION_MANAGER *OwningSection ); #endif PVOID PutBufferAtEndOfAllocation ( IN PVOID Allocation, IN ULONG AllocationSize, IN ULONG BufferSize ); PVOID ConvertBufferToAllocation ( IN PBUFFER Buffer, IN BOOL IsBufferInitialized ); PVOID CommitSegment ( IN PVOID SegmentStart, IN ULONG SegmentSize ); public: BCACHE(RPC_STATUS &); ~BCACHE(); PVOID Allocate(CONST size_t cSize); VOID Free(PVOID); VOID ThreadDetach(THREAD *); }; extern BCACHE *gBufferCache; // Helper APIs inline PVOID RpcAllocateBuffer(CONST size_t cSize) { return(gBufferCache->Allocate(cSize)); } inline VOID RpcFreeBuffer(PVOID pBuffer) { if (pBuffer == 0) { return; } gBufferCache->Free(pBuffer); } #endif // __BCACHE_HXX