2010-06-03 3 views
5

Существуют ли они?C++ Lock-Free templated ObjectPool

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

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

+2

Какие вопросы я могу задать здесь? Вопросы программирования, конечно! Пока ваш вопрос: подробные и конкретные написаны ясно и просто интерес для других программистов - Try тратить немного больше времени на ваш вопрос так, чтобы мы могли на самом деле быть в состоянии ответить на него. –

+0

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

+0

Могу сказать, что вы включили в свой комментарий примерно три раза больше информации, чем в своем вопросе, и я думаю, что все должно быть наоборот. –

ответ

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++; 
    } 
}; 

`

+0

Выглядит Windows специфически ('InterlockExchange' говорит), так ли это? –

+0

не сбрасывает значение count в 0 в ~ ObjectPoolList() до того, как этот цикл удалит элементы хранения неправильно? – vavan

+0

не должен «this-> available--» и «this-> available ++» быть атомарным (с блокировкой)? – vavan

1

Ваш лучший выбор - проверить Boost.Pool и написать для него свободный интерфейс allocator/mutex.

+1

, если я использую boost :: pool и просто заменяю его распределитель блокировочным распределителем, я думаю, что он не делает пул блокировкой или потоком событий безопасным, поскольку boost :: пул может быть реализован список фрагментов с использованием связанного списка или что-то, что не является потокобезопасным, и требуется синхронизация не в распределителе, а в методе заимствованияReusable() и returnReusable(), то это был бы не блокирующий пул, am Я прав? – uray

0

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

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

+1

моя забота заключается в том, что если это действительно легко или близко к блокировке с высокой производительностью, то почему после этих лет многоядерности никто ее уже не реализовал ?. один из моих случаев, уже поставленный под сомнение здесь: http: //stackoverflow.com/questions/2953554/recycle-freed-objects. Я реализую незаблокированную очередь, но я хочу немного ее оптимизировать, уменьшив распределение кучи с помощью ObjectPool. но будет ли какое-либо преимущество в производительности, если «пул узлов очередей» в очереди реализован с использованием очереди – uray

+0

Hum, что кажется довольно избыточным: p боюсь, что тогда это не сработает. –