使用算术比存储变量更快吗?

时间:2013-11-06 02:04:15

标签: c optimization bit-manipulation

在C中(或者可能是一般情况下)使用算术获取值或从数组/变量调用它会更快吗?

例如,如果我有

int myarray[7] = {16};
int mysixteen = 16;

然后我可以通过多种不同方式获得16

myarray[#]
mysixteen
16
1 << 4
10 + 6

逻辑上16是最快的,但对于一组数字而言,这并不总是方便或合理的。可能与此相关的一个示例是预计算表。假设您需要64位的位掩码,您可以填充数组

for (int i = 0; i < 64; ++i) {
    mask[i] = 1 << i;
}

并在将来调用数组,或者创建一个宏

#define mask(b) (1 << b)

并打电话给。

2 个答案:

答案 0 :(得分:2)

一般来说,

中的任何一个
  • 16
  • 1&lt;&lt; 4
  • 10 + 6

将导致文字16,因为编译器肯定会实现名为constant folding的优化。

的表现
  • mysixteen
  • myArray的[N]

可能更低,具体取决于存储这些变量的值的位置。在记忆中?如果是这样,任何CPU缓存中的内存是什么?或者它存储在一个CPU寄存器中?没有确定的答案。

我是一般的,对于特定的程序,你总能看到你的编译器给你的东西 - 但请注意,这可能会发生很大变化,具体取决于周围的代码和优化标记。

要自己尝试一下,请考虑这个小程序:

int f() { return 16; }

int g() { return 1 << 4; }

int h() { return 10 + 6; }

int i() {
    int myarray[7] = { 16 };
    return myarray[3];
}

int j() {
    int mysixteen = 16;
    return mysixteen;
}

如果我使用gcc 4.7.2编译它,然后检查反汇编,如

$ gcc -c so19802742.c -o so19802742.o
$ objdump --disassemble so19802742.o

我明白了:

0000000000000000 <f>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   b8 10 00 00 00          mov    $0x10,%eax
   9:   5d                      pop    %rbp
   a:   c3                      retq   

000000000000000b <g>:
   b:   55                      push   %rbp
   c:   48 89 e5                mov    %rsp,%rbp
   f:   b8 10 00 00 00          mov    $0x10,%eax
  14:   5d                      pop    %rbp
  15:   c3                      retq   

0000000000000016 <h>:
  16:   55                      push   %rbp
  17:   48 89 e5                mov    %rsp,%rbp
  1a:   b8 10 00 00 00          mov    $0x10,%eax
  1f:   5d                      pop    %rbp
  20:   c3                      retq   

0000000000000021 <i>:
  21:   55                      push   %rbp
  22:   48 89 e5                mov    %rsp,%rbp
  25:   48 c7 45 e0 00 00 00    movq   $0x0,-0x20(%rbp)
  2c:   00 
  2d:   48 c7 45 e8 00 00 00    movq   $0x0,-0x18(%rbp)
  34:   00 
  35:   48 c7 45 f0 00 00 00    movq   $0x0,-0x10(%rbp)
  3c:   00 
  3d:   c7 45 f8 00 00 00 00    movl   $0x0,-0x8(%rbp)
  44:   c7 45 e0 10 00 00 00    movl   $0x10,-0x20(%rbp)
  4b:   8b 45 ec                mov    -0x14(%rbp),%eax
  4e:   5d                      pop    %rbp
  4f:   c3                      retq   

0000000000000050 <j>:
  50:   55                      push   %rbp
  51:   48 89 e5                mov    %rsp,%rbp
  54:   c7 45 fc 10 00 00 00    movl   $0x10,-0x4(%rbp)
  5b:   8b 45 fc                mov    -0x4(%rbp),%eax
  5e:   5d                      pop    %rbp
  5f:   c3                      retq   

请注意,由于常量折叠,fgh会产生完全相同的机器代码。 i中的数组访问导致机器代码最多(但不一定是最慢的!),j介于两者之间。

然而,这根本没有任何更复杂的代码优化!用例如编译时生成的代码-O2可能完全不同,因为编译器注意到对五个函数中的任何一个的调用等同于仅使用常量16

答案 1 :(得分:0)

你不应该担心这些事情。在大多数情况下,编译器足够智能。即使像multiply这样的基本操作有时也会优化使用移位,因为这种方式更有效。

说到你的例子,阵列版本需要大量的内存访问,这非常慢。在大多数情况下,宏将更快,具体取决于访问次数。