【发布时间】:2023-03-07 19:40:01
【问题描述】:
问题背景
我目前正在使用 OpenMP 并行编程模型分析和改进用 C++ 编写的计算密集型并发应用程序的性能。我使用分析工具看到代码的特定并行区域将频率降至 200MHz,这意味着大量的周期用于系统时间或 CPU 空闲。我已确定此问题的原因是同时执行大量内存分配,导致分配器同步线程并浪费大量等待时间。
不过,这些内存分配是vector<double> 运算符重载的结果,该运算符在相关并行循环期间大量使用(从现在开始感兴趣的区域)。运算符重载的作用如下:
std::vector<double> operator+( const std::vector<double>& v1 , const std::vector<double>& v2 )
{
std::vector<double> v = v1;
for( unsigned int i=0; i < v1.size() ; i++ )
{ v[i] += v2[i]; }
return v;
}
这只是一个示例,其他算术运算符以及向量和标量(double 类型)的操作也是如此。如您所见,一个新的向量被初始化,然后作为操作的结果返回。这会导致至少一个 malloc 和一个 free。但正如我所说,这在感兴趣区域的一次迭代中被调用了多次,并且这个循环在大量并行线程(最多 48 个)上运行大量迭代。此操作调用的一个示例如下:
std::vector<double> corner_point= 0.5*(my_voxel_center+other_voxel_center);
在这种情况下,两个操作一个接一个地完成(向量的+,然后向量和标量的*),然后将结果分配给新创建的向量。
问题
所以,我的问题如下:正如我们已经看到它的表现有多糟糕,这应该是运算符重载的最佳实践,特别是对于像vector<> 这样的类型,以避免分配和释放每次调用一个新向量?有没有更好的写法?
我已阅读“https://stackoverflow.com/questions/4421706/what-are-the-basic-rules-and-idioms-for-operator-overloading”帖子寻求帮助,但没有 cmets关于重载函数中内存的具体使用,以及这些函数在并发应用程序上的执行方式。
我知道也许没有其他方法可以解决这个问题,在这种情况下:我应该把注意力集中在哪里来解决这个问题?我的想法是:
- 使用另一个可以更好地处理并发的分配器。
- 根本不使用运算符重载来避免分配这些时间向量,并且每次出现在代码中时都使用循环执行操作。在这种情况下,代码大小的增长应该不是问题,正如我所说,这个应用程序对计算至关重要,这是最重要的事情。
【问题讨论】:
标签: c++ memory-management parallel-processing operator-overloading