gpt4 book ai didi

C编译器可以在运行时消除这个条件测试吗?

转载 作者:行者123 更新时间:2023-12-03 16:53:30 25 4
gpt4 key购买 nike

假设我有这样的伪代码:

main() {
BOOL b = get_bool_from_environment(); //get it from a file, network, registry, whatever
while(true) {
do_stuff(b);
}
}

do_stuff(BOOL b) {
if(b)
path_a();
else
path_b();
}

现在,既然我们知道外部环境可以影响 get_bool_from_environment()可能产生真或假的结果,那么我们知道这两个代码if(b) 的 true 和 false 分支必须包含在二进制文件中。我们不能简单地从代码中省略 path_a();path_b();

但是——我们只设置了一次BOOL b,我们总是在程序初始化后重复使用相同的值。

如果我要编写这个有效的 C 代码,然后使用 gcc -O0 编译它,则每次在处理器上重复计算 if(b) do_stuff(b) 被调用,在我看来,它将不必要的指令插入管道中,用于在初始化后基本上是静态的分支。

如果我假设我实际上有一个像 gcc -O0 一样愚蠢的编译器,我会重写这段代码以包含一个函数指针和两个独立的函数,do_stuff_a()do_stuff_b(),它们不执行 if(b) 测试,而是继续执行两个路径之一.然后,在 main() 中,我将根据 b 的值分配函数指针,并在循环中调用该函数。这消除了分支,尽管它确实为函数指针取消引用添加了内存访问(由于架构实现 I don't think 我真的需要担心这一点)。

是否有可能,甚至在原则上,编译器采用与原始伪代码样本相同风格的代码,并意识到一旦 b 的值被赋值一次就不需要测试在 main() 中?如果是这样,此编译器优化的理论名称是什么?能否请您举一个执行此操作的实际编译器实现(开源或其他方式)的示例?

我意识到编译器无法在运行时生成动态代码,原则上唯一可以做到这一点的系统类型是字节码虚拟机或解释器(例如 Java、.NET、Ruby 等)——所以问题仍然是是否可以静态 执行此操作并生成包含 path_a(); 分支和 path_b() 分支,但避免为 do_stuff(b); 的每次调用评估条件测试 if(b)

最佳答案

如果您告诉编译器进行优化,if(b) 很有可能只被计算一次。

稍微修改给定的示例,使用标准的 _Bool 而不是 BOOL,并添加缺少的返回类型和声明,

_Bool get_bool_from_environment(void);
void path_a(void);
void path_b(void);

void do_stuff(_Bool b) {
if(b)
path_a();
else
path_b();
}

int main(void) {
_Bool b = get_bool_from_environment(); //get it from a file, network, registry, whatever
while(1) {
do_stuff(b);
}
}

clang -O3 [clang-3.0] 生成的(相关部分)是

    callq   get_bool_from_environment
cmpb $1, %al
jne .LBB1_2
.align 16, 0x90
.LBB1_1: # %do_stuff.exit.backedge.us
# =>This Inner Loop Header: Depth=1
callq path_a
jmp .LBB1_1
.align 16, 0x90
.LBB1_2: # %do_stuff.exit.backedge
# =>This Inner Loop Header: Depth=1
callq path_b
jmp .LBB1_2

b 仅被测试一次,main 跳入 path_apath_b 的无限循环,具体取决于b 的值。如果 path_apath_b 足够小,它们将被内联(我强烈希望)。使用 -O-O2,clang 生成的代码将在循环的每次迭代中计算 b

gcc (4.6.2) 的行为与 -O3 类似:

    call    get_bool_from_environment
testb %al, %al
jne .L8
.p2align 4,,10
.p2align 3
.L9:
call path_b
.p2align 4,,6
jmp .L9
.L8:
.p2align 4,,8
call path_a
.p2align 4,,8
call path_a
.p2align 4,,5
jmp .L8

奇怪的是,它为 path_a 展开了循环,但没有为 path_b 展开循环。使用 -O2-O,它会在无限循环中调用 do_stuff

因此

Is it possible, even in principle, for a compiler to take code of the same style as the original pseudocode sample, and to realize that the test is unnecessary once the value of b is assigned once in main()?

答案是肯定的是的,编译器有可能认识到这一点并利用这一事实。优秀的编译器会在被要求努力优化时做到。

If so, what is the theoretical name for this compiler optimization, and can you please give an example of an actual compiler implementation (open source or otherwise) which does this?

我不知道优化的名称,但执行此操作的两个实现是 gcc 和 clang(至少是最近发布的版本)。

关于C编译器可以在运行时消除这个条件测试吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14343511/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com