【问题标题】:Why std::chrono does not work when measuring the execution time of OpenMP parallelized for loops and compiler optimizations?为什么在测量 OpenMP 并行化 for 循环和编译器优化的执行时间时 std::chrono 不起作用?
【发布时间】:2019-12-11 09:36:33
【问题描述】:

我有一个简单的代码:

#include <iostream>
#include <chrono>

int main(int argc, char ** argv)
{
  int I=0;
  double time=0.0;
  for(int i=0; i<10; ++i)
  {
    auto begin1=std::chrono::steady_clock::now();
  #pragma omp parallel for simd
    for(int j=0; j<1000000; ++j) I=j;
    auto end1=std::chrono::steady_clock::now();
    auto timei=std::chrono::duration_cast<std::chrono::milliseconds>(end1-begin1).count();
    std::cout<<"time 1:"<<time<<std::endl;
    time+=timei;
    std::cout<<"time 2:"<<time<<std::endl;
  }
  return 0;
}

使用 g++ 5.3.1 并编译行:

cmake . -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_CXX_FLAGS="-O2 -fopenmp"

但是输出是:

time 1:0
time 2:11
time 1:11
time 2:16
time 1:16
time 2:16
time 1:16
time 2:16
time 1:16
time 2:16
time 1:16
time 2:16
time 1:16
time 2:16
time 1:16
time 2:16
time 1:16
time 2:16
time 1:16
time 2:16

你看,我无法使用 std::chrono 正确测量执行时间!

为什么?到底是怎么回事?如何衡量执行时间?

这用于“-O2”和“-O1”编译器优化标志。使用“-O0”一切正常。为什么?

同样的情况是当我使用 Intel 编译器 icpc 19.0.1.144 和编译行时:

cmake .-DCMAKE_C_COMPILER=icc -DCMAKE_CXX_COMPILER=icpc -DCMAKE_CXX_FLAGS="-march=native -mtune=native -O2 -ipo16 -mcmodel=large"

如果使用“-O2”和“-O1”编译器标志,时间测量不正确,但如果用“-O0”替换它们,std::chrono可以正常工作。

说实话,我很震惊。

但问题是一样的:为什么使用 std::chrono 的执行时间测量在“-O1”和“-O2”中不能正常工作而在“O0”中工作?以及如何正确测量这段代码的执行时间?

请让我更新一下代码示例:

#include <iostream>
#include <chrono>
#include <ctime>
#include <omp.h>

int array[10000000]{0};

int main(int argc, char ** argv)
{
  clock_t t;
  double time=0.0;
  for(int i=0; i<10; ++i)
  {
    auto begin1=std::chrono::steady_clock::now();
    t=clock();
  #pragma omp parallel for simd
    for(int j=0; j<1000000; ++j) array[j]=j;
    auto end1=std::chrono::steady_clock::now();
    auto timei=std::chrono::duration_cast<std::chrono::milliseconds>(end1-begin1).count();
    std::cout<<"time 1:"<<time<<std::endl;
    time+=timei;
    std::cout<<"time 2:"<<time<<std::endl;
    t=clock()-t;
    printf(\nt%i=%f\n", i, (double)t/CLOCKS_PER_SEC);
  }
  return 0;
}

现在 std:chrono 计时器正确更新。但有时 std::clock 和 std::chrono 的结果差异很大。假设,std::chrono 更准确,应该使用它的计时。

因此,正如@Hamza 在下面回答的那样,编译器只是丢弃了无用的代码块。但是 Intel 和 g++ 编译器都没有警告我任何事情。

因此,对于未来,不要编写什么都不做的 for 循环。编译器可能会简单地丢弃一段没有效果的代码。

在我的完整代码中,我尝试比较 2 个函数的相对性能,返回相同的结果:1 - 返回插入表格的值,2 - 从公式计算它(公式是表格点的近似值)。我的错误是我将结果写在临时堆栈变量的内部循环中,根本没有做任何事情。编译器把它扔掉了。我应该将内部循环中的值写入一个数组,或者以任何其他方式将它们累积起来,简单地说,做一些有用的事情,编译器不会丢弃的。

我是这么理解的。

【问题讨论】:

  • 您尝试过更精细的分辨率吗?例如纳秒。此外,您的工作循环没有做任何有用的事情,编译器可能只是将其扔出窗口,因此您实际上什么也没有测量。所以你没有时间增加。而且我不熟悉openmp。访问I时是否线程安全?
  • 我想,它应该被隐式地设为#pragma omp parallel for simd private(I) 并且工作正常。
  • 我当然可以做出更精细的分辨率,但我不明白:如您所见,for(int j=0; j

标签: c++ g++ openmp compiler-optimization chrono


【解决方案1】:

我的猜测是编译器只是优化了你在循环中所做的事情,因为它认为它是无用的。 使用以下代码,您可以获得一些实际的毫秒数:

#include <iostream>
#include <chrono>
#include <omp.h>

int main(int argc, char ** argv)
{
    int I=0;
    double time=0.0;
    for(int i=0; i<10; ++i)
    {
        auto begin1=std::chrono::steady_clock::now();
        #pragma omp parallel for simd
        for(int j=0; j<100000000; ++j) I+=j;
        auto end1=std::chrono::steady_clock::now();
        auto timei=std::chrono::duration_cast<std::chrono::milliseconds>(end1-   begin1).count();

        std::cout << I << std::endl;
        std::cout<<"time 1:"<<time<<std::endl;    
        time+=timei;
        std::cout<<"time 2:"<<time<<std::endl;
    }
    return 0;
}

我得到以下输出:

887459712
time 1:0
time 2:71
1774919424
time 1:71
time 2:142
-1632588160
time 1:142
time 2:213
-745128448
time 1:213
time 2:283
142331264
time 1:283
time 2:351
1029790976
time 1:351
time 2:419
1917250688
time 1:419
time 2:487
-1490256896
time 1:487
time 2:555
-602797184
time 1:555
time 2:623
284662528
time 1:623
time 2:692

【讨论】:

  • 您编写的结果仅适用于带有 g++ 的“-O0”和“-O1”编译器选项。使用“-O2”也有同样的问题。
  • 我用 -O2 得到了它,即使是用 -O3 也是如此。那很奇怪。就此而言,我使用的是 g++ 7.4.0。
  • 奇怪,我不知道。我正在使用 g++ 5.3.1。
  • 如果您尝试使用另一个计时器会发生什么?也许来自英特尔 TBB 的 tick_count?
  • 非常感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-23
  • 1970-01-01
  • 2021-05-22
  • 2021-10-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多