C++ 在哪里为最有效的代码定义一次常量?
哪种变体最有效,为什么?或者它们会被优化为相同的代码C++ 在哪里为最有效的代码定义一次常量?,c++,performance,C++,Performance,哪种变体最有效,为什么?或者它们会被优化为相同的代码 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]; } co
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];
}
charinplace(inti){
//[如果0进行一些检查,我认为编译器会为1和3生成相同的结果,也可能为2生成相同的结果(这取决于它是否意识到可以不将数据复制到堆栈上)。我还假设这不在某个头或类中,这会产生很大的不同(我知道你的代码看起来不像,但我只是小心而已)
一般来说,除非优化证明不是这样,否则我会尽量减少声明的范围(排除3)。在1到2之间,如果更改字符串的内容,1的出错几率要高得多
这让我想到,它很可能对生成的代码没有影响,但选项2比其他2好得多。我刚刚编译并反汇编了您的代码。inplace
和ingglobals
是相同的。这非常直观:编译器可以将常量字符串存储在.rodata
节
奇怪的是,gcc为infunc
(见下文)生成了相当多的代码,可能是因为您“坚持”在堆栈上有s
。将s
定义为static
会使infunc
生成与inplace
和inglobals
相同的代码
0000000000000010 :
10: 48 83 ec 18 sub $0x18,%rsp
14: 48 63 ff movslq %edi,%rdi
17: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
1e: 00 00
20: 48 89 44 24 08 mov %rax,0x8(%rsp)
25: 31 c0 xor %eax,%eax
27: c7 04 24 61 7a 53 00 movl $0x537a61,(%rsp)
2e: 0f b6 04 3c movzbl (%rsp,%rdi,1),%eax
32: 48 8b 54 24 08 mov 0x8(%rsp),%rdx
37: 64 48 33 14 25 28 00 xor %fs:0x28,%rdx
3e: 00 00
40: 75 05 jne 47
42: 48 83 c4 18 add $0x18,%rsp
46: c3 retq
47: e8 00 00 00 00 callq 4c
因此,在本例中,GCC选择将字符串与代码内联存储,并在执行期间将其复制到堆栈上。我认为这是非常有效的,因为处理器的缓存中已经填充了字符串,因此不会发生内存访问
编辑
总而言之,这三个版本都是等效的。然而,根据您的编译器实现,一个版本可能比另一个版本更有效。对于GCC,infunc
似乎对短字符串更有效,因为字符串是随指令一起提取的。对于较大的字符串,我将使用inplace
或inglobals
出生的C++程序员应该学会阅读汇编,这样她就可以尝试并看到差异(如果有的话!)对于她自己!…那么返回'a'+i
呢?我不想把它作为一个答案,因为我不确定,但我希望在优化过程中,不断的折叠会在相当早的时候将后两者转换为第一个。@akavel:没有“为什么”第三部分。该语言不强制要求其实现的细节。所有三段代码的行为都相同(第三种情况下的全局代码除外),因此它们都可以在任何(可能相同的)语言中实现是的,我知道对于任何3个字符,我们仍然可以很容易地找到一个系列-我可以保持现在的状态,不进一步更改为100个字符(以避免任何多项式答案的机会)?;对于汇编,它不会回答“为什么?”part.argh,我习惯了英特尔语法,从来没有花足够的时间学习AT&T:/案例2(infunc)对于小字符串是否会更快?可能现在需要进行基准测试…如果是,那么案例1(inplace)为什么不进行测试还要优化堆栈吗?非常感谢您的回答和所有这些努力!事实上,为了确保infunc
更快,需要进行基准测试。我想这不是小事:它的优势只会在字符串不在缓存中的情况下显现出来,也就是说,您必须在每次调用之前刷新缓存,但不能在缓存中测量刷新你的基准。至于inplace
,你没有告诉编译器创建一个局部变量。我认为这会违反“精神”让编译器做一些它没有被要求做的事情。嗯,很有趣;我没有考虑关于字符串内容变化的含义。因此,我现在也开始怀疑inplace字符串(案例1)根据定义,它实际上是const
,或者不是。不过,看看user1202136的答案,IIUC gcc认为至少在这种情况下它是const
。
0000000000000010 :
10: 48 83 ec 18 sub $0x18,%rsp
14: 48 63 ff movslq %edi,%rdi
17: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
1e: 00 00
20: 48 89 44 24 08 mov %rax,0x8(%rsp)
25: 31 c0 xor %eax,%eax
27: c7 04 24 61 7a 53 00 movl $0x537a61,(%rsp)
2e: 0f b6 04 3c movzbl (%rsp,%rdi,1),%eax
32: 48 8b 54 24 08 mov 0x8(%rsp),%rdx
37: 64 48 33 14 25 28 00 xor %fs:0x28,%rdx
3e: 00 00
40: 75 05 jne 47
42: 48 83 c4 18 add $0x18,%rsp
46: c3 retq
47: e8 00 00 00 00 callq 4c