为什么两个核心上的两个实例的运行时间仅大于一个实例?

时间:2013-09-13 00:51:04

标签: c++ c

我正面临一个“可能”奇怪的问题。假设我有一个可执行文件。当我在具有两个核心的计算机上运行它时,该过程运行时间t1。然后,如果我运行该进程的两个实例(相同的可执行文件但在不同的目录上,手动或使用gnu并行启动),每个进程的运行时间不接近t1但实际上更大,有时接近1.9t1。我必须注意到这两个核心是物理的(macbook pro 2009中,Mountain Lion)。我还在具有8个内核的linux机器上测试了这种行为。如果我运行1,2,3和4个实例,则每个实例的运行时间约为t1。但是,在5,6,7和8个实例之后,每个实例的运行时间越来越大于t1。

我在运行模拟时检测到了这种行为。我能够将测试用例简化为下面的简单测试。我想在几个编译级别检查std::vectorstd::array,静态和动态数组。测试代码如下:

#include <iostream>
#include <vector>
#include <array>
#include <cstdlib>

struct Particle {
private:
  int nc;
public:
  void reset(void) { nc = 0; };
  void set(const int & val) { nc = val; };
};

#define N 10000 // number of particles
#define M 200000 // number of steps

#define STDVECTOR 0
#define STDARRAY  0
#define ARRAY     1
#define DYNARRAY  0

int main (void)
{
#if STDVECTOR
  std::vector<Particle> particles(N);
#elif STDARRAY
  std::array<Particle, N> particles;
#elif ARRAY
  Particle particles[N];
#elif DYNARRAY
  Particle *particles; particles = new Particle [N];
#endif

  int jj = 0;
  for (int ii = 0; ii < M; ++ii) {
    //for (auto & body : particles) body.reset();
    for (int idx = 0; idx < N; ++idx) particles[idx].reset();
    jj = ii;
  }
  particles[0].set(jj*drand48());

  return 0;
}

编译测试以

完成

for a in 0 1 2 3; do printf "\n\nOPT=$a\n\n"; g++-4.8 -O${a} -o tmp.x tmp.cpp; cp tmp.x simul01/ ; cp tmp.x simul02/; time simul01/tmp.x ; parallel 'time {}/tmp.x' ::: simul01 simul02 ; done

对于这两台核心机器,我获得了以下数据: Running times or one and two processes

其中时间以秒为单位,例如,vector-1或vector-2分别表示使用std::vector并运行一个或两个进程时的运行时间。对于这两个过程,我花了两个时间之间的最大时间。

我的期望:我希望这两个进程的运行时间与单个进程时间相似。但是,即使核心数量足够,运行多个实例的时间也会有系统性的增加。正如我所说,当进程数大于4时,这也适用于八核机器。

我如何衡量时间:我使用time命令,并选择用户时间。系统时间非常小,不足以解释一个或两个进程运行时的差异。

我已经使用gcc 4.6,4.7,4.8和4.9进行了检查。

因此,我的问题是:为什么会这样?也许与操作系统的某些内在性以及从核心到核心的进程迁移有关。我真的不知道。如果有人能够对此有所了解,我非常感激,因为这会影响我模拟的运行时间。我需要同时运行多个进程,但运行时间正在增加。相反,对于一个和两个进程,具有不同方法的另一个模拟代码几乎同时运行。所以我想丢弃或确保这是我自己的程序的问题。我不知道如何以可移植的方式设置处理器关联(在mac和linux之间)。

提前致谢

3 个答案:

答案 0 :(得分:0)

您的编译器 CPU可能会自动对代码进行线程处理。因此,您将获得1个实例的最大性能......因此运行2个实例将花费大约两倍的时间。

答案 1 :(得分:0)

请记住,还有其他任务与应用程序一起运行(内核线程,后台进程等等)。在2个内核上引入另一个CPU密集型任务意味着可能存在跨内核的线程迁移,实际上它们正在争夺资源。具有更多内核的Linux方案也是如此。一旦达到某个限制,那么您将与系统中的其他进程竞争,无论是您生成的进程还是后台进程和内核线程等....

答案 2 :(得分:0)

可能是很多事情:

  1. 核心争用 - 特别是英特尔的超线程往往会减慢不包含不可预测跳转的CPU密集型代码。但也可能是,如果你只有两个核心,那么系统中的其他进程会有更多的争用(网络数据包,电子邮件软件醒来检查新邮件等)。
  2. 缓存争用:如果代码和数据并非全部适合L1缓存,它可能最终会出现在共享缓存中,并且这两个实例会争论谁来存储什么和哪里。此外,如果两个线程在同一个核心上进行调度,因为某些其他进程必须运行(例如,您的电子邮件客户端,Web浏览器等),那么L1缓存内容将从进程A中抛出以使进程B受益。它不会花费那么多微秒来完全填充替换L1缓存中的内容,所以即使很短的时间也会影响到这一点。第二个过程也可能会转移到另一个过程。
  3. 内存带宽限制 - 两个实例使用两倍的内存带宽,导致内存负载增加。
  4. 上面的列表远非完整的可能性列表,只是一些更常见的。

    您可以通过使用分析工具(例如oprofileperf)来判断这些选项中的哪一个(或其他选项),并检查机器上的各种性能计数器,比较1流程与2流程方案。