【问题标题】:Usage of TBB parallel_for_each() with reduction to Atomic counters使用 TBB parallel_for_each() 并减少到原子计数器
【发布时间】:2014-05-06 05:49:25
【问题描述】:

这是用于 TBB 代码的原子计数器类。

#include <atomic>
template <typename T = int>
struct AtomicCounter {

private:
std::atomic<T> value;
std::atomic<T> num_inter;

public:
void put_inter(T niter) {
T x = num_inter.fetch_add(niter);
}

T get_inter() { return num_inter.load(); }

void increment() {  ++value; }
void put(T data) {  value.fetch_add(data) ; // ignore the old value returned }
void decrement() {  --value; }
T    get()       { return value.load(); }

};

我正在使用并行 foreach 循环,其中每个线程都有自己的本地计数器值,并使用 AtomicCounter 类的 put 函数更新全局计数器对象。并行 foreach 的函数对象将此全局计数器作为参考。

template<typename counter_t>
struct my_functor {
 public:
  my_functor(){}
  my_functor(counter_t &c):m_c(c){ } 

  template<typename label_t>
  void operator()(label_t label) const {

  // do some work --> local counter

   m_c.put(local_value); // put the local counter value in the global counter 
  } 

  private:
    counter_t& m_c;
    mutable int local_value; //local counter value  

  }

   // for TBB 
 //declare an object of this class as a global counter.

 AtmoicCounter<int> c;
 my_functor<atomicCounter<int>>  func(c) //functor object
 parallel_for_each( mywork.begin(), mywork.end(), func)    //TBB parallel for each

所以基本上,每个线程都会更新全局计数器。我声明 std::atomic 成员的 atomicCounter 类有什么问题吗?我正在使用具有 C++11 功能和英特尔 TBB 4.2 的 GCC 4.7.2

谢谢!

【问题讨论】:

  • 您是在问您的 实现是否正确,GCC 的原子实现是否正确? (如果是后者,您遇到了什么问题?)
  • @Mat- 我刚刚在某处读到 GCC 4.7 中的原子实现与 GCC 4.8 中的不同之处在于对无锁指令的支持。另外,我想确认一下我使用原子计数器保持全局计数器值的方式是否正确?
  • 您正在将 reduce 操作打包成普通的 for_each。相反,您应该使用适当的 a reduce 函数(xxx::parallel_reduce)并完全避免使用原子。

标签: c++ multithreading c++11 tbb reduction


【解决方案1】:

我认为声明中没有任何问题。如果由于某种原因您不确定 GCC atomics,请使用我确定的 TBB atomics。

但我发现问题在于使用 mutable 关键字来访问来自 const 仿函数的字段。 mutable 通常是糟糕设计的标志。在并行编程中,它的使用特别容易出错。如果一个被并行调用的方法标记为 const,这通常意味着它将在仿函数类的同一个实例上被并行调用。因此,您在可变字段上存在数据竞争。

您可以将 local_value 移动到 operator() 的范围内,而不是打破 const 限制,或者尝试线程本地容器 tbb::combinabletbb::enumerable_thread_specific 缓存特定于线程的部分值,或者应用并行缩减算法tbb::parallel_reduce 您可以安全地将部分结果存储在仿函数体中。

【讨论】:

  • “如果一个被并行调用的方法标记为 const”,这也可能意味着它是为仿函数类的一个临时实例调用的,该实例随后将被销毁,如tbb::parallel_for。在任何情况下,玩弄 constness 都会导致问题。
  • 在这种情况下,线程安全的临时对象会起作用,因为 local_value 似乎只在operator()的范围内使用
  • 正确,但是这句话是通用的,适用于其他情况。无论如何,我不是在争论 bur,而是在扩展它。
猜你喜欢
  • 1970-01-01
  • 2020-05-07
  • 2015-10-21
  • 1970-01-01
  • 2022-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多