配置文件C执行

时间:2012-07-19 16:53:44

标签: c profiler clock execution-time

所以,只是为了好玩,出于好奇,我想看看在做奇偶校验,模数或按位比较时执行得更快。

所以,我掀起了以下内容,但我不确定它的行为是否正确,因为差异太小了。我在网上看到,按位应该比模数检查快一个数量级。

它是否有可能被优化掉?我刚刚开始修补程序集,否则我会尝试解析一下可执行文件。

编辑3:这是一个有效的测试,非常感谢@phonetagger:

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

// to reset the global
static const int SEED = 0x2A;

// 5B iterations, each
static const int64_t LOOPS = 5000000000;

int64_t globalVar;

// gotta call something
int64_t doSomething( int64_t input )
{
  return 1 + input;
}

int main(int argc, char *argv[]) 
{
  globalVar = SEED;

  // mod  
  clock_t startMod = clock();

  for( int64_t i=0; i<LOOPS; ++i )
  {    
    if( ( i % globalVar ) == 0 )
    {
      globalVar = doSomething(globalVar);      
    }    
  }

  clock_t endMod = clock();

  double modTime = (double)(endMod - startMod) / CLOCKS_PER_SEC;

  globalVar = SEED;

  // bit
  clock_t startBit = clock();

  for( int64_t j=0; j<LOOPS; ++j )
  {
    if( ( j & globalVar ) == 0 )
    {
      globalVar = doSomething(globalVar);
    }       
  }

  clock_t endBit = clock();

  double bitTime = (double)(endBit - startBit) / CLOCKS_PER_SEC;

  printf("Mod: %lf\n", modTime);
  printf("Bit: %lf\n", bitTime);  
  printf("Dif: %lf\n", ( modTime > bitTime ? modTime-bitTime : bitTime-modTime ));
}

每个循环的50亿次迭代,以及全局删除编译器优化产生以下结果:

Mod: 93.099101
Bit: 16.701401
Dif: 76.397700

3 个答案:

答案 0 :(得分:3)

gcc foo.c -std=c99 -S -O0 (请注意,我特别为-O0生成了> x86为我提供了两个循环的相同的程序集。运算符strength reduction表示两个if都使用andl来完成工作(这比英特尔计算机上的模数更快):

First Loop:

.L6:
        movl    72(%esp), %eax
        andl    $1, %eax
        testl   %eax, %eax
        jne     .L5
        call    doNothing
.L5:
        addl    $1, 72(%esp)
.L4:
        movl    LOOPS, %eax
        cmpl    %eax, 72(%esp)
        jl      .L6

第二圈:

.L9:
        movl    76(%esp), %eax
        andl    $1, %eax
        testl   %eax, %eax
        jne     .L8
        call    doNothing
.L8:
        addl    $1, 76(%esp)
.L7:
        movl    LOOPS, %eax
        cmpl    %eax, 76(%esp)
        jl      .L9

您看到的微不足道的差异可能是因为clock的解析/不准确。

答案 1 :(得分:2)

按位检查只需要一条机器指令(“和......,0x01”);这很难被击败。

如果你有一个实际上通过取余数来计算模数的哑编译器,模数检查绝对会慢一些(有时包括对模程例程的子程序调用!)。智能编译器了解模数函数并直接为其生成代码;如果他们有任何体面的优化,他们知道“modulo(x,2)”可以用上面相同的AND技巧实现。

我们的PARLANSE编译器当然是这样做的。如果广泛使用的C和C ++编译器也不这样做,我会感到惊讶。

使用这样的“好”编译器,你编写奇数/偶数(甚至“是2的幂”)检查的方式无关紧要;它会非常快。

答案 2 :(得分:2)

大多数编译器都会将以下两种方法编译为完全相同的机器指令:

if( ( i % 2 ) == 0 )

if( ( i & 1 ) == 0 )

...即使没有打开任何“优化”。原因是您使用常量值进行MOD-ing和AND,并且正如任何编译器编写者所知,%2操作在功能上等同于&amp; 1操作。实际上,任何2次幂的MOD都具有等效的AND运算。如果你真的想测试差异,你需要让两个操作的右侧都是可变的,并且绝对确保编译器的聪明性不会阻碍你的努力,你需要埋葬变量'初始化某个地方,编译器在那时无法告诉它的运行时值是什么;即,您需要将值作为参数传递给GLOBALLY-DECLARED(即非'静态')测试函数,在这种情况下,编译器无法追溯到它们的定义&amp;用常量替换变量,因为理论上任何外部调用者都可以为这些参数传递任何值。或者,您可以将代码保留在main()中并全局定义变量,在这种情况下,编译器不能用常量替换它们,因为它无法确定另一个函数是否可能改变了全局变量的值。

顺便说一下,对于除法运算存在同样的问题。通过等效的右移(&gt;&gt;)运算可以用恒定的2次幂进行除法。相同的技巧适用于乘法(&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;真正的除法操作在硬件上花费很长时间,尽管在大多数现代处理器中已经取得了显着的改进,甚至在15年前,除法操作仍然需要80个时钟周期,而a>&gt;操作只需一个周期。你不会在现代处理器上使用按位技巧看到“数量级”的改进,但是大多数编译器仍会使用这些技巧,因为仍有一些明显的改进。

编辑:在某些嵌入式处理器上(虽然它是令人难以置信的,在v8之前的原始Sparc桌面/工作站处理器版本),但根本没有分割指令。所有真正的分歧和这些处理器上的mod操作必须完全用软件执行,这可能是一项非常昂贵的操作。在那种环境中,你肯定会看到一个数量级的差异。