C++ 是g+中的OpenMP(并行)+;4.7效率不高?5倍CPU时的2.5倍

C++ 是g+中的OpenMP(并行)+;4.7效率不高?5倍CPU时的2.5倍,c++,openmp,g++-4.7,C++,Openmp,G++ 4.7,我曾尝试将OpenMP与一个#pragma omp parallel for,它使我的程序从运行时间35s(99.6%CPU)变为运行时间14s(500%CPU)。这就是使用g++-O3和g++-O3-fopenmp编译的区别,这两种方法都是在Debian 7(喘息)上使用gcc(Debian 4.7.2-5)4.7.2 为什么它最多只使用500%的CPU,而理论上的最大值是800%,因为CPU是4核/8线程?它不应该至少达到700秒吗 为什么我的总时间只提高了2.5倍,而CPU使用率却提高了

我曾尝试将OpenMP与一个
#pragma omp parallel for
,它使我的程序从运行时间35s(99.6%CPU)变为运行时间14s(500%CPU)。这就是使用
g++-O3
g++-O3-fopenmp
编译的区别,这两种方法都是在Debian 7(喘息)上使用
gcc(Debian 4.7.2-5)4.7.2

  • 为什么它最多只使用500%的CPU,而理论上的最大值是800%,因为CPU是4核/8线程?它不应该至少达到700秒吗

  • 为什么我的总时间只提高了2.5倍,而CPU使用率却提高了5倍?缓存抖动

整个程序是基于C++代码>字符串< /COD>操作,递归处理(使用大量<代码> .SUBR(1)< /CUT>和一些连接),其中字符串被连续插入<<代码>向量<代码> >代码> SET/COD> < < /P> 换句话说,基本上,在一个并行for循环中,大约有2k次循环迭代,在

向量
上运行,每一次循环都可以使用一些
字符串
.substr(1)和
+char
串联对自身进行两次递归调用,然后递归以单个字符串或两个字符串的串联的
.insert
终止,并且所述
.insert
还处理大量可能的重复项


一切都在规范范围内正常运行,但我正在尝试看看它是否可以运行得更快。:-)

实现线性加速通常不是一件小事,而且内核越多就越难实现。您可能需要寻找以下几个方面:

  • 错误共享:如果阵列切片不正确,则缓存线可能在两个核心之间发生争用,通常缓存线的两半由两个线程写入,导致缓存线从一个核心的二级缓存移动到另一个核心。如果使用大量
    shared
    变量,也可能发生缓存共享。在这种情况下,考虑使用<代码>私有< /代码>或<代码>第一私有> /代码>以避免同步。
  • OpenMP调度:如果未指定,OpenMP将在系统中的线程之间以相等的方式分割循环的迭代,并为每个线程分配子范围。但是,如果每个索引的工作量不同,那么您可能会遇到这样的情况:大多数线程都完成了它们的工作,并且它们被阻塞在并行区域末尾的屏障上,等待较慢的线程(需要做更多工作的线程)。这取决于在循环中实现的算法类型,但OpenMP允许您使用
    schedule()
    指令更改调度策略。考虑尝试<代码>动态< /代码>。

  • 实现线性加速通常不是一件小事,而且内核越多,它就越难实现。您可能需要寻找以下几个方面:

  • 错误共享:如果阵列切片不正确,则缓存线可能在两个核心之间发生争用,通常缓存线的两半由两个线程写入,导致缓存线从一个核心的二级缓存移动到另一个核心。如果使用大量
    shared
    变量,也可能发生缓存共享。在这种情况下,考虑使用<代码>私有< /代码>或<代码>第一私有> /代码>以避免同步。
  • OpenMP调度:如果未指定,OpenMP将在系统中的线程之间以相等的方式分割循环的迭代,并为每个线程分配子范围。但是,如果每个索引的工作量不同,那么您可能会遇到这样的情况:大多数线程都完成了它们的工作,并且它们被阻塞在并行区域末尾的屏障上,等待较慢的线程(需要做更多工作的线程)。这取决于在循环中实现的算法类型,但OpenMP允许您使用
    schedule()
    指令更改调度策略。考虑尝试<代码>动态< /代码>。

  • 根据您的描述,您可以得出以下结论:

    我假设OpenMP确实使用8个线程(通过导出OMP\u NUM\u threads=8验证)

  • 500%的CPU意味着在屏障上花费了大量时间。这是由于糟糕的负载平衡:不同的迭代占用不同的时间。因此,默认(静态)循环调度效率低下,尝试不同的方法,例如
    动态

  • 如果运行时间没有与线程数成比例减少(例如,所花费的CPU总时间增加),则可能存在作为瓶颈的共享资源,或者线程之间存在影响。注意,这也可能是短屏障(来自负载不平衡)的结果,短屏障执行忙等待而不是阻塞

    • 最常见的共享资源是内存带宽。这是否会影响您,取决于您的工作集是否适合本地缓存。考虑到现代系统中的许多内存层次结构和NUMA属性,这可能变得非常复杂。除了重新构造数据访问以更有效地使用缓存(阻塞)之外,您没有什么可以做的

    • 错误共享:如果多个线程对相同的内存位置(缓存线)进行写入和读取,则相应的内存访问速度会慢得多。尝试将并行循环中的写入限制为私有变量

    • 超线程-核心是两个硬件线程之间的共享资源

      使用5倍的资源

      这是一个根本性的误解。额外的硬件线程为每个核心提供的额外资源很少。如果两个三分之一