测量某些指令执行所需时间的最有效和最正确的方法是什么?

时间:2017-09-27 11:16:28

标签: c performance assembly time benchmarking

我想测量执行一段代码所需的时间。什么是最有效和正确的方法。

我写了一个类似下面的代码,结果每次都不一样。有一定程度的随机化发生,我不知道为什么以及如何消除这种影响。

#include<stdio.h>
#include<time.h>

int main() 
{
   int x=10;
   int y=25;
   int z=x+y;
   printf("Sum of x+y = %i", z);
   time_t start = clock();
   for(int i=0;i<100000;i++)z=x+y;
   time_t stop = clock();

   printf("\n\nArithmetic instructions take: %d",stop-start);
   start = clock();
   for(int i=0;i<100000;i++)z=x&y;
   stop = clock();

   printf("\n\nLogic instructions take: %d",stop-start);
}

结果如下:

Arithmetic instructions take: 327
Logic instructions take: 360

Arithmetic instructions take: 271
Logic instructions take: 271

Arithmetic instructions take: 287
Logic instructions take: 294

Arithmetic instructions take: 279
Logic instructions take: 266

Arithmetic instructions take: 265
Logic instructions take: 296

还有哪些方法可以衡量执行循环所需的时间。

注意:编译器优化不会删除循环,我检查了它。

那么,对一段代码进行基准测试的正确方法是什么?

2 个答案:

答案 0 :(得分:1)

您获得的数字表明您在启用优化标志的情况下编译,这使您的基准测试无效。

例如我编译:

 gcc prog.c -Wall -Wextra -O2 -march=native

得到for(long long int i=0;i<10000000000000;i++)时间0和1。

哪一个获得1?

第一个for循环,无论哪个是(我的意思是&+运算符)。这背后的原因是第一个循环可能在冷启动阶段找到程序,例如缓存。

  

那么,对一般代码进行基准测试的正确方法是什么?

  • 使用优化标记进行编译(例如GCC中的-O3)。
  • 运行多个独立实验。创建两个程序,其中 一个将使用第一个算法,而另一个将使用第二个竞争性算法。
  • 增加迭代的大小,以便您确定操作系统 间接费用可以忽略不计。

对于您的特定情况,godbolt完全优化了循环。您没有看到它,因为您没有启用优化标志进行编译。此外,像你这样的测试完全无法测量对C +运算符有用的任何东西,因为它会在同一台机器上的不同上下文中编译成不同的指令。

答案 1 :(得分:0)

时间变化的原因是因为运行程序并不是计算机唯一能做的事情。有几件事可能会导致程序稍长或稍短:

  1. 需要为CPU提供其他程序。即使您没有运行其他程序,操作系统也会如此。每当它决定切换上下文时,它需要做大量的工作,比如将寄存器备份到内存并恢复其他程序的状态。这可能是您计划的主要因素。
  2. 缓存未命中:这里不太可能是一个主要问题,因为大多数内存访问都将在两个页面中的一个中:堆栈的顶部和加载器放置常量的区域。但是那些可能不会在他们重新加载的第一次时间内缓存,这会产生影响。在一个更复杂的计划中,这可能是一个更重要的因素。
  3. 阻止系统调用。依赖于物理资源(网络,文件访问等)的任何函数调用可能需要延迟,直到该资源可用。根据其他程序使用该资源的情况,所需的时间可能会有所不同。
  4. 解决方案并不是试图控制所有这些;这不实用。只需运行几百万次基准测试即可平均所有噪音。 (我的经验法则是不断向计数添加零,直到完成至少需要20-30秒,然后运行两次并检查结果大致相同)。这样可以让您清楚地知道通常采取多长时间,如果您还计算方差或标准偏差,您就会知道这些差异与标准差有多大关系。案例分析。