【问题标题】:OpenMP: Huge performance differences between Visual C++ 2008 and 2010OpenMP:Visual C++ 2008 和 2010 之间的巨大性能差异
【发布时间】:2011-01-19 16:44:29
【问题描述】:

我正在运行一个相机采集程序,该程序对采集的图像执行处理,并且我正在使用简单的 OpenMP 指令进行此处理。所以基本上我等待来自相机的图像,然后处理它。

在迁移到 VC2010 时,我看到非常奇怪的性能消耗:在 VC2010 下,我的应用程序占用了近 100% 的 CPU,而在 VC2008 下它只占用了 10%。

如果我只对处理代码进行基准测试,我发现 VC2010 和 VC2008 之间没有差异,那么在使用采集功能时就会出现差异。

我已将重现问题所需的代码简化为执行以下操作的简单循环:

  for (int i=0; i<1000; ++i)
  {
    GetImage(buffer);//wait for image
    Copy2Array(buffer, my_array);

    long long sum = 0;//do some simple OpenMP parallel loop
    #pragma omp parallel for reduction(+:sum)
    for (int j=0; j<size; ++j)
      sum += my_array[j];
  }

这个循环在 2008 年消耗了 5% 的 CPU,在 2010 年消耗了 70%。

我做了一些分析,这表明在 2010 年大部分时间都花在了 OpenMP 的vcomp100.dll!_vcomp::PartialBarrierN::Block

我还做了一些并发分析:

在 2008 年,处理工作分布在 3 个工作线程上,这些工作线程非常活跃,因为处理时间远不如图像等待时间

同样的线程出现在2010年,但都100%被PartialBarrierN::Block函数占用。由于我有四个核心,它们占用了 75% 的工作,这大致是我在 CPU 占用中看到的。

因此,OpenMP 和 Matrox 采集库(专有)之间似乎存在冲突。但这是 VS2010 还是 Matrox 的错误? 有什么我可以做的吗? VC++2010 对我来说是必须的,所以我不能只坚持 2008 年。

非常感谢

状态更新

使用 DeadMG 建议的新并发框架会导致 40% 的 CPU。对其进行分析表明时间花费在处理上,因此它没有显示我在 OpenMP 中看到的错误,但在我的情况下,性能比 OpenMP 差得多。

状态更新 2

我已经安装了最新英特尔 C++ 的评估版。它显示了完全相同的性能问题!

我交叉发布到MSDN forum

状态更新 3

在 Windows 7 64 位和 XP 32 位上测试,结果完全相同(在同一台机器上)

【问题讨论】:

  • 消耗了100%的CPU,但是需要多长时间呢?它跑得更快吗?
  • 不,它不会跑得更快。在这两种情况下,我都可以在新图像到达之前处理图像,因此如果处理速度更快,我将不会在我的程序中看到它。我的问题更多是 CPU 占用而不是处理时间。
  • 使用 OpenMP 而看到它使用所有处理器资源才是真正的问题。获得 100% 的负载是预期的结果。更多信息:blogs.msdn.com/b/oldnewthing/archive/2010/12/03/10097861.aspx
  • 对不起,汉斯,我不明白?我有 100% 的负载,而在以前的情况下,负载不到 5%。因此它是过度的和不正常的。在我的实际程序中,我需要 CPU 进行其他处理,因此会减慢应用程序的速度。
  • @Hans Passant:该逻辑仅适用于固定负载程序。这是一个固定速率的程序:您每 40 毫秒(通常)获得一张相机图像。没有办法提前完成。每 40 毫秒 CPU 使用率就会达到峰值。 MP达到100%,是的。但峰值应该更短,平均超过 40 毫秒,结果应该是 5% 左右。

标签: c++ performance visual-studio-2010 openmp


【解决方案1】:

在 2010 OpenMP 中,每个工作线程在任务完成后会进行大约 200 毫秒的自旋等待。在我的 I/O 等待和重复 OpenMP 任务的情况下,它会大量加载 CPU。

解决方案是改变这种行为;英特尔 C++ 对此有一个 extension routinekmp_set_blocktime()。但是Visual 2010没有这种可能性。

this Autodesk note 中,他们谈到了英特尔 C++ 的问题。该编译器首先引入了该行为,但允许对其进行更改(见上文)。 Visual 2010 切换到它,但是......没有像 Intel 这样的解决方法。

所以总结一下,切换到英特尔 C++ 并使用 kmp_set_blocktime(0) 解决了它。

感谢DataLever Corporationother MSDN thread 上的 John Lilley

问题已提交至MS Connect,并收到“不会修复”的反馈。

【讨论】:

  • 将旋转块时间设置为一些较小的值,例如20,不是直零。在某些情况下,如果值正好为 0,英特尔 OpenMP 会冻结。多年来,我在很多场合都遇到过这个问题。
【解决方案2】:

使用 OpenMP 3.0,可以通过 OMP_WAIT_POLICY 停用 spinwait:

_putenv_s( "OMP_WAIT_POLICY", "PASSIVE" );

效果与kmp_set_blocktime(0)基本相同,但由于我们在运行时设置了环境变量OMP_WAIT_POLICY,它只会影响当前进程和子进程。

当然 OMP_WAIT_POLICY 也可以由启动器应用程序设置,例如Blender 就是这样处理的。

VC2010 的修补程序可用here,VC2013 等更高版本直接支持它。

【讨论】:

    【解决方案3】:

    您可以尝试 VS2010 附带的新并发运行时 - 从您的测试样本开始。

    也就是说,

    for (int i=0; i<1000; ++i)
      {
        GetImage(buffer);//wait for image
        Copy2Array(buffer, my_array);
    
        long long sum = 0;//do some simple OpenMP parallel loop
        #pragma omp parallel for reduction(+:sum)
        for (int j=0; j<size; ++j)
          sum += my_array[j];
      }
    

    会变成

    for (int i=0; i<1000; ++i)
      {
        GetImage(buffer);//wait for image
        Copy2Array(buffer, my_array);
    
        Concurrency::combinable<int> combint;
        Concurrency::parallel_for(0, size / 1000, [&](int j) {
          for(int i = 0; i < 1000; i++)
              combint.local() += my_array[(j * 1000) + i];
        });
        combint.combine([](int a, int b) { return a + b; });
      }
    

    【讨论】:

    • 我必须找出如何翻译 OpenMP 指令以减少 (+sum),然后返回编辑谢谢你为我做的!
    • 当然,即使这解决了他的问题,也不能解决微软在 OpenMP 中明显的性能错误。
    • @DeadMG 查看状态更新,没有错误,但在我的情况下,并发运行时与 OpenMP 相比性能非常差
    • 数组的大小通常是多少?
    • @CharlesB:你可以试试半手动展开的版本,就像我刚刚编辑的那样。
    【解决方案4】:

    我测试了另一个采集板,问题是一样的,所以罪魁祸首是VC++2010。正如thread on MSDN forums 所示,Microsoft 对 OpenMP 实现进行了更改,从而搞砸了像我这样的程序。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-18
      • 2018-08-29
      • 2012-03-10
      相关资源
      最近更新 更多