уберегите от велосипеда (очень простой алокатор)
От: Caracrist https://1pwd.org/
Дата: 28.06.10 07:56
Оценка:
Нужно написать простой алокатор, размер элемента один, количество ограничено и известно. Необходим алокатор переиспользующий память. ценарий очень простой, создание и уничтожение одинаковых элементов.
Киньте ссылкой в готовую реализацию, буду благодарен.
~~~~~
~lol~~
~~~ Single Password Solution
Re: уберегите от велосипеда (очень простой алокатор)
От: A.Lokotkov Россия  
Дата: 28.06.10 08:02
Оценка: 1 (1)
Здравствуйте, Caracrist, Вы писали:

C>Нужно написать простой алокатор, размер элемента один


Глава 4 в книжке Александреску
Автор(ы): Андрей Александреску

В книге изложена новая технология программирования, представляющая собой сплав обобщенного программирования, метапрограммирования шаблонов и объектно- ориентированного программирования на С++. Настраиваемые компоненты, созданные автором, высоко подняли уровень абстракции, наделив язык С++ чертами языка спецификации проектирования, сохранив всю его мощь и выразительность. В книге изложены способы реализации основных шаблонов проектирования. Разработанные компоненты воплощены в библиотеке Loki, которую можно загрузить с Web-страницы автора. Книга предназначена для опытных программистов на С++.
bloß it hudla
Re: уберегите от велосипеда (очень простой алокатор)
От: Guard_h4s Россия  
Дата: 28.06.10 08:16
Оценка: 9 (2)
Здравствуйте, Caracrist, Вы писали:

C>Нужно написать простой алокатор, размер элемента один, количество ограничено и известно. Необходим алокатор переиспользующий память. ценарий очень простой, создание и уничтожение одинаковых элементов.

C>Киньте ссылкой в готовую реализацию, буду благодарен.
boost::object_pool
Re: уберегите от велосипеда (очень простой алокатор)
От: D14  
Дата: 28.06.10 08:32
Оценка: 1 (1)
Здравствуйте, Caracrist, Вы писали:

C>Нужно написать простой алокатор, размер элемента один, количество ограничено и известно. Необходим алокатор переиспользующий память. ценарий очень простой, создание и уничтожение одинаковых элементов.

C>Киньте ссылкой в готовую реализацию, буду благодарен.
http://lj.rossia.org/users/kouzdra/664091.html#cutid3
Re: уберегите от велосипеда (очень простой алокатор)
От: CreatorCray  
Дата: 28.06.10 08:34
Оценка: 4 (1) +3
Здравствуйте, Caracrist, Вы писали:

C>Нужно написать простой алокатор, размер элемента один, количество ограничено и известно. Необходим алокатор переиспользующий память. ценарий очень простой, создание и уничтожение одинаковых элементов.

C>Киньте ссылкой в готовую реализацию, буду благодарен.
Могу разве что только свой лисапед скинуть

Не надо бояться велосипедов если руки ровные.
Велосипед может оказаться более подходящим решением чем громозкая generic либа т.к. может быть оптимально заточен под конкретную задачу.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Re[2]: уберегите от велосипеда (очень простой алокатор)
От: D14  
Дата: 28.06.10 12:17
Оценка:
Здравствуйте, Guard_h4s, Вы писали:

G_>Здравствуйте, Caracrist, Вы писали:


C>>Нужно написать простой алокатор, размер элемента один, количество ограничено и известно. Необходим алокатор переиспользующий память. ценарий очень простой, создание и уничтожение одинаковых элементов.

C>>Киньте ссылкой в готовую реализацию, буду благодарен.
G_>boost::object_pool
Это, кстати, тормоз ацкий. Я когда тестировал, выделял быстро, но медленно отдавал. Собственно, как fixed size аллокатор не годится.
Re[3]: уберегите от велосипеда (очень простой алокатор)
От: Guard_h4s Россия  
Дата: 28.06.10 12:23
Оценка:
Здравствуйте, D14, Вы писали:

D14>Здравствуйте, Guard_h4s, Вы писали:


G_>>Здравствуйте, Caracrist, Вы писали:


C>>>Нужно написать простой алокатор, размер элемента один, количество ограничено и известно. Необходим алокатор переиспользующий память. ценарий очень простой, создание и уничтожение одинаковых элементов.

C>>>Киньте ссылкой в готовую реализацию, буду благодарен.
G_>>boost::object_pool
D14>Это, кстати, тормоз ацкий. Я когда тестировал, выделял быстро, но медленно отдавал. Собственно, как fixed size аллокатор не годится.
Сам использую, особых проблем со скоростью освобождения не замечал (правда просто boost::pool в связке с перегруженными для класса операторами new/delete) Объектов в среднем 1-2М
Re[4]: уберегите от велосипеда (очень простой алокатор)
От: D14  
Дата: 28.06.10 18:32
Оценка: 1 (1)
Здравствуйте, Guard_h4s, Вы писали:

C>>>>Киньте ссылкой в готовую реализацию, буду благодарен.

G_>>>boost::object_pool
D14>>Это, кстати, тормоз ацкий. Я когда тестировал, выделял быстро, но медленно отдавал. Собственно, как fixed size аллокатор не годится.
G_>Сам использую, особых проблем со скоростью освобождения не замечал (правда просто boost::pool в связке с перегруженными для класса операторами new/delete) Объектов в среднем 1-2М

Не может такого быть. С перегруженным delete. Не предназначен pool для этого. Хуже new/delete результат будет.

#include <iostream>
#include <vector>
#include <ctime>
#include <boost\pool\object_pool.hpp>

struct X
{
    int i;
};

boost::object_pool<X> pool_alloc;

void* free_list=0;
X* new_x()
{
    if (free_list != NULL)                
    {                                    
        void * res = free_list;             
        free_list = *(void **)free_list;     
        return (X*)res;                           
    }                                        
    return new X;
}   
void delete_x(X* ptr)
{
    if (ptr == NULL) return;                       
    *(void **)ptr = free_list;                      
    free_list = ptr;  
}

const int n = 50000;
const int m = 10;
std::vector<X*> ptrs(n);

void run1()
{
    clock_t start = clock();
    for (int j=0; j<m; ++j)
    {
        for (int i=0; i<n; ++i)
        {
            ptrs[i] = pool_alloc.construct();
        }
        for (int i=0; i<n; ++i)
        {
            pool_alloc.destroy(ptrs[i]);
        }
    }
    clock_t finish = clock();
    printf ("Pool alloc takes %g sec.\n", (double)(finish - start) / CLOCKS_PER_SEC);
}

void run2()
{
    clock_t start = clock();
    for (int j=0; j<m; ++j)
    {
        for (int i=0; i<n; ++i)
        {
            ptrs[i] = new X;
        }
        for (int i=0; i<n; ++i)
        {
            delete ptrs[i];
        }
    }
    clock_t finish = clock();
    printf ("New/delete takes %g sec.\n", (double)(finish - start) / CLOCKS_PER_SEC);
}


int main()
{
    run1();
    run2();
    return 0;
}


Pool alloc takes 21.587 sec.
New/delete takes 0.04 sec.
Re: уберегите от велосипеда (очень простой алокатор)
От: Aleх  
Дата: 28.06.10 20:38
Оценка: 4 (1)
Здравствуйте, Caracrist, Вы писали:

C>Нужно написать простой алокатор, размер элемента один, количество ограничено и известно. Необходим алокатор переиспользующий память. ценарий очень простой, создание и уничтожение одинаковых элементов.

C>Киньте ссылкой в готовую реализацию, буду благодарен.

Мне понравился этот http://www.rsdn.ru/forum/src/888215.flat.1.aspx
Автор: _Winnie
Дата: 08.11.04

http://rsdn.ru/File/23256/winnie_alloc05.rar

ещё я как то пользовался вот этим того же автора

#define STORAGE
//__declspec(thread)


namespace MWinnie
{

  namespace Detail
  {
    template <bool, class T1, class T2>
    struct SelectType: public T1
    {
    };

    template <class T1, class T2>
    struct SelectType<false, T1, T2> : public T2
    {
    };

    enum { USE_OPERATOR_NEW = 512 };

    struct SChunkHeader
    {
      SChunkHeader *m_pNext;
    };

    struct FreeBlock
    {
      FreeBlock *m_pNext;
    };

    struct SThreadAllocatorState
    {
      SThreadAllocatorState *m_pNext;
      void (*Cleaner)();
      SChunkHeader *m_pChunksHead;
      FreeBlock *m_pFirstFree;
    };

    STORAGE SThreadAllocatorState *pThreadAllocatorStates;


    //STATIC_ASSERT (идея Андрея Александреску)_____________________________
    template<bool>
    struct CompileTimeChecker
    {
      CompileTimeChecker(...);
      operator int();
    };
    template<> struct CompileTimeChecker<false> { operator int(); };

#define STATIC_ASSERT(expr, msg)                      \
    {                                      \
    class ERROR_##msg {};                          \
    (void)sizeof( ( int )CompileTimeChecker<(expr) != 0>((ERROR_##msg())));  \
    }


    template <size_t SIZE, size_t CHUNK_SIZE> class CFreeListSizedAllocator
    {

    private:

      STORAGE
        static SThreadAllocatorState ThreadAllocatorState;

      static void Cleaner()
      {
        SChunkHeader *pChunk = ThreadAllocatorState.m_pChunksHead;
        while (pChunk)
        {
          SChunkHeader *pNext = pChunk->m_pNext;
          delete[] (char*)pChunk;
          pChunk = pNext;
        }

        //std::cout << SIZE <<' ';
      }

      static __declspec(noinline) void NewChunk()
      {
        if (!ThreadAllocatorState.m_pChunksHead)
        {
          //добавляем себя в односвязный список аллокаторов, которым нужно сделать Clean при выходе из потока.
          ThreadAllocatorState.m_pNext = pThreadAllocatorStates;
          ThreadAllocatorState.Cleaner = Cleaner;
          pThreadAllocatorStates = &ThreadAllocatorState;
        }
        SChunkHeader *pNewChunk = (SChunkHeader*)new char[CHUNK_SIZE+sizeof(SChunkHeader)];
        pNewChunk->m_pNext = ThreadAllocatorState.m_pChunksHead;
        ThreadAllocatorState.m_pChunksHead = pNewChunk;
        char *pChunkStorage = (char *)(pNewChunk+1);

        // если здесь не компилится, значит не хватает размера чанка для хранения минимум 1 элемента
        STATIC_ASSERT((CHUNK_SIZE + sizeof(SChunkHeader)) > SIZE, CHECK_CHUNK_SIZE_FAILED);

        const size_t NumBlocksInChunk = CHUNK_SIZE/SIZE - 1;
        for (int i=NumBlocksInChunk; i >= 0 ; --i)
        {
          FreeBlock *pFree = (FreeBlock*)(pChunkStorage + i*SIZE);
          pFree->m_pNext = (FreeBlock*)ThreadAllocatorState.m_pFirstFree;
          ThreadAllocatorState.m_pFirstFree = pFree;
        }
      }

    public:

      static void *Allocate()
      {
        if (!ThreadAllocatorState.m_pFirstFree)
          NewChunk();
        FreeBlock *pFree = ThreadAllocatorState.m_pFirstFree;
        ThreadAllocatorState.m_pFirstFree = pFree->m_pNext;
        return pFree;
      }

      static void Deallocate(void *p)
      {
        FreeBlock *pFree = (FreeBlock*)p;
        pFree->m_pNext =  ThreadAllocatorState.m_pFirstFree;
        ThreadAllocatorState.m_pFirstFree = pFree;
      }
    };

#undef STATIC_ASSERT

    template <size_t SIZE, size_t CHUNK_SIZE>
    SThreadAllocatorState CFreeListSizedAllocator<SIZE, CHUNK_SIZE>::ThreadAllocatorState;

    template <size_t SIZE>
    class COperatorNewSizedAllocator
    {
    public:
      static void *Allocate()
      {
        return new char[SIZE];
      }

      static void Deallocate(void *p)
      {
        delete[] (char*)p;
      }
    };

    template <size_t SIZE, size_t CHUNK_SIZE = 4092>  class CSizedAllocator: public SelectType<
      SIZE <= USE_OPERATOR_NEW,
      CFreeListSizedAllocator<SIZE >= sizeof(FreeBlock) ? SIZE : sizeof(FreeBlock), CHUNK_SIZE>,
      COperatorNewSizedAllocator<SIZE> >
    {
    };

  }// namespace Detail

  void CleanThreadAllocatorState()
  {
    for (
      Detail::SThreadAllocatorState *pThreadState = Detail::pThreadAllocatorStates;
      pThreadState;
    pThreadState = pThreadState->m_pNext)
      pThreadState->Cleaner();
  };

  // MihaPro: 4092 = это 4K размер без 4х байт для указателя
  /*template <class T, size_t CHUNK_SIZE = 4092> class CFastPoolAllocator
  {
  public:
    typedef T *pointer;
    typedef const T *const_pointer;
    typedef ptrdiff_t difference_type;
    typedef T &reference;
    typedef const T &const_reference;
    typedef size_t size_type;
    typedef T value_type;

    template <class U, size_t CHUNK_SIZE>
    CFastPoolAllocator(const CFastPoolAllocator<U, CHUNK_SIZE> &in_Other) {};

    CFastPoolAllocator() {}

    pointer address(reference in_X) const { return &in_X; }
    const_pointer address(const_reference in_X) const { return &in_X; }
    size_type max_size() const { return size_t(-1)/sizeof(T); }

    pointer allocate(size_type in_Count = 1, void * = 0)
    {
      if (in_Count != 1)
        return (T*)new char[in_Count*sizeof(T)];
      return (T*)Detail::CSizedAllocator<sizeof(T), CHUNK_SIZE>::Allocate();
    }

    void deallocate(pointer p, size_type in_Count = 1)
    {
      if (in_Count != 1)
      {
        delete[] (char*)p;
        return;
      }
      Detail::CSizedAllocator<sizeof(T), CHUNK_SIZE>::Deallocate(p);
    }

    void construct(pointer out_p, const value_type &in_Val)
    {
      new (out_p)T(in_Val);
    }

    void destroy(pointer p)
    {
      p->~T();
    }

    template <class U>
    struct rebind
    {
      typedef CFastPoolAllocator<U, CHUNK_SIZE> other;
    };
  };*/
} //namespace MWinnie



юзается так:

struct AAAA
{
            void *operator new( size_t real_size)
            {
                return MWinnie::Detail::CSizedAllocator<sizeof(AAAA)>::Allocate();
            }
            void operator delete( void * p)
            {
                MWinnie::Detail::CSizedAllocator<sizeof(AAAA)>::Deallocate(p);
            }
};
Re[5]: уберегите от велосипеда (очень простой алокатор)
От: Sni4ok  
Дата: 29.06.10 06:17
Оценка:
Здравствуйте, D14, Вы писали:

D14>Здравствуйте, Guard_h4s, Вы писали:


D14>
Pool alloc takes 21.587 sec.
D14>New/delete takes 0.04 sec.
D14>



ну вы конечно придумали синтетический тест, в котором boost::pool показывает наихудшее время, перепешите его так:

void run1()
{
    boost::object_pool<X> pool_alloc;
    clock_t start = clock();
    for (int j=0; j<m; ++j)
    {
        for (int i=0; i<n; ++i)
        {
            ptrs[i] = pool_alloc.construct();
        }
        //for (int i=0; i<n; ++i)
        //{
        //    pool_alloc.destroy(ptrs[i]);
        //}
    }
    clock_t finish = clock();
    printf ("Pool alloc takes %g sec.\n", (double)(finish - start) / CLOCKS_PER_SEC);
}


и пул уже впереди:
Pool alloc takes 0.015 sec.
New/delete takes 0.188 sec.

я boost::pool(точнее boost::fast_pool_allocator из boost::pool) активно использую для контейнера std::set — мелких обьектов, в котором количество записей может до сотни, однако вставок/удалений в контейнер в процессе работы происхоит миллионы, по сравнению со стандартным аллокатором- выигрышь очень большой.
Re[5]: уберегите от велосипеда (очень простой алокатор)
От: sidorov18 США  
Дата: 29.06.10 06:31
Оценка:
Скажите пожалуйста, а в чем смысл вот этого выражения:

free_list = *(void **)free_list;//тут, как я понял, вы присваиваете указателю его же значение?
Re[6]: уберегите от велосипеда (очень простой алокатор)
От: D14  
Дата: 29.06.10 06:59
Оценка: 1 (1)
Здравствуйте, sidorov18, Вы писали:

S>Скажите пожалуйста, а в чем смысл вот этого выражения:


S>
S>free_list = *(void **)free_list;//тут, как я понял, вы присваиваете указателю его же значение? 
S>

В велосипедном аллокататоре по той ссылке, что я привел, автор использует связанный список неиспользуемых чанков. Данная конкретная строка присваивает списку неиспольуемых чанков значение хвоста списка, т.к. голову собираемся использовать.
Re[6]: уберегите от велосипеда (очень простой алокатор)
От: D14  
Дата: 29.06.10 07:10
Оценка:
Здравствуйте, Sni4ok, Вы писали:

S>и пул уже впереди:

S>Pool alloc takes 0.015 sec.
S>New/delete takes 0.188 sec.

А теперь с "велосипедом" тайминги, будьте так добры.

S>я boost::pool(точнее boost::fast_pool_allocator из boost::pool) активно использую для контейнера std::set — мелких обьектов, в котором количество записей может до сотни, однако вставок/удалений в контейнер в процессе работы происхоит миллионы, по сравнению со стандартным аллокатором- выигрышь очень большой.


Ну, set, особливо его деструктор, тот ещё тормоз. Чтобы улучшить ситуацию идея не отдавать память вообще может быть применена, но не стоит ее абсолютизировать. Таки иногда имеет смысл в поворном использовании памяти, и в общем случае pool не заменяет fixed-size allocator.
Re[7]: уберегите от велосипеда (очень простой алокатор)
От: Sni4ok  
Дата: 29.06.10 07:40
Оценка:
Здравствуйте, D14, Вы писали:

D14>А теперь с "велосипедом" тайминги, будьте так добры.

не понял о чём вы.

D14>Ну, set, особливо его деструктор, тот ещё тормоз. Чтобы улучшить ситуацию идея не отдавать память вообще может быть применена, но не стоит ее абсолютизировать.


там при работе с этим контейнером память отдаётся- пулу, при частой аллокации-освобождении(а не как в вашем синтетическом тесте, когда много подрят выделений а потом много подрят освобождений) boost::pool сильно эффективней чем new/delete.
Re[5]: уберегите от велосипеда (очень простой алокатор)
От: Guard_h4s Россия  
Дата: 29.06.10 08:06
Оценка:
Здравствуйте, D14, Вы писали:

D14>Не может такого быть. С перегруженным delete. Не предназначен pool для этого. Хуже new/delete результат будет.

Пожалуйста:

#include <iostream>
#include <vector>
#include <ctime>
#include <boost\pool\pool.hpp>

template<class Resource, int PoolSize>
class ResourcePool
{
public:
    static void* operator new(size_t size)
    {
        if(!s_pool || size != sizeof(Resource))
            return ::operator new(size);

        void* ptr = 0;
        {
            ptr = s_pool->malloc();
            if(!ptr)
            {
                static const std::bad_alloc nomem;
                throw nomem;
            }
            ++s_usecount;
            s_pool->set_next_size(PoolSize);
        }
        return ptr;
    }
    static void operator delete(void* ptr)
    {
        if(!s_pool)
            ::operator delete(ptr);
        else
        {
            --s_usecount;
            s_pool->free(ptr);
        }
    }
public:
    static void InitPool()
    {
        if(!s_pool)
        {
            s_pool = new boost::pool<>(sizeof(Resource), PoolSize);
        }
    }
    static void DestroyPool()
    {
        if(s_pool)
        {
            delete s_pool;
            s_pool = 0;
        }
    }
    static size_t UseCount()
    {
        return s_usecount;
    }
private:
    static void *operator new[](std::size_t);
    static void operator delete[](void*);
    static boost::pool<>* s_pool;
    static size_t s_usecount;
};
template<class Resource, int PoolSize>
boost::pool<>* ResourcePool<Resource, PoolSize>::s_pool(0);
template<class Resource, int PoolSize>
size_t ResourcePool<Resource, PoolSize>::s_usecount(0);


struct X : public ResourcePool<X, 100000>
{
    int i;
};

const int n = 500000;
const int m = 10;
std::vector<X*> ptrs(n);

void run()
{
    clock_t start = clock();
    for (int j=0; j<m; ++j)
    {
        for (int i=0; i<n; ++i)
        {
            ptrs[i] = new X;
        }
        for (int i=0; i<n; ++i)
        {
            delete ptrs[i];
        }
    }
    clock_t finish = clock();
    printf ("Run() takes %g sec.\n", (double)(finish - start) / CLOCKS_PER_SEC);
}

int _tmain(int argc, _TCHAR* argv[])
{
    printf("Running generic new/delete: ");
    run();

    printf("Running overloaded new/delete: ");
    X::InitPool();
    run();
    X::DestroyPool();

    return 0;
}



Running generic new/delete: Run() takes 0.788 sec.
Running overloaded new/delete: Run() takes 0.05 sec.

Re[8]: уберегите от велосипеда (очень простой алокатор)
От: D14  
Дата: 29.06.10 08:15
Оценка:
Здравствуйте, Sni4ok, Вы писали:

S>Здравствуйте, D14, Вы писали:


D14>>А теперь с "велосипедом" тайминги, будьте так добры.

S>не понял о чём вы.
http://lj.rossia.org/users/kouzdra/664091.html#cutid3

S>там при работе с этим контейнером память отдаётся- пулу, при частой аллокации-освобождении(а не как в вашем синтетическом тесте, когда много подрят выделений а потом много подрят освобождений) boost::pool сильно эффективней чем new/delete.


Я так не думаю. Контейнер счетчика ссылок shared_ptr это частая аллокация/деаллокация?
Re: уберегите от велосипеда (очень простой алокатор)
От: remark Россия http://www.1024cores.net/
Дата: 29.06.10 14:33
Оценка: 9 (2)
Здравствуйте, Caracrist, Вы писали:

C>Нужно написать простой алокатор, размер элемента один, количество ограничено и известно. Необходим алокатор переиспользующий память. ценарий очень простой, создание и уничтожение одинаковых элементов.

C>Киньте ссылкой в готовую реализацию, буду благодарен.


inline size_t slab_allocator_granularity_init()
{
    SYSTEM_INFO info = {};
    GetSystemInfo(&info);
    return (size_t)info.dwAllocationGranularity;
}

size_t const slab_allocator_granularity = slab_allocator_granularity_init();

template<typename T>
class slab_allocator
{
public:
    slab_allocator()
        : head_()
    {
    }

    void* allocate()
    {
        void* mem = head_;
        if (mem)
        {
            head_ = *(void**)mem;
            return (T*)mem;
        }
        else
        {
            return alloc_slow();
        }
    }

    void deallocate(void* mem)
    {
        *(void**)mem = head_;
        head_ = mem;
    }

private:
    static size_t const sz = sizeof(T) >= sizeof(void*) ? sizeof(T) : sizeof(void*);

    void* head_;

    __declspec(noinline)
    void* alloc_slow()
    {
        void* mem = VirtualAlloc(0, slab_allocator_granularity, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
        if (mem == 0)
            throw bad_alloc();
        char* head = 0;
        char* pos = (char*)mem;
        char* end = pos + slab_allocator_granularity;
        for (; pos + sz <= end; pos += sz)
        {
            *(char**)pos = head;
            head = pos;
        }
        head_ = head;
        return allocate();
    }

    slab_allocator(slab_allocator const&)
    void operator = (slab_allocator const&);
};



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: уберегите от велосипеда (очень простой алокатор)
От: lost_guadelenn  
Дата: 01.07.10 11:23
Оценка:
Здравствуйте, Caracrist.

Недавно была такая задача. Решили взять аллокатор из Локи, как наиболее разумный.
И... в итоге сильно обработать напильником для оптимизации скорости (удаления).

Исходники вот
http://files.rsdn.ru/44186/loki_improved.zip
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.