{"id":21274173,"url":"https://github.com/endurodave/stl_allocator","last_synced_at":"2025-07-11T06:33:52.982Z","repository":{"id":113550036,"uuid":"108659943","full_name":"endurodave/stl_allocator","owner":"endurodave","description":"STL std::allocator Fixed Block Memory Allocator","archived":false,"fork":false,"pushed_at":"2024-11-02T21:07:38.000Z","size":28,"stargazers_count":7,"open_issues_count":0,"forks_count":4,"subscribers_count":0,"default_branch":"master","last_synced_at":"2024-11-02T21:25:55.788Z","etag":null,"topics":["cpp","embedded-systems","memory-allocation","stl","stl-containers"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/endurodave.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-10-28T15:18:57.000Z","updated_at":"2024-11-02T21:07:42.000Z","dependencies_parsed_at":"2023-03-13T13:18:59.787Z","dependency_job_id":null,"html_url":"https://github.com/endurodave/stl_allocator","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2Fstl_allocator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2Fstl_allocator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2Fstl_allocator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/endurodave%2Fstl_allocator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/endurodave","download_url":"https://codeload.github.com/endurodave/stl_allocator/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225701555,"owners_count":17510556,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp","embedded-systems","memory-allocation","stl","stl-containers"],"created_at":"2024-11-21T09:19:14.059Z","updated_at":"2025-07-11T06:33:52.976Z","avatar_url":"https://github.com/endurodave.png","language":"C++","readme":"![License MIT](https://img.shields.io/github/license/BehaviorTree/BehaviorTree.CPP?color=blue)\n[![conan Ubuntu](https://github.com/endurodave/stl_allocator/actions/workflows/cmake_ubuntu.yml/badge.svg)](https://github.com/endurodave/stl_allocator/actions/workflows/cmake_ubuntu.yml)\n[![conan Ubuntu](https://github.com/endurodave/stl_allocator/actions/workflows/cmake_clang.yml/badge.svg)](https://github.com/endurodave/stl_allocator/actions/workflows/cmake_clang.yml)\n[![conan Windows](https://github.com/endurodave/stl_allocator/actions/workflows/cmake_windows.yml/badge.svg)](https://github.com/endurodave/stl_allocator/actions/workflows/cmake_windows.yml)\n\n# A Custom STL std::allocator Replacement Improves Performance\n\nProtect against heap fragmentation faults and improve execution speed with a fixed block alternative to STL std::allocator.\n\nOriginally published on CodeProject at: \u003ca href=\"https://www.codeproject.com/Articles/1089905/A-Custom-STL-std-allocator-Replacement-Improves-Pe\"\u003e\u003cstrong\u003eA Custom STL std::allocator Replacement Improves Performance\u003c/strong\u003e\u003c/a\u003e\n\n# Table of Contents\n\n- [A Custom STL std::allocator Replacement Improves Performance](#a-custom-stl-stdallocator-replacement-improves-performance)\n- [Table of Contents](#table-of-contents)\n- [Getting Started](#getting-started)\n- [Introduction](#introduction)\n- [std::allocator](#stdallocator)\n- [xallocator    ](#xallocator-)\n- [stl\\_allocator](#stl_allocator)\n  - [\"x\" Containers](#x-containers)\n  - [\"x\" Strings](#x-strings)\n  - [\"x\" Streams](#x-streams)\n- [Benchmarking](#benchmarking)\n- [Reference Articles](#reference-articles)\n- [Benefits](#benefits)\n\n\n# Getting Started\n\n[CMake](https://cmake.org/) is used to create the project build files on any C++ system including Windows, Linux, or an embedded system. \n\n1. Clone the repository.\n2. From the repository root, run the following CMake command:   \n   `cmake -B Build .`\n3. Build and run the project within the `Build` directory. \n\n# Introduction\n\n\u003cp\u003eThis is my third and final article concerning fixed block memory allocators here on Code Project. This time, we\u0026#39;ll create an alternate C++ Standard Library \u003ccode\u003estd::allocator\u003c/code\u003e memory manager using the groundwork laid by the first two articles.\u0026nbsp;\u003c/p\u003e\n\n\u003cp\u003eThe Standard Template Library (STL) is powerful C++ software library including container and iterator support. The problem with using the library for mission-critical or time-critical projects isn\u0026#39;t with STL itself \u0026ndash; the library is very robust. Instead, it\u0026#39;s with the unrestricted use of the global heap. The standard STL allocator uses the heap extensively during operation. This is a problem on resource constrained embedded systems. Embedded devices can be expected to operate for months or years where a fault caused by a fragmented heap must be prevented. Fortunately, STL provides a means to replace \u003ccode\u003estd::allocator\u003c/code\u003e with one of our own design.\u0026nbsp;\u003c/p\u003e\n\n\u003cp\u003eFixed block memory allocators are a common technique to provide dynamic-like operation yet the memory is provided from memory pools where the blocks doled are of a fixed size. Unlike the global heap, which is expected to universally operate on blocks of any size, a fixed block allocator can be tailored for a narrowly defined purpose. Fixed block allocators can also offer consistent execution times whereas the global heap cannot offer such guarantees.\u0026nbsp;\u003c/p\u003e\n\n\u003cp\u003eThis article describes an STL-compatible allocator implementation that relies upon a fixed block allocator for dispensing and reclaiming memory. The new allocator prevents faults caused by a fragmented heap and offer consistent allocation/deallocation execution times.\u0026nbsp;\u003c/p\u003e\n\n# std::allocator\n\n\u003cp\u003eThe STL \u003ccode\u003estd::allocator\u003c/code\u003e class provides the default memory allocation and deallocation strategy. If you examine code for a container class, such as \u003ccode\u003estd::list\u003c/code\u003e, you\u0026#39;ll see the default \u003ccode\u003estd::allocator\u003c/code\u003e template argument. In this case, \u003ccode\u003eallocator\u0026lt;_Ty\u0026gt;\u003c/code\u003e is template class handling allocation duties for \u003ccode\u003e_Ty\u003c/code\u003e objects.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\ntemplate\u0026lt;class _Ty, class _Ax = allocator\u0026lt;_Ty\u0026gt; \u0026gt;\nclass list : public _List_val\u0026lt;_Ty, _Ax\u0026gt;\n{\n    // ...\n}\u003c/pre\u003e\n\n\u003cp\u003eSince the template parameter \u003ccode\u003e_Ax\u003c/code\u003e defaults to \u003ccode\u003eallocator\u0026lt;_Ty\u003c/code\u003e\u0026gt; you can create a list object without manually specifying an allocator. Declaring \u003ccode\u003emyList \u003c/code\u003eas shown below creates an allocator class for allocating/deallocating \u003ccode\u003eint \u003c/code\u003evalues.\u0026nbsp;\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\nstd::list\u0026lt;int\u0026gt; myList;\u003c/pre\u003e\n\n\u003cp\u003eThe STL containers rely upon dynamic memory for elements and nodes. An element is the size of an inserted object. In this case, \u003ccode\u003esizeof(int)\u003c/code\u003e is the memory required to store one list element. A node is an internal structure required to bind the elements together. For \u003ccode\u003estd::list\u003c/code\u003e, it is a doubly-linked list storing, at a minimum, pointers to the next and previous nodes.\u0026nbsp;\u003c/p\u003e\n\n\u003cp\u003eOf course, the element size varies depending on the objects stored. However, the node size varies too depending on the container used. A \u003ccode\u003estd::list\u003c/code\u003e node may have a different size than a \u003ccode\u003estd::map\u003c/code\u003e node. Because of this, a STL-allocator must be able to handle different sized memory block requests.\u0026nbsp;\u003c/p\u003e\n\n\u003cp\u003eAn STL-allocator must adhere to specific interface requirements. This isn\u0026#39;t an article on the how\u0026#39;s and why\u0026#39;s of the \u003ccode\u003estd::allocator\u003c/code\u003e API \u0026ndash; there are many online references that explain this better than I. Instead, I\u0026#39;ll focus on where to place the memory allocation/deallocation calls within an existing STL-allocator class interface and provide new \u0026quot;x\u0026quot; versions of all common containers to simplify usage.\u0026nbsp;\u003c/p\u003e\n\n# xallocator\u0026nbsp;\u0026nbsp; \u0026nbsp;\n\n\u003cp\u003eMost of the heavy lifting for the new fixed block STL allocator comes from the underlying \u003ccode\u003exallocator\u003c/code\u003e as described within my article \u0026quot;\u003ca href=\"https://github.com/endurodave/xallocator\"\u003e\u003cstrong\u003eReplace malloc/free with a Fast Fixed Block Memory Allocator\u003c/strong\u003e\u003c/a\u003e\u0026quot;. As the title states, this module replaces \u003ccode\u003emalloc\u003c/code\u003e/\u003ccode\u003efree \u003c/code\u003ewith new fixed block \u003ccode\u003exmalloc\u003c/code\u003e/\u003ccode\u003exfree\u003c/code\u003e versions.\u0026nbsp;\u003c/p\u003e\n\n\u003cp\u003eTo the user, these replacement functions operate in the same way as the standard CRT versions except for the fixed block feature. In short, \u003ccode\u003exallocator\u003c/code\u003e has two modes of operation: \u003cem\u003estatic pools\u003c/em\u003e, where all memory is obtained from pre-declared static memory, or \u003cem\u003eheap blocks\u003c/em\u003e, where blocks are obtained from the global heap but recycled for later use when freed. See the aforementioned article for implementation details.\u0026nbsp;\u003c/p\u003e\n\n# stl_allocator\n\n\u003cp\u003eThe class \u003ccode\u003estl_allocator \u003c/code\u003eis the fixed block STL-compatible implementation. This class is used as an alternative to \u003ccode\u003estd::allocator\u003c/code\u003e.\u0026nbsp;\u003c/p\u003e\n\n```cpp\n#ifndef _STL_ALLOCATOR_H\n#define _STL_ALLOCATOR_H\n\n// @see https://github.com/endurodave/stl_allocator\n// David Lafreniere\n\n#include \"xallocator.h\"\n#include \u003cmemory\u003e  // For std::allocator and std::allocator_traits\n\n// Forward declaration for stl_allocator\u003cvoid\u003e\ntemplate \u003ctypename T\u003e\nclass stl_allocator;\n\n// Specialization for `void`, but we no longer need to define `pointer` and `const_pointer`\ntemplate \u003c\u003e\nclass stl_allocator\u003cvoid\u003e \n{\npublic:\n\ttypedef void value_type;\n\n\ttemplate \u003cclass U\u003e\n\tstruct rebind { typedef stl_allocator\u003cU\u003e other; };\n};\n\n// Define the custom stl_allocator inheriting from std::allocator\ntemplate \u003ctypename T\u003e\nclass stl_allocator : public std::allocator\u003cT\u003e \n{\npublic:\n\ttypedef size_t size_type;\n\ttypedef ptrdiff_t difference_type;\n\ttypedef T* pointer;\n\ttypedef const T* const_pointer;\n\ttypedef T\u0026 reference;\n\ttypedef const T\u0026 const_reference;\n\ttypedef T value_type;\n\n\t// Default constructor\n\tstl_allocator() {}\n\n\t// Copy constructor\n\ttemplate \u003cclass U\u003e\n\tstl_allocator(const stl_allocator\u003cU\u003e\u0026) {}\n\n\t// Rebind struct\n\ttemplate \u003cclass U\u003e\n\tstruct rebind { typedef stl_allocator\u003cU\u003e other; };\n\n\t// Override allocate method to use custom allocation function\n\tpointer allocate(size_type n, typename std::allocator_traits\u003cstl_allocator\u003cvoid\u003e\u003e::const_pointer hint = 0) \n\t{\n\t\treturn static_cast\u003cpointer\u003e(xmalloc(n * sizeof(T)));\n\t}\n\n\t// Override deallocate method to use custom deallocation function\n\tvoid deallocate(pointer p, size_type n) \n\t{\n\t\txfree(p);\n\t}\n\n\t// You can inherit other methods like construct and destroy from std::allocator\n};\n\n// Comparison operators for compatibility\ntemplate \u003ctypename T, typename U\u003e\ninline bool operator==(const stl_allocator\u003cT\u003e\u0026, const stl_allocator\u003cU\u003e) { return true; }\n\ntemplate \u003ctypename T, typename U\u003e\ninline bool operator!=(const stl_allocator\u003cT\u003e\u0026, const stl_allocator\u003cU\u003e) { return false; }\n\n#endif \n```\n\n\u003cp\u003eThe code is really just a standard \u003ccode\u003estd::allocator\u003c/code\u003e interface. There are many examples online. The source attached to this article has been used on many different compilers (GCC, Keil, VisualStudio). The thing we\u0026#39;re interested in is where to tap into the interface with our own memory manager. The methods of interest are:\u003c/p\u003e\n\n\u003cul\u003e\n\t\u003cli\u003e\u003ccode\u003eallocate()\u003c/code\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ccode\u003edeallocate()\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\n\u003cp\u003e\u003ccode\u003eallocate()\u003c/code\u003e allocates \u003ccode\u003en\u003c/code\u003e number of instances of object type \u003ccode\u003eT\u003c/code\u003e. \u003ccode\u003exmalloc()\u003c/code\u003e is used to obtained memory from the fixed block memory pool as opposed to the global heap.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\npointer allocate(size_type n, stl_allocator\u0026lt;void\u0026gt;::const_pointer hint = 0)\n{\n    return static_cast\u0026lt;pointer\u0026gt;(xmalloc(n*sizeof(T)));\n}\u003c/pre\u003e\n\n\u003cp\u003e\u003ccode\u003edeallocate()\u003c/code\u003e deallocates a memory block previously allocated using \u003ccode\u003eallocate()\u003c/code\u003e. A simple call to \u003ccode\u003exfree()\u003c/code\u003e routes the request to our memory manager.\u0026nbsp;\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\nvoid deallocate(pointer p, size_type n)\n{\n    xfree(p);\n}\u003c/pre\u003e\n\n\u003cp\u003eReally, that\u0026#39;s all there is to it once you have an fixed block memory manager. \u003ccode\u003exallocator\u003c/code\u003e is designed to handle any size memory request. Therefore, no matter storage size called for by the C++ Standard Library, elements or nodes, \u003ccode\u003exmalloc\u003c/code\u003e/\u003ccode\u003exfree\u003c/code\u003e processes the memory request.\u003c/p\u003e\n\n\u003cp\u003eOf course, \u003ccode\u003estl_allocator\u003c/code\u003e is a template class. Notice that the fixed block allocation duties are delegated to non-template functions \u003ccode\u003exmalloc()\u003c/code\u003e and \u003ccode\u003exfree()\u003c/code\u003e. This makes the instantiated code for each instance as small as possible.\u0026nbsp;\u003c/p\u003e\n\n## \u0026quot;x\u0026quot; Containers\n\n\u003cp\u003eThe following STL container classes use fixed sized memory blocks that never change in size while the container is being utilized. The number of heap element/node blocks goes up and down, but block sizes are constant for a given container instantiation.\u003c/p\u003e\n\n\u003cul\u003e\n\t\u003cli\u003e\u003ccode\u003estd::list\u003c/code\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ccode\u003estd::map\u003c/code\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ccode\u003estd::multipmap\u003c/code\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ccode\u003estd::set\u003c/code\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ccode\u003estd::multiset\u003c/code\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ccode\u003estd::queue\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\n\u003cp\u003eTo make using \u003ccode\u003estl_allocator\u003c/code\u003e a bit easier, new classes were created for many of the standard container types. Each new container just inherits from the standard library counterpart and is pre-pended with \u0026quot;\u003ccode\u003ex\u003c/code\u003e\u0026quot;.\u0026nbsp;\u003c/p\u003e\n\n\u003cul\u003e\n\t\u003cli\u003e\u003ccode\u003exlist\u003c/code\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ccode\u003exmap\u003c/code\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ccode\u003exmultimap\u003c/code\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ccode\u003exset\u003c/code\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ccode\u003exmultiset\u003c/code\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ccode\u003exqueue\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\n\u003cp\u003eThe following code shows the complete \u003ccode\u003exlist\u003c/code\u003e implementation. Notice \u003ccode\u003exlist\u003c/code\u003e just inherits from \u003ccode\u003estd::list\u003c/code\u003e, but the key difference is the \u003ccode\u003e_Ax\u003c/code\u003e template argument defaults to \u003ccode\u003estl_allocator\u003c/code\u003e and not \u003ccode\u003estd::allocator\u003c/code\u003e.\u0026nbsp;\u003c/p\u003e\n\n```cpp\n#ifndef _XLIST_H\n#define _XLIST_H\n\n#include \"stl_allocator.h\"\n#include \u003clist\u003e\n\n// xlist uses a fix-block memory allocator\ntemplate \u003ctypename T, typename Alloc = stl_allocator\u003cT\u003e\u003e\nusing xlist = std::list\u003cT, Alloc\u003e;\n\n#endif\n```\n\n\u003cp\u003eEach of the \u0026ldquo;\u003ccode\u003ex\u003c/code\u003e\u0026rdquo; versions of the STL container is used just like the \u003ccode\u003estd\u003c/code\u003e version except all allocations are handled by \u003ccode\u003estl_allocator\u003c/code\u003e. For instance:\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\n#include  \u0026quot;xlist.h\u0026quot;\n\nxlist\u0026lt;xstring\u0026gt; myList;\nmyList.push_back(\u0026quot;hello world\u0026quot;);\n\u003c/pre\u003e\n\n\u003cp\u003eThe following container types use variable sized memory blocks from the heap that expand or contract in size during operation.\u0026nbsp;\u003c/p\u003e\n\n\u003cul\u003e\n\t\u003cli\u003e\u003ccode\u003estd::string\u003c/code\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ccode\u003estd::vector\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\n\u003cp\u003eA corresponding \u003ccode\u003exvector\u003c/code\u003e wasn\u0026#39;t implemented because vectors require a contiguous memory region whereas the other container types are node based. A fixed block allocator works well with equally sized blocks, but not so well if a \u003ccode\u003evector\u003c/code\u003e continually expands to a larger and larger single block. While \u003ccode\u003estl_allocator\u003c/code\u003e would work with a \u003ccode\u003evector\u003c/code\u003e, it could be misused and cause runtime problems with the fixed block memory manager.\u0026nbsp;\u003c/p\u003e\n\n\u003cp\u003eA \u003ccode\u003estd::string\u003c/code\u003e also varies its requested memory size during execution, but typically, strings aren\u0026#39;t used in an unbounded fashion. Meaning, in most cases you\u0026#39;re not trying to store a 100K-byte string, whereas a \u003ccode\u003evector\u003c/code\u003e you might actually do that. Therefore, \u003ccode\u003exstring\u003c/code\u003e\u0026#39;s are available as explained next.\u0026nbsp;\u003c/p\u003e\n\n## \u0026quot;x\u0026quot; Strings\n\n\u003cp\u003eNew \u003ccode\u003ex\u003c/code\u003e-version of the standard and wide versions of the \u003ccode\u003estring\u003c/code\u003e classes are created.\u003c/p\u003e\n\n\u003cul\u003e\n\t\u003cli\u003e\u003ccode\u003exstring\u003c/code\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ccode\u003exwstring\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\n\u003cp\u003eHere, we just \u003ccode\u003etypedef\u003c/code\u003e a new x-version using \u003ccode\u003estl_allocator\u003c/code\u003e as the default allocator of \u003ccode\u003echar\u003c/code\u003e and \u003ccode\u003ewchar_t\u003c/code\u003e types:\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\n#ifndef _XSTRING_H\n#define _XSTRING_H\n\n#include \u0026quot;stl_allocator.h\u0026quot;\n#include \u0026lt;string\u0026gt;\n\ntypedef std::basic_string\u0026lt;char, std::char_traits\u0026lt;char\u0026gt;, stl_allocator\u0026lt;char\u0026gt; \u0026gt; xstring;\ntypedef std::basic_string\u0026lt;wchar_t, std::char_traits\u0026lt;wchar_t\u0026gt;, stl_allocator\u0026lt;wchar_t\u0026gt; \u0026gt; xwstring;\n\n#endif \u003c/pre\u003e\n\n\u003cp lang=\"c++\"\u003eUsage of an \u003ccode\u003exstring\u003c/code\u003e is just like any other \u003ccode\u003estd::string\u003c/code\u003e.\u0026nbsp;\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\n#include \u0026quot;xstring.h\u0026quot;\n\nxwstring s = L\u0026quot;This is \u0026quot;;\ns += L\u0026quot;my string.\u0026quot;;\u003c/pre\u003e\n\n## \u0026quot;x\u0026quot; Streams\n\n\u003cp\u003eThe \u003ccode\u003eiostreams\u003c/code\u003e C++ Standard Library offers powerful and easy-to-use string formatting by way of the \u003ccode\u003estream\u003c/code\u003e classes.\u0026nbsp;\u003c/p\u003e\n\n\u003cul\u003e\n\t\u003cli\u003e\u003ccode\u003estd::stringstream\u003c/code\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ccode\u003estd::ostringstream\u003c/code\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ccode\u003estd::wstringstream\u003c/code\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ccode\u003estd::wostringstream\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\n\u003cp\u003eLike the container classes, \u003ccode\u003eiostreams\u003c/code\u003e makes use of the custom STL allocator instead of the global heap using these new definitions.\u003c/p\u003e\n\n\u003cul\u003e\n\t\u003cli\u003e\u003ccode\u003exstringstream\u003c/code\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ccode\u003exostringstream\u003c/code\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ccode\u003exwstringstream\u003c/code\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ccode\u003exwostringstream\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\n\u003cp\u003eAgain, just \u003ccode\u003etypedef\u003c/code\u003e new x-versions with the default \u003ccode\u003estl_allocator\u003c/code\u003e template arguments makes it easy.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\n#ifndef _XSSTREAM_H\n#define _XSSTREAM_H\n\n#include \u0026quot;stl_allocator.h\u0026quot;\n#include \u0026lt;sstream\u0026gt;\n\ntypedef std::basic_stringstream\u0026lt;char, std::char_traits\u0026lt;char\u0026gt;, \nstl_allocator\u0026lt;char\u0026gt; \u0026gt; xstringstream;\ntypedef std::basic_ostringstream\u0026lt;char, std::char_traits\u0026lt;char\u0026gt;, \nstl_allocator\u0026lt;char\u0026gt; \u0026gt; xostringstream;\n\ntypedef std::basic_stringstream\u0026lt;wchar_t, std::char_traits\u0026lt;wchar_t\u0026gt;, \nstl_allocator\u0026lt;wchar_t\u0026gt; \u0026gt; xwstringstream;\ntypedef std::basic_ostringstream\u0026lt;wchar_t, std::char_traits\u0026lt;wchar_t\u0026gt;, \nstl_allocator\u0026lt;wchar_t\u0026gt; \u0026gt; xwostringstream;\n\n#endif\u003c/pre\u003e\n\n\u003cp\u003eNow, using an \u003ccode\u003exstringstream\u003c/code\u003e is a snap.\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\nxstringstream myStringStream;\nmyStringStream \u0026lt;\u0026lt; \u0026quot;hello world \u0026quot; \u0026lt;\u0026lt; 2016 \u0026lt;\u0026lt; ends;\n\u003c/pre\u003e\n\n# Benchmarking\n\n\u003cp\u003eIn my previous allocator articles, simple tests show the fixed block allocator is faster than the global heap. Now let\u0026#39;s check the \u003ccode\u003estl_allocator\u003c/code\u003e enabled containers to see they compare against \u003ccode\u003estd::allocator\u003c/code\u003e on a Windows PC.\u0026nbsp;\u003c/p\u003e\n\n\u003cp\u003eAll tests were built with Visual Studio 2008 and maximum speed compiler optimizations enabled. The \u003ccode\u003exallocator\u003c/code\u003e is running in heap blocks mode where blocks are initially obtained from the heap but recycled during deallocations. The debug heap is also disabled by setting the \u003cstrong\u003eDebugging \u0026gt; Environment _NO_DEBUG_HEAP=1\u003c/strong\u003e. The debug heap is considerably slower because of the extra safety checks.\u0026nbsp;\u003c/p\u003e\n\n\u003cp\u003eThe benchmark tests are simplistic and stress insertion/removal of 10000 elements. Each test is run three times. The insert/remove is where the STL library relies upon dynamic storage and that\u0026#39;s the focus of these tests, not the underlying algorithmic performance.\u0026nbsp;\u003c/p\u003e\n\n\u003cp\u003eThe code snippet below is the \u003ccode\u003estd::list\u003c/code\u003e test. The other container tests are similarly basic.\u0026nbsp;\u003c/p\u003e\n\n\u003cpre lang=\"C++\"\u003e\nlist\u0026lt;int\u0026gt; myList;\nfor (int i=0; i\u0026lt;MAX_BENCHMARK; i++)\n    myList.push_back(123);\nmyList.clear();\u003c/pre\u003e\n\n\u003cp\u003eThe performance difference between \u003ccode\u003estd::allocator\u003c/code\u003e and \u003ccode\u003estl_allocator\u003c/code\u003e running in heap block mode is shown below:\u0026nbsp;\u003cbr /\u003e\n\u0026nbsp;\u003c/p\u003e\n\n\u003ctable class=\"ArticleTable\"\u003e\n\t\u003cthead\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003ctd\u003eContainer\u003c/td\u003e\n\t\t\t\u003ctd\u003eMode\u003c/td\u003e\n\t\t\t\u003ctd\u003eRun\u003c/td\u003e\n\t\t\t\u003ctd\u003eBenchmark Time (mS)\u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003ctd\u003e\u003ccode\u003estd::list\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003ctd\u003eGlobal Heap\u003c/td\u003e\n\t\t\t\u003ctd\u003e1\u003c/td\u003e\n\t\t\t\u003ctd\u003e60\u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003ctd\u003e\u003ccode\u003estd::list\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003ctd\u003eGlobal Heap\u003c/td\u003e\n\t\t\t\u003ctd\u003e2\u003c/td\u003e\n\t\t\t\u003ctd\u003e32\u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003ctd\u003e\u003ccode\u003estd::list\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003ctd\u003eGlobal Heap\u003c/td\u003e\n\t\t\t\u003ctd\u003e3\u003c/td\u003e\n\t\t\t\u003ctd\u003e23\u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003ctd\u003e\u003ccode\u003exlist\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003ctd\u003eHeap Blocks\u003c/td\u003e\n\t\t\t\u003ctd\u003e1\u003c/td\u003e\n\t\t\t\u003ctd\u003e19\u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003ctd\u003e\u003ccode\u003exlist\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003ctd\u003eHeap Blocks\u003c/td\u003e\n\t\t\t\u003ctd\u003e2\u003c/td\u003e\n\t\t\t\u003ctd\u003e11\u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003ctd\u003e\u003ccode\u003exlist\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003ctd\u003eHeap Blocks\u003c/td\u003e\n\t\t\t\u003ctd\u003e3\u003c/td\u003e\n\t\t\t\u003ctd\u003e11\u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003ctd\u003e\u003ccode\u003estd::map\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003ctd\u003eGlobal Heap\u003c/td\u003e\n\t\t\t\u003ctd\u003e1\u003c/td\u003e\n\t\t\t\u003ctd\u003e37\u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003ctd\u003e\u003ccode\u003estd::map\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003ctd\u003eGlobal Heap\u003c/td\u003e\n\t\t\t\u003ctd\u003e2\u003c/td\u003e\n\t\t\t\u003ctd\u003e34\u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003ctd\u003e\u003ccode\u003estd::map\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003ctd\u003eGlobal Heap\u003c/td\u003e\n\t\t\t\u003ctd\u003e3\u003c/td\u003e\n\t\t\t\u003ctd\u003e41\u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003ctd\u003e\u003ccode\u003exmap\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003ctd\u003eHeap Blocks\u003c/td\u003e\n\t\t\t\u003ctd\u003e1\u003c/td\u003e\n\t\t\t\u003ctd\u003e38\u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003ctd\u003e\u003ccode\u003exmap\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003ctd\u003eHeap Blocks\u003c/td\u003e\n\t\t\t\u003ctd\u003e2\u003c/td\u003e\n\t\t\t\u003ctd\u003e25\u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003ctd\u003e\u003ccode\u003exmap\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003ctd\u003eHeap Blocks\u003c/td\u003e\n\t\t\t\u003ctd\u003e3\u003c/td\u003e\n\t\t\t\u003ctd\u003e25\u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003ctd\u003e\u003ccode\u003estd::string\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003ctd\u003eGlobal Heap\u003c/td\u003e\n\t\t\t\u003ctd\u003e1\u003c/td\u003e\n\t\t\t\u003ctd\u003e171\u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003ctd\u003e\u003ccode\u003estd::string\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003ctd\u003eGlobal Heap\u003c/td\u003e\n\t\t\t\u003ctd\u003e2\u003c/td\u003e\n\t\t\t\u003ctd\u003e146\u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003ctd\u003e\u003ccode\u003estd::string\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003ctd\u003eGlobal Heap\u003c/td\u003e\n\t\t\t\u003ctd\u003e3\u003c/td\u003e\n\t\t\t\u003ctd\u003e95\u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003ctd\u003e\u003ccode\u003exstring\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003ctd\u003eHeap Blocks\u003c/td\u003e\n\t\t\t\u003ctd\u003e1\u003c/td\u003e\n\t\t\t\u003ctd\u003e57\u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003ctd\u003e\u003ccode\u003exstring\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003ctd\u003eHeap Blocks\u003c/td\u003e\n\t\t\t\u003ctd\u003e2\u003c/td\u003e\n\t\t\t\u003ctd\u003e36\u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\t\u003ctr\u003e\n\t\t\t\u003ctd\u003e\u003ccode\u003exstring\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003ctd\u003eHeap Blocks\u003c/td\u003e\n\t\t\t\u003ctd\u003e3\u003c/td\u003e\n\t\t\t\u003ctd\u003e40\u003c/td\u003e\n\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\n\u003cp\u003eThe test results shows that, for this benchmark, the \u003ccode\u003estl_allocator\u003c/code\u003e enabled versions are approximately 2 to 3 times faster than \u003ccode\u003estd::allocator\u003c/code\u003e. Now this doesn\u0026#39;t mean that STL is now magically overall twice as fast. Again, this benchmark is just concentrating on insertion and removal performance. The underlying container algorithmic performance hasn\u0026#39;t changed. Therefore, the overall improvement seen will vary depending on how often your application inserts/removes from containers.\u0026nbsp;\u003c/p\u003e\n\n\u003cp\u003eThe \u003ccode\u003estl_allocator\u003c/code\u003e operates in fixed time if \u003ccode\u003exallocator\u003c/code\u003e is used in static pool mode. If \u003ccode\u003exallocator\u003c/code\u003e heap blocks mode is used, the execution time is constant once the free list is populated with blocks obtained from the heap. Notice that the \u003ccode\u003exlist\u003c/code\u003e benchmark above has an initial execution time of 19mS, yet the subsequent executions are 11mS each. On the first run, \u003ccode\u003exallocator\u003c/code\u003e had to obtain fresh blocks from the global heap using \u003ccode\u003eoperator new\u003c/code\u003e. On runs 2 and 3, the blocks already existed within the \u003ccode\u003exallocator\u003c/code\u003e free-list so the heap isn\u0026#39;t relied upon making successive runs much faster.\u0026nbsp;\u003c/p\u003e\n\n\u003cp\u003eThe global heap has unpredictable execution time performance when the heap fragments. Since \u003ccode\u003exallocator\u003c/code\u003e only allocates blocks and recycles them for later use the execution time is more predictable and consistent.\u0026nbsp;\u003c/p\u003e\n\n\u003cp\u003eGame developers go to great lengths to solve heap fragmentation and its myriad of associated problems. The Electronic Arts Standard Template Library (EASTL) white paper goes into detail about the problems with STL and specifically the \u003ccode\u003estd::allocator\u003c/code\u003e. Paul states in the\u0026nbsp;\u003cem\u003eEASTL -- Electronic Arts Standard Template Library\u003c/em\u003e document:\u003c/p\u003e\n\n\u003cblockquote class=\"quote\"\u003e\n\u003cdiv class=\"op\"\u003eQuote:\u003c/div\u003e\n\n\u003cp\u003eAmong game developers the most fundamental weakness is the std allocator design, and it is this weakness that was the largest contributing factor to the creation of EASTL.\u003c/p\u003e\n\u003c/blockquote\u003e\n\n\u003cp\u003eWhile I don\u0026#39;t write code for games, I can see how the lag associated with fragmentation and erratic allocation/deallocation times would effect game play.\u0026nbsp;\u003c/p\u003e\n\n# Reference Articles\n\n\u003cul\u003e\n\t\u003cli\u003e\u003cstrong\u003e\u003ca href=\"http://www.codeproject.com/Articles/1084801/Replace-malloc-free-with-a-Fast-Fixed-Block-Memory\"\u003eReplace malloc/free with a Fast Fixed Block Memory Allocator\u003c/a\u003e\u003c/strong\u003e by David Lafreniere\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"http://www.codeproject.com/Articles/1083210/An-Efficient-Cplusplus-Fixed-Block-Memory-Allocato\"\u003e\u003cstrong\u003eAn Efficient C++ Fixed Block Memory Allocator\u003c/strong\u003e\u003c/a\u003e by David Lafreniere\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2271.html\"\u003e\u003cstrong\u003eEASTL -- Electronic Arts Standard Template Library\u003c/strong\u003e\u003c/a\u003e\u0026nbsp; by Paul Pedriana\u003c/li\u003e\n\u003c/ul\u003e\n\n# Benefits\n\n\u003cp\u003eSTL is an extremely useful C++ library. Too often through, for medical devices I can\u0026#39;t use it due to the possibility of a heap fragmentation memory fault. This leads to rolling your own custom container classes for each project instead of using an existing, well-tested library.\u0026nbsp;\u003c/p\u003e\n\n\u003cp\u003eMy goal with \u003ccode\u003estl_allocator\u003c/code\u003e was eliminating memory faults; however, the fixed block allocator does offer faster and more consistent execution times whereas the heap does not. The heap implementation and level of fragmentation leads to unpredictable execution times. Depending on your application, this may or may not be an issue.\u003c/p\u003e\n\n\u003cp\u003eAs shown in this article, simply employing an STL-compatible fixed block allocator opens up the possibility of using C++ Standard Template Library features on a project that otherwise may not have been possible.\u0026nbsp;\u003c/p\u003e\n\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fendurodave%2Fstl_allocator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fendurodave%2Fstl_allocator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fendurodave%2Fstl_allocator/lists"}