簡體   English   中英

OpenMP(C / C ++):共享unordered_map的有效方法 <string, vector<int> &gt;和向量 <int> 螺紋之間

[英]OpenMP(C/C++): Efficient way of sharing an unordered_map<string, vector<int>> and a vector<int> between threads

我有一個要並行執行的for循環,但是線程必須共享unordered_mapvector

因為for循環有些大,所以我將在此處對此進行簡要概述,以便使我的主要問題明確。 請閱讀評論。

   unordered_map<string, vector<int>> sharedUM;

   /*
      here I call a function that updates the unordered_map with some
      initial data, however the unordered_map will need to be updated by
      the threads inside the for loop
   */

   vector<int> sharedVector;
  /* 
     the shared vector initially is empty, the threads will 
    fill it with integers, the order of these integers should be in ascending
    order, however I can simply sort the array after all the 
    threads finish executing so I guess we can assume that the order 
    does not matter
  */

   #pragma omp parallel for
   for(int i=0; i<N; i++){

      key = generate_a_key_value_according_to_an_algorithm();
      std::unordered_map<string, vector<int>::iterator it = sharedUM.find(key);

      /*
       according to the data inside it->second(the value), 
       the thread makes some conclusions which then
       uses in order to figure out whether 
       it should run a high complexity algorithm
       or not.
      */
       bool conclusion = make_conclusion();

       if(conclusion == true){

           results = run_expensive_algorithm();

          /*
             According to the results, 
             the thread updates some values of
             the key that it previously searched for inside the unordered_map
             this update may help other threads avoid running 
             the expensive algorithm
          */

       }

       sharedVector.push_back(i);

   }

最初,我按原樣保留了代碼,因此我只在for循環上使用了#pragma ,但是在sharedVector的更新方面遇到了一些問題。 因此,我決定使用簡單的鎖,以便在寫入向量之前強制線程獲取該鎖。 所以在我的實現中,我有這樣的事情:

      omp_lock_t sharedVectorLock;
      omp_init_lock(&sharedVectorLock);
      ...
      for(...)
      ...
       omp_set_lock(&sharedVectorLock);
       sharedVector.push_back(i);
       omp_unset_lock(&sharedVectorLock);
      ...
      omp_destroy_lock(&sharedVectorLock);

我已經運行了很多次應用程序,並且一切似乎都運行良好,直到我決定自動重新運行它很多次,直到得到錯誤的結果為止。 因為我對OpenMP和整個線程領域還很陌生,所以我沒有意識到在編寫者更新某些共享數據時我們應該鎖定所有讀者的事實。 如您在我的應用程序中所見,線程總是從unordered_map中讀取一些數據,以便得出一些結論並了解分配給他們的密鑰的知識。 但是,如果兩個線程必須使用同一個鍵,而另一個線程試圖讀取該鍵的值,而另一個線程已經達到更新這些值的程度,會發生什么呢? 我認為這就是我的問題所在。

但是我現在的主要問題是我不確定什么是避免此類事情發生的最佳方法。 就像我的系統在99%的時間內都能正常工作,但是那1%的東西會毀了一切,因為很少有兩個線程被分配相同的鍵,這又是因為我的unordered_map通常很大。

鎖定unordered_map可以完成我的工作嗎? 很有可能,但這並不是有效的,因為想要與鍵x一起使用的線程A必須等待已經與鍵y一起使用的線程B才能完成,其中y可以不同於x

所以我的主要問題是,我應該如何解決這個問題? 當且僅當兩個線程使用同一個鍵時,才能鎖定unordered_map

先感謝您

1關於使用鎖和互斥鎖。 您必須在並行塊外部 (在#pragma omp parallel之前)聲明並初始化鎖變量,然后在並行塊內使用它們:(1)獲取一個鎖(如果另一個線程將其鎖定,則可能會阻塞),(2)根據競爭條件更改變量,(3)釋放鎖。 最后,退出並行塊后銷毀它。 在並行塊內部聲明的鎖在線程本地,因此無法提供同步。 這可以解釋您的問題。

2寫入復雜的C ++容器。 OpenMP最初是為簡單的FORTRAN do循環設計的(類似於具有整數控制變量的循環的C / C ++)。 所有更復雜的事情都會讓您頭痛。 為了安全起見,對C ++容器的任何非恆定操作都必須在一個鎖(對同一容器的任何這樣的操作使用相同的鎖)或omp關鍵區域(對以下任何一種這樣的操作使用相同的名稱)內執行同一容器)。 這包括pop()push()等,除了簡單的讀取之外,什么都沒有。 僅當此類非恆定容器操作僅花費一小部分時間時,這才可以保持效率。

3如果我是您,則不會為openMP所困擾(我曾經用過它,但現在對此感到后悔)。 使用C ++,您可以使用TBB,它還帶有一些線程安全但無鎖的容器。 它還使您可以考慮以任務而非線程的方式來考慮這些任務,這些任務是遞歸執行的(父任務產生子任務等),但是TBB例如具有一些用於並行for循環的簡單實現。

一種替代方法是使用TBB的current_unordered_map

您不必使用TBB其余的並行性支持(盡管如果您從頭開始使用C ++,肯定比OpenMP更“ c ++式”)。

可能這可以幫助:

    vector<bool> sv(N);

更換

    sharedVector.push_back(i);   

通過

    sv[i]=true;

這樣可以避免鎖定(非常耗時),並且sharedVector可以輕松地進行排序,例如

    for(int i=0; i<N;i++){
        if(sv[i])sharedVector.push_back(i);
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM