Community
cancel
Showing results for 
Search instead for 
Did you mean: 
sunqxj
Beginner
49 Views

How to lock the whole concurrent_hash_map not warping a portion of code with mutex?

Originally, I thought TBB provide the range iterator will block all access to the hash table itself. I tried to see whether or not it is the case. I found out, it does not provide this protection. I create two tasks and one for insert number while the other to print all the numbers in the current map. The result gives me either segmentation fault or just not run correctly. Would some one tell me how to lock the whole hash_map table instead of just using mutex around the print map code which just prevents the other task to print it. Also, I tried if I wrap the code with mutex as
task* execute() {
mutex printMutex;
mutex::scoped_lock mylock(printMutex);
    cout << "************** print map ***************** " << endl;
    for (CHM::iterator it = chm.begin(); it != chm.end(); it++) {
        cout << it->first << " -> " << it->second << endl;
    }
    return NULL;
  }

It still gives segmentation fault error. Would some one give me a hint? I am quite new for parallel programming. Thanks.

Here is the code :

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define N 100000

using namespace std;
using namespace tbb;

class hash_func {
    public:
        static size_t hash( const int &n)
        {
            return n;
        }

        static bool equal( const int &s1, const int &s2)
        {
            return s1 == s2;
        }
};

typedef concurrent_hash_map CHM;



class FObject {
    public:
        CHM &chm;
        int *number;

  FObject( CHM &chm_, int * _number ) : chm(chm_), number(_number) {}

        void operator()( const blocked_range r ) const
        {
            for ( int ptr =r.begin(); ptr!=r.end(); ++ptr ) {
                CHM::accessor chm_acc;
                chm.insert( chm_acc, number[ptr]);
                chm_acc->second += 1;
        cout << "insert " << number[ptr]<< endl;
            }
        }
};

class InNum: public task {
public:
  InNum(CHM & _chm, int *_num):chm(_chm),num(_num) {
  }
  CHM &chm;
  int * num ;

  task* execute() {
    parallel_for( blocked_range(0, N), FObject(chm, num) );
    return NULL;
  }
};
class PrintNum: public task {
public:
  PrintNum(CHM & _chm):chm(_chm) {
  }
  CHM &chm;

  task* execute() {
    cout << "************** print map ***************** " << endl;
    for (CHM::iterator it = chm.begin(); it != chm.end(); it++) {
        cout << it->first << " -> " << it->second << endl;
    }
    return NULL;
  }
};
int main()
{
    task_scheduler_init init(-1);

    int words;
    CHM chm;
    for(int i = 0 ; i < N; ++i) {
      words = rand();
    }

    empty_task* parent = new(task::allocate_root()) empty_task;
    parent->set_ref_count(6);
    InNum *inNum = new( parent->allocate_child()) InNum(chm, words);

    parent->spawn(*inNum);
    for(int j = 1; j <= N; ++j) {
      if (j% 20000 == 0 ) {
    PrintNum *printNum = new (parent->allocate_child()) PrintNum(chm);
    parent->spawn(*printNum);
      }

    }

    return 0;
}

0 Kudos
6 Replies
RafSchietekat
Black Belt
49 Views

The iterators in a concurrent_hash_map are only meant for non-cuncurrent access, so use concurrent_unordered_map instead.
sunqxj
Beginner
49 Views

Thanks Raf for your answer.
I have another question. For concurrent access, in the reference manual, it says concurrent_unordered_map provides concurrent insertion and traversal. Does it also include concurrent modification of the map value? Could you give me a hint on it such as the description of the detail implementation and performance comparing with concurrent_hash_map. Thanks ahead.

RafSchietekat
Black Belt
49 Views

I guess you'll have to update nonatomically (erase followed by insert). Additional comments welcome.
Anton_M_Intel
Employee
49 Views

Right, iterator and range in concurrent_hash_map do not work in conjunction with concurrent operations (count, find, insert, erase). concurrent_unordered_map supports concurrent iterators but at the price of removing concurrent erase operation (only serial unsafe_erase) and the burden of item's access synchronization lies on your shoulders.
sunqxj
Beginner
49 Views

Thanks for both of your reply.

I am trying to figure out what is the best choice for me concurrent_unordered_map or concurren_hash_map.
For my application, my hash key is a structure pointer and my hash value is a link-list. If if the applying hash function on the key results in the same value, then this item will be inserted into the corresponding list. Otherwise, it will insert this pair. I will never delete a pair modification. Sorry for the confusion, I put in my last comment.
For the concurrent traverse, it is not a hard requirement. Therefore, I would like to choose a class which gives me better performance on search, update, insert.
If concurrent_hash_map as what Anton described, I think it would be better for me to swap back to concurrent_hash_map. Is it right? Really appreciate of your suggestions.
Another question, I read the source code of concurrent_hash_map and I am not sure whether or not my understanding is correct. For each node of hash table, there is a spin_rw_mutex asssociated with it, which is the memory cost we pay for concurrency. Also, are there other major memory cost for using concurrent vector?
Also, if the initialization size for the bucket is not provided, will concurrent_hash_map dynamically reorganize the bucket list to get better performance?
Anton_M_Intel
Employee
49 Views

I don't quite understand what you described above regarding "hash" key, value, and function.. Anyway, if you don't need concurrent erasure and traverse, both containers fit your needs. There is still difference in the interface - .._hash_map provides access syncronization to the elements using spin_rw_locks hidden behind the accessors; .._unordered_map returns iterators which effectivelyare just pure pointers, so you need to synchronizeany updates yourself. Here is main performance difference - if you are able to synchronize updates more effectively than by spin_rw_mutex - use .._unordered_map, otherwise .._hash_map may be somewhat faster. Another difference, which may be important for [soft] realtime apps where preemption+spin_locks can harm, is that .._hash_map is build using fine-grain locking of buckets while .._unordered_map is lock-free (which does not mean it involves less memory synchronization on average).
Both hash tables are designed to grow dynamically. However, it is better for performance and scalability to specify reasonable initial capacity before using them (e.g., rehash() method).
Reply