繁体   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