为什么将 const 属性应用于纯函数不能减少运行时间?
根据 GNU 文档:
int square (int) __attribute__ ((const))
告诉 GCC 后续调用具有相同参数值的函数 square 可以 被第一次调用的结果替换,无论 之间的语句。
我预计在删除函数声明中的 __attribute__((const))
时,以下代码会变慢。
#include <stdio.h>
#include <limits.h>
int my_double(int b) __attribute__((const));
//int my_double(int b);
int main(void) {
long result = 0;
for (int i = 0; i < INT_MAX/2; i++)
{
result += my_double(5);
}
printf("%ld\n", result);
}
int my_double(int b) {
return b*2;
}
然而,实验表明__attribute__((const))
不会显着影响时序结果。有谁知道原因吗?谢谢。
顺便说一句,我使用以下命令清除任何可能污染每个实验的计时结果的缓存。
sudo sh -c 'echo 3 > /proc/sys/vm/drop_caches'
sudo swapoff -a
sudo swapon -a
并使用 /usr/bin/time 来计时实验。
附言。对应的汇编如下:(我对汇编不熟悉)
.file "attribute-o.c"
.text
.section .rodata
.LC0:
.string "%ld\n"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq $0, -8(%rbp)
movl $0, -12(%rbp)
jmp .L2
.L3:
movl $5, %edi
call my_double
cltq
addq %rax, -8(%rbp)
addl $1, -12(%rbp)
.L2:
cmpl $1073741822, -12(%rbp)
jle .L3
movq -8(%rbp), %rax
movq %rax, %rsi
leaq .LC0(%rip), %rax
movq %rax, %rdi
movl $0, %eax
call printf@PLT
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.globl my_double
.type my_double, @function
my_double:
.LFB1:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movl -4(%rbp), %eax
addl %eax, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size my_double, .-my_double
.ident "GCC: (Ubuntu 11.2.0-7ubuntu2) 11.2.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:
According to the GNU document:
int square (int) __attribute__ ((const))
tells GCC that subsequent calls to function square with the same argument value can
be replaced by the result of the first call regardless of the
statements in between.
I expect the following code will be slow down when removing __attribute__((const))
in the function declaration.
#include <stdio.h>
#include <limits.h>
int my_double(int b) __attribute__((const));
//int my_double(int b);
int main(void) {
long result = 0;
for (int i = 0; i < INT_MAX/2; i++)
{
result += my_double(5);
}
printf("%ld\n", result);
}
int my_double(int b) {
return b*2;
}
However, the experiments show that __attribute__((const))
does not affect timing results significantly. Does anyone know the reason? Thanks.
By the way, I use the following commands to clear any cache that might pollute the timing results of each experiment.
sudo sh -c 'echo 3 > /proc/sys/vm/drop_caches'
sudo swapoff -a
sudo swapon -a
And use /usr/bin/time
to time the experiments.
PS. The corresponding assembly is as follows: (I am unfamiliar with assembly)
.file "attribute-o.c"
.text
.section .rodata
.LC0:
.string "%ld\n"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq $0, -8(%rbp)
movl $0, -12(%rbp)
jmp .L2
.L3:
movl $5, %edi
call my_double
cltq
addq %rax, -8(%rbp)
addl $1, -12(%rbp)
.L2:
cmpl $1073741822, -12(%rbp)
jle .L3
movq -8(%rbp), %rax
movq %rax, %rsi
leaq .LC0(%rip), %rax
movq %rax, %rdi
movl $0, %eax
call printf@PLT
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.globl my_double
.type my_double, @function
my_double:
.LFB1:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movl -4(%rbp), %eax
addl %eax, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size my_double, .-my_double
.ident "GCC: (Ubuntu 11.2.0-7ubuntu2) 11.2.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
__attribute__ ((const))
是关于同一表达式内的公共子表达式消除。表达式
my_double(5)
中没有公共子表达式,因此无法消除任何内容,并且生成的代码是相同的。如果将
my_double(5)
替换为my_double(5) + my_double(5)
,则有两个公共子表达式。由于编译器不知道my_double(5)
的作用(可能会有副作用),因此必须调用my_double(5)
两次,除非编译器知道my_double
没有副作用(因为__attribute__ ((const))
)然后my_double(5)
只能调用一次,结果可以相加。检查这里:https://www.godbolt.org/z/bGnfq9zM5
__attribute__ ((const))
is about common subexpression elimination inside the same expression.There is no common subexpresson in the expression
my_double(5)
, therefore nothing can be eliminated and the generated code is the same.If you replace
my_double(5)
withmy_double(5) + my_double(5)
, then there are two common subexpressions. As the compiler does not know whatmy_double(5)
does (there may be side effects),my_double(5)
must be called twice unless the compiler knows thatmy_double
has no side effects (because of__attribute__ ((const))
) and thenmy_double(5)
can only be called once and the results can be added.Check here: https://www.godbolt.org/z/bGnfq9zM5