为什么将 const 属性应用于纯函数不能减少运行时间?
Why applying a const attribute to a pure function cannot reduce elapsed time?
根据 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.
我预计在函数声明中删除 __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
为实验计时。
PS。对应的程序集如下:(我对程序集不熟悉)
.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 , %rsp
movq [=12=], -8(%rbp)
movl [=12=], -12(%rbp)
jmp .L2
.L3:
movl , %edi
call my_double
cltq
addq %rax, -8(%rbp)
addl , -12(%rbp)
.L2:
cmpl 73741822, -12(%rbp)
jle .L3
movq -8(%rbp), %rax
movq %rax, %rsi
leaq .LC0(%rip), %rax
movq %rax, %rdi
movl [=12=], %eax
call printf@PLT
movl [=12=], %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:
__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)
只能调用一次,结果可以相加
根据 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.
我预计在函数声明中删除 __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
为实验计时。
PS。对应的程序集如下:(我对程序集不熟悉)
.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 , %rsp
movq [=12=], -8(%rbp)
movl [=12=], -12(%rbp)
jmp .L2
.L3:
movl , %edi
call my_double
cltq
addq %rax, -8(%rbp)
addl , -12(%rbp)
.L2:
cmpl 73741822, -12(%rbp)
jle .L3
movq -8(%rbp), %rax
movq %rax, %rsi
leaq .LC0(%rip), %rax
movq %rax, %rdi
movl [=12=], %eax
call printf@PLT
movl [=12=], %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:
__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)
只能调用一次,结果可以相加