C++ Lock-Free шаблонный ObjectPool

они существуют?

*добавлено для уточнения:

существует ли какая-либо полезная библиотека, которая реализует без блокировки (которая является потокобезопасной и может реализовывать спин-блокировку или другую облегченную синхронизацию) ObjectPool ( http://en.wikipedia.org/wiki/Object_pool_pattern ) написано на языке C++ с использованием шаблона?


person uray    schedule 03.06.2010    source источник
comment
Какие вопросы я могу задать здесь? Вопросы по программированию, конечно! Пока ваш вопрос: подробный и конкретный, написанный ясно и просто и представляющий интерес для других программистов -- постарайтесь уделить немного больше времени своему вопросу, чтобы мы могли действительно ответить на него.   -  person Ben Burnett    schedule 03.06.2010
comment
почему непонятно? существует ли какая-либо пригодная для использования библиотека, которая реализует безблокировку (которая является потокобезопасной и может реализовывать спин-блокировку или другую облегченную синхронизацию) ObjectPool (en.wikipedia.org/wiki/Object_pool_pattern), написанный на языке C++ с использованием шаблона?   -  person uray    schedule 03.06.2010
comment
Я хочу сказать, что вы включили в свой комментарий примерно в три раза больше информации, чем в свой вопрос, и я думаю, что должно быть наоборот.   -  person Ben Burnett    schedule 03.06.2010
comment
*Вопрос обновлен, спасибо..   -  person uray    schedule 03.06.2010


Ответы (3)


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

он может выполнять 16,6 миллионов операций заимствования-возврата в секунду на Intel Core 2 Quad 2,4 ГГц win7-x64 с использованием 4 потоков.

`

#define CACHE_LINE_SIZE 64
#define alignCache  __declspec(align(CACHE_LINE_SIZE))
#ifdef _WIN64
#   define alignArch  __declspec(align( 8))
#else
#   define alignArch  __declspec(align( 4))
#endif

class InterlockedFlag {
    protected:
        alignArch volatile unsigned int value;
    public: 
        inline void set(unsigned int val) {
            this->value = val;
        }
        inline unsigned int exchange(unsigned int val) {
            return InterlockedExchange(&this->value,val);
        }
};

#pragma pack(push,1)
template <typename T> struct ObjectPoolNode {
    ObjectPoolNode<T>* next;
    T data;
    ObjectPoolNode() : next(nullptr) { };
};
#pragma pack(pop,1)

template <typename T> struct alignCache ObjectPoolList {
    ObjectPoolList<T>* nextList;
    char pad1[CACHE_LINE_SIZE - sizeof(ObjectPoolList<T>*)];
    ObjectPoolNode<T>* first;
    char pad2[CACHE_LINE_SIZE - sizeof(ObjectPoolNode<T>*)];
    InterlockedFlag consumerLock;
    char pad3[CACHE_LINE_SIZE - sizeof(InterlockedFlag)];
    ObjectPoolNode<T>* last;
    char pad4[CACHE_LINE_SIZE - sizeof(ObjectPoolNode<T>*)];
    InterlockedFlag producerLock;
    char pad5[CACHE_LINE_SIZE - sizeof(InterlockedFlag)];
    ObjectPoolNode<T>** storage;                
    char pad6[CACHE_LINE_SIZE - sizeof(ObjectPoolNode<T>**)];
    size_t available;
    size_t count;

    ObjectPoolList(size_t count)
        : producerLock(false), consumerLock(false)
    {
        this->available = this->count = count;
        this->storage = new ObjectPoolNode<T>*[count+1];
        for(size_t i=0 ; i<count+1 ; i++) {
            this->storage[i] = new ObjectPoolNode<T>;
        }
        for(size_t i=0 ; i<count ; i++) {
            this->storage[i]->next = this->storage[i+1];
        }
        this->first = this->storage[0];
        this->last  = this->storage[count];         
    }

    ~ObjectPoolList() {
        this->count = 0;
        this->available = 0;
        if(this->storage) {
            for(size_t i=0 ; i<count+1 ; i++) {
                delete this->storage[i];
            }
            delete[] this->storage;
            this->storage = NULL;
        }
    }
};

template <typename T> class alignCache ObjectPool {
private:
    ObjectPoolList<T>** lists;
    char pad1[CACHE_LINE_SIZE - sizeof(ObjectPoolList<T>**)];
    size_t available;
    size_t listCount;
public:
    ObjectPool(size_t count,size_t parallelCount = 0) {
        this->available = count;
        this->listCount = parallelCount;
        if(this->listCount == 0) {
            this->listCount = getSystemLogicalProcessor(); //default
        }       
        this->lists = new ObjectPoolList<T>*[this->listCount];
        for(size_t i=0 ; i<this->listCount ; i++) {
            this->lists[i] = new ObjectPoolList<T>(count/this->listCount);
        }
        for(size_t i=0 ; i<this->listCount-1 ; i++) {
            this->lists[i]->nextList = this->lists[i+1];
        }
        this->lists[this->listCount-1]->nextList = this->lists[0];
    }

    ~ObjectPool() {
        if(this->lists) {
            for(size_t i=0 ; i<this->listCount ; i++) {
                delete this->lists[i];
            }
            delete[] this->lists;
            this->lists = NULL;
        }
        this->available = 0;
        this->listCount = 0;
    }

    T* borrowObj() {
        ObjectPoolList<T>* list = this->lists[0];
        while( !list->available || list->consumerLock.exchange(true) ) {
            if(!this->available) {
                return NULL;
            }
            list = list->nextList;
        }
        if(list->first->next) {
            ObjectPoolNode<T>* usedNode = list->first;
            list->first = list->first->next;
            list->available--;
            this->available--;
            list->consumerLock.set(false);
            usedNode->next = nullptr;
            return &usedNode->data;                     
        }           
        list->consumerLock.set(false);
        return NULL;
    }

    void returnObj(T* object) {
        ObjectPoolNode<T>* node = (ObjectPoolNode<T>*)(((char*)object) - sizeof(ObjectPoolNode<T>*));
        ObjectPoolList<T>* list = this->lists[0];
        while( list->producerLock.exchange(true) ) {
            list = list->nextList;
        }
        list->last->next = node;
        list->last       = node;
        list->producerLock.set(false);
        list->available++;
        this->available++;
    }
};

`

person uray    schedule 07.06.2010
comment
Выглядит специфично для Windows (InterlockExchange говорит), так ли это? - person Matthieu M.; 30.07.2014
comment
не сбрасывает счетчик на 0 в ~ ObjectPoolList () до того, как этот цикл удаляет элементы хранения неправильно? - person vavan; 09.02.2016
comment
разве это-›доступно-- и это-›доступно++ не должно быть атомарным (связанным)? - person vavan; 10.02.2016

Лучше всего проверить Boost.Pool и напишите для него свободный от блокировок интерфейс распределителя/мьютекса.

person Kornel Kisielewicz    schedule 03.06.2010
comment
если я использую boost::pool и просто заменяю его распределитель на распределитель без блокировки, я думаю, что это не делает пул свободным от блокировки или безопасным для потоков событий, поскольку boost::pool может реализовывать список фрагментов, используя связанный список или что-то, что не является потокобезопасным и требует синхронизации не в аллокаторе, а в методе заимствованияReusable() и returnReusable(), тогда это будет не lock-free пул, я прав? - person uray; 03.06.2010

Учитывая, что есть очереди без блокировок, я бы сказал, что если пула не существует, вы наверняка можете создать один (почти) свободный от блокировок пул.

В сочетании с классическим распределителем tmalloc (который может блокировать, но максимально избегает его), я думаю, вы приблизитесь к своей цели.

person Matthieu M.    schedule 03.06.2010
comment
меня беспокоит, если это действительно просто или близко к безблокировке с высокой производительностью, почему после этих лет многоядерности никто еще не реализовал это?. один из моих случаев, уже заданный здесь: stackoverflow.com/questions/2953554/recycle-freed-objects . Я реализую очередь без блокировки, но хочу немного ее оптимизировать, уменьшив выделение кучи с помощью ObjectPool. но будет ли какое-либо преимущество в производительности, если пул узлов очереди очереди реализован с использованием очереди - person uray; 03.06.2010
comment
Хм, это кажется совершенно излишним :p Боюсь, тогда это не сработает. - person Matthieu M.; 04.06.2010