custom stl allocators

Download Custom STL Allocators

Post on 31-Dec-2015




1 download

Embed Size (px)


Custom STL Allocators. Pete Isensee Xbox Advanced Technology Group Topics. Allocators: What are They Good For? Writing Your First Allocator The Devil in the Details Allocator Pitfalls State Syntax Testing Case Study. Containers and Allocators. - PowerPoint PPT Presentation


  • Custom STL AllocatorsPete IsenseeXbox Advanced Technology

  • TopicsAllocators: What are They Good For?Writing Your First AllocatorThe Devil in the DetailsAllocator PitfallsStateSyntaxTestingCase Study

  • Containers and AllocatorsSTL containers allocate memorye.g. vector (contiguous), list (nodes)string is a container, for this talkAllocators provide a standard interface for container memory useIf you dont provide an allocator, one is provided for you

  • ExampleDefault Allocatorlist b;// same as:list< int, allocator > b;Custom Allocator#include MyAlloc.hlist< int, MyAlloc > c;

  • The GoodOriginal idea: abstract the notion of near and far memory pointersExpanded idea: allow customization of container allocationGood forSize: Optimizing memory usage (pools, fixed-size allocators)Speed: Reducing allocation time (single-threaded, one-time free)

  • Example AllocatorsNo heap locking (single thread)Avoiding fragmentationAligned allocations (_aligned_malloc)Fixed-size allocationsCustom free listDebuggingCustom heapSpecific memory type

  • The BadNo realloc()Requires advanced C++ compilersC++ Standard hand-wavingGenerally library-specificIf you change STL libraries you may need to rewrite allocatorsGenerally not cross-platformIf you change compilers you may need to rewrite allocators

  • The UglyNot quite real objectsAllocators with state may not work as expectedGnarly syntaxmap m;map m;

  • Pause to ReflectPremature optimization is the root of all evil Donald KnuthAllocators are a last resort and low-level optimizationEspecially for games, allocators can be the perfect optimizationWritten correctly, they can be introduced w/o many code changes

  • Writing Your First AllocatorCreate MyAlloc.h#include Copy or derive from the default allocatorRename allocator to MyAllocResolve any helper functionsReplace some code with your own

  • Writing Your First AllocatorDemoVisual C++ Pro 7.0 (13.00.9466)Dinkumware STL (V3.10:0009)933MHz PIII w/ 512MBWindows XP Pro 2002Launch Visual Studio

  • Two key functionsAllocateDeallocateThats all!

  • Conventionstemplate< typename T >class allocator{ typedef size_t size_type; typedef T* pointer; typedef const T* const_pointer; typedef T value_type;};

  • Allocate Functionpointer allocate( size_type n, allocator::const_pointer p = 0)n is the number of items T, NOT bytesreturns pointer to enough memory to hold n * sizeof(T) bytesreturns raw bytes; NO constructionmay throw an exception (std::bad_alloc)default calls ::operator newp is optional hint; avoid

  • Deallocate functionvoid deallocate( pointer p, size_type n )p must come from allocate()p must be raw bytes; already destroyedn must match the n passed to allocate()default calls ::operator delete(void*)Most implementations allow and ignore NULL p; you should too

  • A Custom AllocatorDemoThats it!Not quite: the devil is in the detailsConstructionDestructionExample STL container codeRebind

  • ConstructionAllocate() doesnt call constructorsWhy? PerformanceAllocators provide construct function void construct(pointer p, const T& t) { new( (void*)p ) T(t); }Placement newDoesnt allocate memoryCalls copy constructor

  • DestructionDeallocate() doesnt call destructorsAllocators provide a destroy function void destroy( pointer p ){ ((T*)p)->~T(); }Direct destructor invocationDoesnt deallocate memoryCalls destructor

  • Example: Vectortemplate< typename T, typename A >class vector { A a; // allocator pointer pFirst; // first object pointer pEnd; // 1 beyond end pointer pLast; // 1 beyond last};

  • Example: Reservevector::reserve( size_type n ){ pointer p = a.allocate( n, 0 ); // loop on a.construct() to copy // loop on a.destroy() to tear down a.deallocate( pFirst, capacity() ); pFirst = p; pLast = p + size(); pEnd = p + n;}

  • Performance is paramountReserveSingle allocationDoesnt default construct anythingDeals properly with real objectsNo memcpyCopy constructs new objectsDestroys old objectsSingle deallocation

  • RebindAllocators dont always allocate Tlist ObjList; // allocates nodesHow? Rebindtemplate struct rebind{ typedef allocator other; }To allocate an N given type TAlloc a;T* t = a.allocate(1); // allocs sizeof(T)Alloc::rebind::other na;N* n = na.allocate(1); // allocs sizeof(N)

  • Allocator PitfallsTo Derive or Not to DeriveStateCopy ctor and template copy ctorAllocator comparisonSyntax issuesTestingCase Study

  • To Derive or Not To DeriveDeriving from std::allocatorDinkumware derives (see )Must provide rebind, allocate, deallocateLess code; easier to see differencesWriting from scratchAllocator not designed as base classJosuttis and Austern write from scratchBetter understandingPersonal preference

  • Allocators with StateState = allocator member dataDefault allocator has no dataC++ Std says (paraphrasing 20.1.5):Vendors encouraged to support allocators with stateContainers may assume that allocators dont have state

  • State RecommendationsBe aware of compatibility issues across STL vendorslist::splice() or C::swap()will indicate if your vendor supports stateful allocatorsDinkumware: yesSTLport: noTest carefully

  • State ImplicationsContainer size increaseMust provide allocator:Constructor(s)Default may be private if parameters required Copy constructorTemplate copy constructorGlobal comparison operators (==, !=)No assignment operators requiredAvoid static data; generates one per T

  • Heap Allocator Exampletemplate< typename T >class Halloc { Halloc(); // could be private explicit Halloc( HANDLE hHeap ); Halloc( const Halloc& ); // copy template< typename U > // templatized Halloc( const Halloc& ); // copy};

  • Template Copy ConstructorCant see private datatemplate< typename U >Halloc( const Halloc& a ) : m_hHeap( a.m_hHeap ) {} // errorSolutionsProvide public data accessor functionOr allow access to other types Utemplate friend class Halloc;

  • Allocator comparisonExampletemplate< typename T, typename U >bool operator==( const Alloc& a, const Alloc& b ){ return a.state == b.state; }Provide both == and !=Should be global fucns, not membersMay require accessor functions

  • Syntax: TypedefsPrefer typedefsOffensivelist< int, Alloc< int > > b;Better// .htypedef Alloc< int > IAlloc;typedef list< int, IAlloc > IntList;// .cppIntList v;

  • Syntax: ConstructionContainers accept allocators via ctorsIntList b( IAlloc( x,y,z ) );If none specified, you get the defaultIntList b; // calls IAlloc()Map/multimap requires pairsAlloc< pair< K,T > > a;map< K, T, less, Alloc< pair< K,T > > > m( less(), a );

  • Syntax: Other ContainersContainer adaptors accept containers via constructors, not allocatorsAlloc a;deque< T, Alloc > d(a);stack< T, deque > s(d);String exampleAlloc a;basic_string< T, char_traits, Alloc > s(a);

  • TestingTest the normal caseTest with all containers (dont forget string, hash containers, stack, etc.)Test with different objects T, particularly those w/ non-trivial dtorsTest edge cases like list::spliceVerify that your version is better!Allocator test framework:

  • Case StudyIn-place allocatorHand off existing memory blockDole out allocations from the blockNever freeExample usagetypedef InPlaceAlloc< int > IPA;void* p = malloc( 1024 );list< int, IPA > x( IPA( p, 1024 ) );x.push_back( 1 );free( p ); View code

  • In-Place AllocatorProblemsFails w/ multiple concurrent copiesNo copy constructorDidnt support comparisonDidnt handle containers of void*Correct implementationReference countedCopy constructor implementedComparison operatorsVoid specialization

  • In-Place SummarySpeedScenario: add x elements, remove halfAbout 50x faster than default allocator!AdvantagesFast; no overhead; no fragmentationWhatever memory you wantDisadvantagesProper implementation isnt easyLimited use

  • RecommendationsAllocators: a last resort optimizationBase your allocator on Beware porting issues (both compilers and STL vendor libraries)Beware allocators with stateTest thoroughlyVerify speed/size improvements

  • Recommendations part IIUse typedefs to simplify lifeDont forget to writeRebindCopy constructorTemplatized copy constructorComparison operatorsVoid specialization

  • ReferencesC++ Standard section 20.1.5, 20.4.1Your STL implementation: GDC Proceedings: References sectionGame Gems

    Allocators provide a level of indirectionImplies that containers contain allocator objects as member data (or derive from allocators)Implementers are encouraged to supply libraries that can accept allocators that encapsulate more general memory models.... In such implementations, any [additional] requirements... are implementation-defined []Allocators rely on: member templates, partial specialization, partial ordering of function templates, the typename keywordProfile firstPrefer changing your algorithmPrefer changing data structuresMake sure that allocation is the bottleneck

    Not covering exception handlingCool party trickBasically acts as a template typedef, which is not currently supported by the langaugeUse my testing