Code Monkey home page Code Monkey logo

appshift-memorypool's Introduction

MemoryPool For C++

A very fast cross-platform memory pool mechanism for C++ built using a data-oriented approach. I hope this simple feature will help you increase your software's performance - and there are more projects and features to come under the AppShift library name, wait for it ;)

Table of Contents

Usage

To use the memory pool features you just need to copy the MemoryPool.cpp & MemoryPool.h files to your project. The memory pool structure is AppShift::Memory::MemoryPool. The Memory Pool Is Not Thread Safe - In case of threads it is better to create a memory pool for each thread

  • Create a memory pool: AppShift::Memory::MemoryPool * mp = new AppShift::Memory::MemoryPool(size); Create a new memory pool structure and a first memory block. If you don't specify a size then by default it will be the MEMORYPOOL_DEFAULT_BLOCK_SIZE macro.
  • Allocate space: Type* allocated = new (mp) Type[size]; or Type* allocated = (Type*) mp->allocate(size * sizeof(Type)); or Type* allocated = mp->allocate<Type>(size); Where Type is the object\primitive type to create, mp is the memory pool object address, and size is a represention of the amount of types to allocate.
  • Deallocate space: mp->free(allocated) Remove an allocated space
  • Reallocate space: Type* allocated = mp->reallocate<Type>(allocated, size); or Type* allocated = (Type*) mp->reallocate(allocated, size); Rellocate a pre-allocated space, will copy the previous values to the new memory allocated.
  • Dump data of a memory pool: mp->dumpPoolData() This function prints outs the data about the blocks and units in the pool.

Memory scoping

Scoping is a fast way to deallocate many allocations at once. If for example you need to allocate more than once in a given part of the code, and then you deallocate all the allocations that happaned, then you can "scope" all these allocations together. it works the same way as a stack in a function scope.

  • Start A Scope: mp->startScope() where mp is the memory pool structure. This function creates a "checkpoint" of the offset and block in the memory pool.
  • End A Scope: mp->endScope() Will free all the allocations made after the scope started.
  • Scope Inside A Scope: You can nest scopes inside scopes by strating a new scope again, just the same way that the stack works with function scopes. Each scope is pointing to the previous one to create a chain that allows the memory pool manager to manage scope nesting.

Macros

There are some helpful macros available to indicate how you want the MemoryPool to manage your memory allocations.

  • #define MEMORYPOOL_DEFAULT_BLOCK_SIZE 1024 * 1024: The MemoryPool allocates memory into blocks, each block can have a maximum size avalable to use - when it exceeds this size, the MemoryPool allocates a new block - use this macro to define the maximum size to give to each block. By default the value is 1024 * 1024 which is 1MB.

Methodology

The MemoryPool is a structure pointing to the start of a chain of blocks, which size of every block is by default MEMORYPOOL_BLOCK_MAX_SIZE macro (See Macros) or the size passed into the AppShift::Memory::MemoryPool(size) constructor. The MemoryPool is an object holding the necessary functions to work with the a memory pool. What's also good is that you can also access the MemoryPool structure data directly if needed (everything is public).

MemoryPool data (MemoryPool)

The memory pool structure holds meta-data about the memory space that is allocated and stored in the pool.

  • SMemoryBlockHeader* firstBlock; - Holds the first block in the chain of memory blocks.
  • SMemoryBlockHeader* currentBlock; - Holds the last block in the chain that is used first for allocating (allocations are happening in a stack manner, where each memory unit allocated is on top of the previous one, when a block reaches it's maximum size then a new block is allocated and added to the block chain of the pool).
  • size_t defaultBlockSize; - Default size to use when creating a new block, the size is defined by the MEMORYPOOL_BLOCK_MAX_SIZE macro or by passing the size as a parameter for the AppShift::Memory::MemoryPoolManager::create(size) function.
  • SMemoryScopeHeader* currentScope; - A pointer to the current scope in the memory pool.

Memory Block (SMemoryBlockHeader)

Each block contains a block header the size of 48 bytes containing the following information:

  • size_t blockSize; - Size of the block
  • size_t offset; - Offset in the block from which the memory is free (The block is filled in sequencial order)
  • SMemoryBlockHeader* next; - Pointer to the next block
  • SMemoryBlockHeader* prev; - Pointer to the previous block
  • size_t numberOfAllocated - Number of units currently allocated in this block. Helps smart garbage collection when block data has been freed.
  • size_t numberOfDeleted - Number of units that have been flaged as deleted. The system removes blocks by comparing the deleted with the allocated.

When a block is fully filled the MemoryPool creates a new block and relates it to the previous block, and the previous to the current, them uses the new pool as the current block.

Memory Unit (SMemoryUnitHeader)

When allocating a space, MemoryPool creates a SMemoryUnitHeader and moves the blocks offset forward by the header size plus the amount of space requested. The header is 16 bytes long and contains the following data:

  • size_t length; - The length in bytes of the allocated space
  • SMemoryBlockHeader* container - Block which this unit belongs to

Memory Scope (SMemoryScopeHeader)

A scope has it's own structure - it has an offset and a pointer to the starting block of the scope, and also a pointer to the previous scope (parent).

  • size_t scopeOffset; - Saves the offset of the block when start scope is declared.
  • SMemoryBlockHeader* firstScopeBlock; - Saves the current block when a start scope is declared, helps to know until which block to free everything when the scope ends.
  • SMemoryScopeHeader* prevScope; - Pointer to the previous scope/NULL if no parent scope is present.

Benchmark

Windows & CLang


About 21-24 times faster than standard new/delete in each test.

Windows & MSVC


About 10-13 times faster than standard new/delete in each test.

MacOS & CLang


About 8-10 times faster than standard new/delete in each test.

About

  • Sapir Shemer is the proud business owner of DevShift and an Open-Source enthusiast. Have been programming since the age of 7. Mathematics Student :)

Contributors - Thank You! :D

A list of people that were kind enough to help:

More to come in later versions

In the next versions I'm planning to add some interesting features:

  • Ability to put thread safety on the memory pool to make a thread shared memory pool.
  • Ability to create an inter-process memory pool which can be shared between different processes.
  • compressGarbage(): Will compress deleted units that are next to eachother into one unit.

appshift-memorypool's People

Contributors

lesscomplexity avatar sagiweizmann avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

appshift-memorypool's Issues

Benchmark code comparing the custom String class with std::string is not an apples-to-apples comparison

Most C++ implementations of std::string will allocate a new block when appending a string (unless the implementation is specifically tuned to always allocate extra space after a string assignment); and then the data from the old block is copied to the new block, and the old block is freed. The memory pool implementation doesn't have to allocate a new block. It just extends the current block and so it gains the speed advantage.

It would be a fairer benchmark if the String class were templated and it took an Allocator where the allocator could either be the standard C++ allocator, or the MemoryPool allocator. That way it can be shown that the same operations are being performed, but MemoryPool is actually faster.

Ubuntu 20.04 compile Error

Hi,

I succesfully tested on the Mac os X but ubuntu gives below error:

/home/alp2080/Projects/CPPShift-MemoryPool/MemoryPool.cpp: In static member function ‘static void* CPPShift::Memory::MemoryPoolManager::reallocate(void*, size_t)’:
/home/alp2080/Projects/CPPShift-MemoryPool/MemoryPool.cpp:133:7: error: ‘memcpy’ is not a member of ‘std’
std::memcpy(temp_point, unit_pointer_start, unit->length);
^~~~~~
/home/alp2080/Projects/CPPShift-MemoryPool/MemoryPool.cpp:133:7: note: suggested alternative: ‘empty’
std::memcpy(temp_point, unit_pointer_start, unit->length);
^~~~~~
empty
make[2]: *** [CMakeFiles/MemoryPool.dir/build.make:76: CMakeFiles/MemoryPool.dir/MemoryPool.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:76: CMakeFiles/MemoryPool.dir/all] Error 2
make: *** [Makefile:84: all] Error 2

VS2013 warning C4291: "void *operator new(size_t,AppShift::MemoryPool *)"

Hi, I am using VS2013 + WindowsApplicationForDrivers8.1 to build my project

// global var
AppShift::MemoryPool pool;
//....
Object obj = new(&pool) Object;
//...
pool.free(obj);

then I got a warning C4291: "void *operator new(size_t,AppShift::MemoryPool *)": No matching delete operator found; memory will not be released if initialization throws an exception.
Does this matter or can i just ignore it?

new does not return null

This code is wrong:

CPPShift::Memory::MemoryPool* CPPShift::Memory::MemoryPoolManager::create(size_t block_size) { // Create memory pool MemoryPool* mp = new MemoryPool; if (mp == NULL) throw EMemoryErrors::CANNOT_CREATE_MEMORY_POOL;
By default, the call to new MemoryPool will never return null. You should either call malloc like you do in createMemoryBlock (recommended) or catch(std::bad_alloc).

free using in destructor

When using the destructor, your implementation will call the class member function free instead of std:: free. Here I think std:: free should be used to release the memory correctly.

Crash when doing repeated interleaved allocations, reallocations, and frees

The following code crashes:

#include <iostream>
#include "MemoryPool.h"

int main() {

    const char* str = "Hello ";
    int length = strlen(str);
    const char* add = "World";
    int add_length = strlen(add);

    CPPShift::Memory::MemoryPool* mp = CPPShift::Memory::MemoryPoolManager::create();
    for (int i = 0; i < 1000000; i++) {
        // Alloc
        char * start = new (mp) char[length];

        // Realloc
        char* old = start;
        start = new (mp) char[length + add_length];
        CPPShift::Memory::MemoryPoolManager::free(old);

        // Free
        CPPShift::Memory::MemoryPoolManager::free(start);
    }

    return 0;
}

could you tell how to allocate and free correct?

AppShift::Memory::MemoryPool * mp = new AppShift::Memory::MemoryPool(1<<20);
mp->dumpPoolData(); //dump message show current usage : 0% (0/1048576)

uint8_t * allocated[100] = { nullptr };
for (int j = 0; j < 100; j++) {
allocated[j] = mp->allocate<uint8_t>(1);
*allocated[j] = j;
}
mp->dumpPoolData(); //dump message show current usage : 0.162125% (1700/1048576)
for (int j = 0; j < 100; j++) {
mp->free(allocated[j]);
}
mp->dumpPoolData(); //dump message show current usage : 0.16053% (1700/1048576)
delete mp;

after call mp->free, though the usage still 0.16053%, not restore to 0,
could you tell me that why

Allocator interface for memory pool

It would be useful to be able to use memory pools in existing containers, like std::vector, std::deque, etc. (Along with their std::pmr::* counterparts). To do this, we would need an allocator that "wraps" the memory pool.

I'm thinking there should be two versions, one with user-managed pools (so a reference to the host allocator would need to be stored in the container, allows flexibility at the cost of a bit of extra memory), and another with a global pool (less memory usage, less flexibility).

If I were to work on such a feature, should I fork the dev branch and work from there? What is the preferred contributing method?

(Btw, hi from reddit!)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.