c++ - 并发程序中向量算术运算符重载的最佳实践
问题描述
问题上下文
我目前正在使用 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”帖子寻求帮助,但没有具体的评论重载函数内部的内存使用,以及它们如何在并发应用程序上执行。
我知道,在这种情况下,也许没有其他方法可以解决这个问题:我应该把注意力集中在哪里来解决这个问题?我的想法是:
- 使用另一个可以更好地处理并发的分配器。
- 根本不使用运算符重载来避免分配这些时间向量,并且每次出现在代码中时都使用循环执行操作。在这种情况下,代码大小的增长不应该成为我所说的问题,这个应用程序是计算关键的,这是最重要的事情。
解决方案
下面的建议与运算符重载几乎没有关系,而是与一般向量的使用有关。
有几点需要改进:
reserve()
首先,在复制任何内容之前,您绝对应该使用, 。
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;
}
这样,每次操作员调用您将有 1 个分配,而不是几个(至少 1 个,如果v1
很小并且v2
很大,最多很多)。
然后我猜 range 版本的insert()
性能可能比循环更好,但这可能不是真的,应该进行测试(至少它不应该比循环差)。
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;
}
也许它也值得考虑是否v1
必须保持不变。如果没有,那么可能值得重载operator +=
并使用它。
当然,请确保尝试不同的优化级别,有时-Os
或-O2
可能比-O3
推荐阅读
- asp.net - httpContext.Current.Request.RawUrl 用于返回带扩展名的路径
- eclipse - Oomph 安装程序错误:无法加载目录
- swift - Firestore 数据库使用大量内存
- xcode - 目前不支持将快捷方式作为文件导入
- vue.js - Vue3 在哪里存储敏感的环境变量?
- c - 停止此 SUID 程序读取任何其他文件
- scala - 如何在 Spark 中获取数组列的所有组合?
- sorting - SAS 9.3 和 SAS 9.4 左连接排序顺序
- kubernetes - 无法通过 istio 使用浏览器从外部获取产品页面
- android - 在后台捕获屏幕截图