5

哪种变体最有效,为什么?还是他们会针对相同的代码进行优化?

char inplace(int i) {
    // [some check if 0<=i<=2 here]
    return "azS"[i];
}

char infunc(int i) {
    const char s[] = "azS";
    // [some check if 0<=i<sizeof(s)/sizeof(s[0])-1 here]
    return s[i];
}

const char s[] = "azS";
char inglobals(int i) {
    // [some check if 0<=i<sizeof(s)/sizeof(s[0])-1 here]
    return s[i];
}

请不要抱怨过早的优化是多么邪恶,这只是天生的 C++'er 固有的不可抗拒的好奇心;)

4

2 回答 2

2

我刚刚编译并反汇编了你的代码。inplace并且inglobals是相同的。这很直观:编译器可以将 const 字符串存储在.rodata节中。

奇怪的是,gcc 产生了相当多的代码infunc(见下文),可能是因为你“坚持”在s堆栈上。定义sasstatic会产生与和infunc相同的代码。inplaceinglobals

0000000000000010 :
  10: 48 83 ec 18 sub $0x18,%rsp
  14: 48 63 ff movslq %edi,%rdi
  17: 64 48 8b 04 25 28 00 移动 %fs:0x28,%rax
  1e: 00 00
  20: 48 89 44 24 08 移动 %rax,0x8(%rsp)
  25: 31 c0 xor %eax,%eax
  27: c7 04 24 61 7a 53 00 移动 $0x537a61,(%rsp)
  2e: 0f b6 04 3c movzbl (%rsp,%rdi,1),%eax
  32: 48 8b 54 24 08 移动 0x8(%rsp),%rdx
  37: 64 48 33 14 25 28 00 异或 %fs:0x28,%rdx
  3e:00 00
  40:75 05 jne 47
  42: 48 83 c4 18 添加 $0x18,%rsp
  46:c3 回复   
  47:e8 00 00 00 00 呼叫 4c

编辑

该位置%fs:0x28与 GCC 的堆栈保护器有关。禁用它会给出以下代码:

0000000000000010 :
  10: 48 63 ff movslq %edi,%rdi
  13: c7 44 24 f0 61 7a 53 移动 $0x537a61,-0x10(%rsp)
  1a: 00
  1b: 0f b6 44 3c f0 movzbl -0x10(%rsp,%rdi,1),%eax
  20:c3 重新调用   

因此,在这种情况下,GCC 选择将您的字符串与代码内联存储,并在执行期间将其复制到堆栈中。我认为这是非常有效的,因为您的处理器的缓存已经充满了字符串,因此不会发生内存访问。

编辑

综上所述,三个版本都是等价的。然而,根据您的编译器实现,一个可能会比另一个更有效。对于 GCC,infunc对于短字符串似乎更有效,因为字符串是与指令一起获取的。对于较大的字符串,我会使用inplaceor inglobals

于 2012-08-31T09:58:41.740 回答
1

我认为编译器会为 1 和 3 产生相同的结果,并且可能为 2 产生相同的结果(稍微取决于它是否意识到它可以不实际将数据复制到堆栈上而逃脱)。我还假设这不在某个标题或类中,这会产生很大的不同(我知道您的代码看起来不像,但我只是保持谨慎)。

一般来说,除非优化证明不是这样,否则我会尽量减少声明的范围(排除 3)。在 1 和 2 之间,如果您更改字符串的内容,出错的可能性对于 1 来说要高得多。

这让我认为它可能对生成的代码没有任何影响,但选项 2 比其他 2 好得多。

于 2012-08-31T09:56:23.260 回答