并行程序上向量算术运算符重载的最佳实践 问题背景 我目前正在分析和改进一个用OpenMP并行编程模型编写的C++计算密集型并发应用程序的性能。我在分析工具中看到,代码的一个特定并行区域将频率降低到200MHz,这意味着在系统时间或CPU空闲时会花费大量的周期。我已确定此问题的原因是并发执行了大量内存分配,导致分配器同步线程,并浪费了大量等待时间

并行程序上向量算术运算符重载的最佳实践 问题背景 我目前正在分析和改进一个用OpenMP并行编程模型编写的C++计算密集型并发应用程序的性能。我在分析工具中看到,代码的一个特定并行区域将频率降低到200MHz,这意味着在系统时间或CPU空闲时会花费大量的周期。我已确定此问题的原因是并发执行了大量内存分配,导致分配器同步线程,并浪费了大量等待时间,c++,memory-management,parallel-processing,operator-overloading,C++,Memory Management,Parallel Processing,Operator Overloading,然而,这些内存分配是向量操作符重载的结果,该重载在所讨论的并行循环(从现在开始关注区域)中被大量使用。运算符重载的功能如下所示: std::vector操作符+(常量std::vector&v1,常量std::vector&v2) { 标准:向量v=v1; for(无符号整数i=0;i

然而,这些内存分配是
向量
操作符重载的结果,该重载在所讨论的并行循环(从现在开始关注区域)中被大量使用。运算符重载的功能如下所示:

std::vector操作符+(常量std::vector&v1,常量std::vector&v2)
{
标准:向量v=v1;
for(无符号整数i=0;i
这只是一个示例,其他算术运算符以及向量和标量(
double
type)运算也是如此。如您所见,一个新的向量被初始化,然后作为操作的结果返回。这会导致至少一个malloc和一个free。但是正如我所说的,在感兴趣区域的一次迭代中,这个循环被调用了好几次,并且这个循环在大量并行线程(最多48个)上运行了大量的迭代。此操作调用的一个示例如下:

std::向量角点=0.5*(我的体素中心+其他体素中心);
在这种情况下,一个接一个地执行两个操作(向量的
+
,然后向量和标量的
*
),然后将结果分配给新创建的向量

问题: 因此,我的问题是:正如我们所看到的那样,它的性能有多差,这应该是运算符重载的最佳实践,特别是在
vector
之类的类型上,以避免每次调用它时分配和释放新的向量?有没有更好的写作方法

我已经读过了“https://stackoverflow.com/questions/4421706/what-are-the-basic-rules-and-idioms-for-operator-overloading“发布搜索帮助,但没有关于重载函数中内存的具体使用以及这些函数在并发应用程序中的执行情况的评论

我知道,在这种情况下,可能没有其他办法:我应该把注意力集中在哪里来解决这个问题?我的想法是:

  • 使用另一个更好地处理并发性的分配器
  • 根本不使用操作符重载来避免分配这些时间向量,并且每次在代码中出现时都使用循环执行操作。在这种情况下,代码大小的增长不应该是一个问题,正如我所说的,这个应用程序是计算关键的,这是最重要的

    • 下面的建议与运算符重载关系不大,而与一般向量的使用有关

      有几件事需要改进:

      首先,在你复制任何东西之前,你一定要使用

      std::vector<double> operator+( const std::vector<double>& v1 , const std::vector<double>& v2 )
      {
       std::vector<double> v;
       v.reserve(v1.size() + v2.size());
       v = v1;
       for( unsigned int i=0; i < v1.size() ; i++ )
       { v[i] += v2[i]; }
       return v; 
      }
      

      如果
      v1
      必须保持不变,也许也值得考虑。如果没有,那么只需重载
      操作符+=
      并使用它可能是值得的



      当然,一定要尝试不同的优化级别,有时候
      -Os
      -O2
      可能比
      -O3

      更好,谢谢你的回答!我将注意到在矢量复制和分配方面的这些改进。但是我认为你误解了加法重载的含义。我想用数学的方法加上这两个向量:假设两个操作数的大小相同,将向量的每个元素与其在另一个向量中对应的元素相加。这就是
      for
      循环的原因。@Marc啊,我的错。我弄错了。我的答案是无效的,我将删除它一会儿,但你仍然可以考虑,如果你需要保持原始向量不变-如果你可以添加到它,而不是创建一个新的向量,它将不需要分配。另外,如果您可以在编译时指定大小,
      std::array
      比vector(无分配)便宜得多。
      std::vector<double> operator+( const std::vector<double>& v1 , const std::vector<double>& v2 )
      {
       std::vector<double> v;
       v.reserve(v1.size() + v2.size());
       v.insert(v.end(), v1.begin(), v1.end());
       v.insert(v.end(), v2.begin(), v2.end());
       return v; 
      }