2012-02-04 28 views
5

昨天我接受了采访。那里他们问我代码优化何时发生? 说,代码优化何时发生?

int abc;//Global variable 
abc = 3; 
if(abc == 3) 
{ 
    printf("abc will be always 3"); 
} 
else 
{ 
    printf("This will never executed"); 
} 

现在的问题是,当发生了优化? A ...在运行时间 B ...在编译时。 我在编译时回答...对我来说,我认为编译器会在编译时检查volatile关键字。如果变量没有被声明为volatile,那么它会优化代码。但是,当编译器知道时,这个变量永远不会不是3? 如果它在运行时,那么当编译器知道变量永远不会是3以外的时候?因为如果在这部分代码执行后变量将被改变。请清除我的疑问

回答

4

大多数代码优化不能在运行时发生的,至少没怎么你的意思。代码在执行期间不能改变,以反映一组新的或不同的变量,这只会造成绝对的混乱。

在运行时可以做些什么是通过代码选择最佳路径,但这主要是手动完成的,以便创建单独的路径,提前出局,分支等以实现优化。

像这样写的代码可以编译时优化,因为编译器可以检查任何可能的替代值abc,如果找不到,可以优化调用。搜索的范围会极大地影响是否会发生,然而,编译器设置会影响这一点。

如果编译器只是简单地优化单个目标文件,而你的第二行是来自打印部分的另一个文件,那么它可能无法保证abc不会改变,所以不能够进行优化这一切。无论使用何种变量,它都取决于编译器设置的激进程度以及它们是否被允许放弃无用分支,或者会考虑这样做。优化尺寸可能更容易将分支移除而不是速度,但中/高设置可能会以任何方式进行(如果可能)。

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

在编译器无法修剪死代码的情况下,您可以提示它(最近的构造如constexpr可以帮助您)或自己添加优化。这可以像将最可能的路径放在第一位并在其他位置包括返回一样简单,从而使CPU不会跳跃。这种微型优化不太可能是必要的,当然不是这样的简单例子,其中if本身就是很多优化。

8

C代码通常是使用静态编译(也称为提前编译)编译的。代码一旦离开编译器就会立即生效;它在运行时不能改变。

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

+1

在C中最接近运行时优化的是基于Profiler的优化。您编译该程序,然后运行它,同时使用分析器收集统计信息。然后再次编译,将这些统计数据提供给编译器。第二个编译将使用这些信息来生成更快的代码。 – ugoren 2012-02-04 17:00:18

1

我并不太熟悉编译器如何优化代码,但是我知道从您在那里的代码中,编译器可以推断出abc永远不会被更改。这意味着它可以可能完全取出if声明,只是调用第一个printf函数。

然后在运行时,没有太多优化,因为代码非常简单。如果abc发生变化,那么显然if不能被消除,然而,然后,在运行时,有一些事情像CPU上的分支预测,将尝试预测正在采取的代码路径。这也可以被认为是一种优化形式。

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

2

在编译时:)

(当然,原则上依赖于编译器等)

1

我不知道他们是在寻找一个答案或几个可能的答案。

编译器通常在运行时不做任何事情。编译器二进制文件及其组件不需要在运行时环境中存在(对于语言C)。

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

  • DEF使用的分析
  • 静态单分配(SSA)分析
  • 常数传播沿着(变换如果成如果(3 == 3)
    然后
  • 常量表达式消除
    然后
  • 死代码/死分支去除

另一方面,某些编译器可能无法计算出足以在优化期间将其删除的情况。在这种情况下,如果您使用分支预测在芯片上运行代码,芯片会“了解”第一个分支是预测的,并缓存(获取)该分支的优势。但这不是一种编译器机制,通常不称为优化。

1

最简单的回答: 代码优化发生在写作时。

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

难以回答: 考虑到全局范围,编译器应该假定它可以在文件的其他地方访问。多次通过可以优化它。如果编译器认为它不是静态文件(考虑具有平坦内存模型的系统),那么global就是全球性的,假设应该是任何可以改变值的东西。这就是为什么你要避免使用全局变量,除非这个意图真的是全局访问。

取决于处理器,分支预测参数将适用,但大部分是编译时或根本不是。

ps:我真的很不喜欢这样的面试问题。

+0

同意你的回答,但还有一个疑问出现了,如果全局变量(非易失性)可以在任何时间点改变,那么为什么我们需要易变? – 2012-02-04 19:43:56

+0

对于显示为静态的上下文,需要volatile。考虑指针传递的值。此外,如果您直接映射硬件寄存器(考虑外设存储器映射或设备寄存器),则值可以更改,而无需运行任何代码。 – Dtyree 2012-02-04 20:02:42

3

不回答这个问题,但举了一个编译时优化的例子。当被要求时,gcc优化了代码。 -O(优化)选项使不同级别的优化成为可能。它可以用作-O1,-O2和-O3。 gcc手册页精确地描述了每个级别的含义。

-S选项将C转换为汇编并保存在.s文件中。

test.c的

#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"); 
} 

Whitout gcc的优化这两个字符串出现在汇编代码。

$ GCC -S test.c的;猫test.s

.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 

维特GCC级1优化只有一个字符串被转换成装配

$ GCC -O1 -S测试。 c; cat test.s

.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 
+0

优秀....解释 – 2012-02-10 05:25:04