为什么将 const 属性应用于纯函数不能减少运行时间?

发布于 2025-01-09 18:04:05 字数 2539 浏览 5 评论 0原文

根据 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

绾颜 2025-01-16 18:04:06

__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) with my_double(5) + my_double(5), then there are two common subexpressions. As the compiler does not know what my_double(5) does (there may be side effects), my_double(5) must be called twice unless the compiler knows that my_double has no side effects (because of __attribute__ ((const))) and then my_double(5) can only be called once and the results can be added.

Check here: https://www.godbolt.org/z/bGnfq9zM5

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文