TBB之concurrent_hash_map

Intel TBB 提供高併發的容器類,Windows或者Linux線程能使用這些容器類或者和基於task編程相結合(TBB)。

一個併發容器允許多線程同時對容器訪問和更改條例,典型的C++STL容器類不允許 併發更新,嘗試並行更改他們引起惡化容器。STL容器能使用互斥鎖來包裝,目的讓他們安全訪問,通過只讓一個線程同時操作一個容器,但是這種方式消除了並行,限制了並行提速。

Intel TBB提供的容器有更高水準的併發性,通過以下方法:

  • Fine-grained locking:只通過鎖需要鎖的部分,在容器上實現多線程操作,一旦多線程訪問不同部分,他們能同步進行。
  • Lock-free techniques:改正其他干擾線程的影響。

注意高併發容器是有代價的,他們典型的有更高的消耗比STL容器,對高併發容器操作比STL容器消耗更多,因此,當使用高併發容器比STL容器提速的時候,他們是優於串行的性能。

concurrent_hash_map

concurrenthashmap<Key,T,HashCompare> 是一個hash表,允許並行訪問,表是一個從Key到類型T的映射,類型HashCompare定義怎樣hash一個Key和怎樣比較2個Key。

下面的例子建立一個concurrent_hash_map,這個Key是字符串,對應的數據是矩陣Data中字符串出現的次數。

#include "tbb/concurrent_hash_map.h"
#include "tbb/blocked_range.h"
#include "tbb/parallel_for.h"
#include <string>

using namespace tbb;
using namespace std;

// Structure that defines hashing and comparison operations for user's
type.
struct MyHashCompare {
    static size_t hash( const string& x ) {
        size_t h = 0;
        for( const char* s = x.c_str(); *s; ++s )
            h = (h*17)^*s;
        return h;
    }
    //! True if strings are equal
    static bool equal( const string& x, const string& y ) {
        return x==y;
    }
};
// A concurrent hash table that maps strings to ints.
typedef concurrent_hash_map<string,int,MyHashCompare> StringTable;

// Function object for counting occurrences of strings.
struct Tally {
    StringTable& table;
    Tally( StringTable& table_ ) : table(table_) {}
    void operator()( const blocked_range<string*> range ) const {
        for( string* p=range.begin(); p!=range.end(); ++p ) {
            StringTable::accessor a;
            table.insert( a, *p );
            a->second += 1;
        }
    }
};
const size_t N = 1000000;
string Data[N];
void CountOccurrences() {
// Construct empty table.
StringTable table;

// Put occurrences into the table
parallel_for( blocked_range<string*>( Data, Data+N, 1000 ),
                Tally(table) );

// Display the occurrences
for( StringTable::iterator i=table.begin(); i!=table.end(); ++i )
    printf("%s %d\n",i->first.c_str(),i->second);
}

concurrenthashmap 扮演一個類型std::pair<constKey,T> 元素的容器,當訪問一個容器元素時,你對更新它或者讀取它感興趣,模板類concurrent_hash_map支持這2個目的,對應類accessor和const_accessor,其擔當智能指針,accessor表示更新(write)訪問,一旦它指向一個元素,所有其他嘗試尋找這個表的key都會被阻止,直到accessor被完成。const_accessors是類似的,除了只表示讀訪問,多const_accessors能同時指向相同的元素,在頻繁的讀但是很少更新的情況下,這個特點極大的改善並行情形。

方法find和insert採用accessor或者const_accessor作爲一個參數,告訴concurrent_hash_map是否你要求更新或者只讀訪問,一旦這個方法返回,直到訪問結束,accessor或者const_accessor纔會被銷燬,因爲對元素的訪問阻止其他線程,所以儘可能的縮減accessor或者const_accessor生命週期,爲了這麼做,我們在內部塊中聲明它,儘可能在塊結束前釋放這個accessor,下面的例子是重寫循環體,使用release結束accessor的生命週期:

StringTable accessor a;
for( string* p=range.begin(); p!=range.end(); ++p ) {
    table.insert( a, *p );
    a->second += 1;
    a.release();
}

方法remove(key)也能同時操作,它隱式請求寫訪問,因此,在刪除這個Key之前,它等待其他現存key上的訪問結束。

發佈了27 篇原創文章 · 獲贊 26 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章