C 什么时候进行代码优化?

C 什么时候进行代码优化?,c,optimization,runtime,compile-time,C,Optimization,Runtime,Compile Time,昨天,我参加了一次面试。他们问我什么时候进行代码优化? 说 现在的问题是什么时候进行优化? A.在运行时 B在编译时。 我在编译时回答。。。我认为,编译器在编译时检查volatile关键字。如果变量没有声明为volatile,那么它会优化代码。但是,当编译器知道这一点时,这个变量永远不会超过3? 若它是在运行时,那个么当编译器知道变量永远不会超过3时?因为如果变量在执行这部分代码后要更改。请澄清我的疑问C代码通常使用静态(即提前)编译。一旦代码离开编译器,它就一成不变了;它不能在运行时更改 这与

昨天,我参加了一次面试。他们问我什么时候进行代码优化? 说

现在的问题是什么时候进行优化? A.在运行时 B在编译时。 我在编译时回答。。。我认为,编译器在编译时检查volatile关键字。如果变量没有声明为volatile,那么它会优化代码。但是,当编译器知道这一点时,这个变量永远不会超过3?
若它是在运行时,那个么当编译器知道变量永远不会超过3时?因为如果变量在执行这部分代码后要更改。请澄清我的疑问

C代码通常使用静态(即提前)编译。一旦代码离开编译器,它就一成不变了;它不能在运行时更改


这与使用的语言(如Java)形成对比。在那里,优化几乎可以在程序运行的任何时候发生。

我不太熟悉编译器如何优化代码,但是我知道,从您那里的代码中,编译器可以推断出,
abc
从未被更改过。这意味着它可以完全去掉
if
语句,只调用第一个
printf
函数

然后在运行时,没有太多的优化,因为代码非常简单。如果
abc
发生变化,那么显然
If
不能被忽略,但是,在运行时,CPU上的分支预测之类的东西会尝试预测所采用的代码路径。这也可以被认为是一种优化形式

注意:我并没有声称这就是发生的情况,但我可以看到编译器可能会以这种方式进行优化,在一种激进的优化设置中。

在编译时:)


(好吧,原则上这取决于编译器等)

大多数代码优化都不能在运行时进行,至少不能按您的意思进行。代码不能在执行期间更改以反映新的或不同的变量集,这只会造成绝对混乱

在运行时可以做的是通过代码选择最佳路径,但这必须主要手动完成,以便创建单独的路径、早期输出、分支等,以允许优化

这样的代码允许编译时优化,因为编译器可以检查abc的任何可能的替代值,如果没有找到,则优化调用。但是,搜索的范围会极大地影响这种情况是否会发生,编译器设置也会影响这种情况

如果编译器只是简单地优化单个对象文件,而您的第二行位于打印部分的另一个文件中,那么它可能无法保证
abc
不会更改,因此根本无法对其进行优化。不管变量的使用情况如何,这取决于编译器设置的积极性以及它们是否被允许丢弃死分支,或者会考虑这样做。优化大小可能比优化速度更有可能删除分支,但中/高设置可能会以任何方式执行(如果可能)

大多数现代编译器都有一个完整的程序优化选项,这会将大部分优化延迟到链接器阶段。这允许它搜索整个程序,潜在地发现
abc
从未在其他地方更改或使用过,并从条件中删除变量和失败分支。对于每个对象,整个程序优化比单独优化要有效得多,因为它允许更精确的搜索


如果编译器无法修剪死代码,您可以提示它(最近的构造,如
constexpr
可以帮助),或者自己添加优化。它可以简单到将最可能的路径放在第一位,并在其他路径之前包含一个返回,从而避免CPU跳转。这种微观优化不太可能是必要的,当然不是在这样一个简单的例子中,如果
本身就是大量的优化。

我想知道他们是在寻找一个答案还是几个可能的答案

编译器通常在运行时不执行任何操作。编译器二进制文件及其组件不需要在运行时环境中出现(对于C语言)

消除这个死分支的优化类型将使用某些技术来编写优化编译器。参见Muchnik关于编译器的书。所使用的技术可能涉及创建基本块的有向图,然后使用以下方法之一:

  • def使用分析
  • 静态单一分配(ssa)分析
    连同
  • 恒定传播(将if转换为if(3==3)
    然后
  • 常量表达式消除
    然后
  • 失效代码/失效分支移除
另一方面,某些编译器的计算可能不足以在优化过程中删除此项

在这种情况下,如果在具有分支预测的芯片上运行代码,芯片将“了解”第一个分支已被预测,并对该分支进行有利的缓存(获取)。但这不是一种编译器机制,通常不称为优化。

最简单的答案: 代码优化发生在编写时

简单回答: 也许,给定范围,它依赖于编译器

答案很难: 考虑到全局作用域,编译器应该将其放在一边,假设它可以在文件的其他地方访问。多次传递可以优化它。如果编译器不认为它对文件是静态的(想想具有平面内存模型的系统),那么global就是真正的global,假设任何东西都可能改变值。这就是为什么你避免使用global,除非真正的目的是全局访问

取决于p
int abc;//Global variable
abc = 3;
if(abc == 3)
{
  printf("abc will be always 3");
}
else
{
  printf("This will never executed");
}
#include <stdio.h>

int abc;//Global variable

void main()
{
    abc = 3;
    if(abc == 3)
        printf("abc will be always 3");
    else
        printf("This will never executed");
}
    .file   "test.c"
    .comm   abc,4,4
    .section    .rodata
.LC0:
    .string "abc will be always 3"
.LC1:
    .string "This will never executed"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    $3, abc(%rip)
    movl    abc(%rip), %eax
    cmpl    $3, %eax
    jne .L2
    movl    $.LC0, %eax
    movq    %rax, %rdi
    movl    $0, %eax
    call    printf
    jmp .L1
.L2:
    movl    $.LC1, %eax
    movq    %rax, %rdi
    movl    $0, %eax
    call    printf
.L1:
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (GNU) 4.6.1 20110908 (Red Hat 4.6.1-9)"
    .section    .note.GNU-stack,"",@progbits
    .file   "test.c"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "abc will be always 3"
    .text
    .globl  main
    .type   main, @function
main:
.LFB11:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $3, abc(%rip)
    movl    $.LC0, %edi
    movl    $0, %eax
    call    printf
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE11:
    .size   main, .-main
    .comm   abc,4,4
    .ident  "GCC: (GNU) 4.6.1 20110908 (Red Hat 4.6.1-9)"
    .section    .note.GNU-stack,"",@progbits