在g ++ 4.7中OpenMP(并行)不是很有效吗? 2.5倍,5倍CPU

时间:2016-04-30 03:56:43

标签: c++ openmp g++-4.7

我尝试使用单个#pragma omp parallel for使用OpenMP,这导致我的程序从 35s(99.6%CPU)的运行时转到 14s( 500%CPU),在Intel(R) Xeon(R) CPU E3-1240 v3 @ 3.40GHz上运行。这与使用g++ -O3g++ -O3 -fopenmp进行编译的区别在于Debian 7上的gcc (Debian 4.7.2-5) 4.7.2(wheezy)。

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

  • 为什么我的总体时间只提高了2.5倍,但CPU使用成本是5倍?缓存捶打?

整个程序基于C ++ string操作,具有递归处理(使用大量.substr(1)和一些连接),其中所述字符串被连续插入到vector { {1}}。

换句话说,基本上,在单个并行for循环中完成大约2k循环迭代,在set上运行,并且每个循环迭代可以对其自身执行两次递归调用w / some {{1} } vectorstring连接,然后递归以单个字符串的.substr(1) + char或两个字符串的串联终止,并且表示set .insert还会处理大量可能的重复项。

所有内容都在规范中正常运行,但我试图查看它是否可以更快地运行。 : - )

2 个答案:

答案 0 :(得分:3)

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

我假设OpenMP确实使用8个线程(由export OMP_NUM_THREADS=8验证)

  1. 500%的CPU意味着在障碍中花费了大量时间。这是由于负载平衡不良:不同的迭代需要不同的时间。因此,默认(静态)循环调度是低效的,尝试不同的kinds of loop scheduling,例如, dynamic

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

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

    • 虚假共享:如果多个线程写入&读取到相同的存储器位置(高速缓存行),相应的存储器访问变得慢得多。尝试将并行循环中的写入限制为私有变量。

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

        

      使用5x资源

      这是一个根本的误解。额外的硬件线程为每个核心提供的额外资源很少。如果两个线程在同一个内核上运行,它们将共享计算资源和内存带宽,基本上唯一的优势就是隐藏的I / O延迟。尽管有2倍的CPU时间,但你永远不会看到2倍的加速。有时它会给你10%的加速,有时它会慢一些。

      现在考虑在4个核心上运行5个活动线程。共享核心的2个线程将以~50%的速度运行,从而减慢所有速度。尝试将线程数减少到核心数(export OMP_NUM_THREADS=4)。

答案 1 :(得分:1)

实现线性加速通常并非易事,而且拥有的核心越多越难。您可能需要查找以下几个方面:

  1. 虚假共享:如果数组未正确切片,则两个内核之间可能存在争用缓存行,通常缓存行的两半由两个线程写入,导致缓存行从一个缓存中移出核心到另一个。如果使用大量shared变量,也可能发生缓存共享。在这种情况下,请考虑使用privatefirstprivate来避免同步。
  2. OpenMP调度:如果未指定,OpenMP将在系统中的线程之间以相同的方式拆分循环的迭代,并为每个线程分配一个子范围。但是,如果每个索引的工作量发生变化,那么在大多数线程完成其工作并且在并行区域末尾的屏障处阻塞它们等待较慢的线程时(一个有更多工作要做的事情)。这取决于您在循环中实现的算法类型,但OpenMP使您可以使用schedule()指令更改调度策略。请考虑至少尝试dynamic