萨特说:
在C和C++的低效率传统中,编译器通常不需要初始化变量,除非您显式地初始化它(例如,局部变量,被遗忘的成员从构造函数初始化程序列表中省略)
我一直在想,为什么编译器不初始化原语,比如int32,然后浮动到0。如果编译器初始化它,性能会受到什么影响?它应该比不正确的代码更好。
发布于 2012-09-27 08:24:47
事实上,这个论点是不完整的。统一变量可能有两个原因:效率和缺乏合适的缺省值。
1)效率
大多数情况下,C编译器只是汇编翻译程序的C,而没有进行任何优化。
如今,我们有了智能编译器和消除死商店,在大多数情况下,这将消除冗余存储。演示:
int foo(int a) {
int r = 0;
r = a + 3;
return r;
}转化为:
define i32 @foo(i32 %a) nounwind uwtable readnone {
%1 = add nsw i32 %a, 3
ret i32 %1
}不过,在某些情况下,即使是更聪明的编译器也无法消除冗余存储,这可能会产生影响。对于后来被分段初始化的大型数组..。编译器可能没有意识到所有的值最终都会被初始化,因此不会删除冗余的写操作:
int foo(int a) {
int* r = new int[10]();
for (unsigned i = 0; i <= a; ++i) {
r[i] = i;
}
return r[a % 2];
}注意以下对memset的调用(我需要用()作为new调用的后缀,这是值初始化)。即使0是不需要的,它也没有被消除。
define i32 @_Z3fooi(i32 %a) uwtable {
%1 = tail call noalias i8* @_Znam(i64 40)
%2 = bitcast i8* %1 to i32*
tail call void @llvm.memset.p0i8.i64(i8* %1, i8 0, i64 40, i32 4, i1 false)
br label %3
; <label>:3 ; preds = %3, %0
%i.01 = phi i32 [ 0, %0 ], [ %6, %3 ]
%4 = zext i32 %i.01 to i64
%5 = getelementptr inbounds i32* %2, i64 %4
store i32 %i.01, i32* %5, align 4, !tbaa !0
%6 = add i32 %i.01, 1
%7 = icmp ugt i32 %6, %a
br i1 %7, label %8, label %3
; <label>:8 ; preds = %3
%9 = srem i32 %a, 2
%10 = sext i32 %9 to i64
%11 = getelementptr inbounds i32* %2, i64 %10
%12 = load i32* %11, align 4, !tbaa !0
ret i32 %12
}2)违约?
另一个问题是缺乏合适的价值。虽然float完全可以初始化为NaN,但整数又如何呢?没有一个整数值表示没有值,根本没有!0是一个候选值(包括其他的),但是有人可能会说它是最糟糕的候选值之一:它是一个非常可能的数字,因此可能对手头的usecase有一个特定的含义;您确定这个意思是默认的吗?
思想食粮
最后,统一变量有一个很好的优点:它们是可检测的。编译器可能会发出警告(如果它足够聪明的话),而将引发错误。这使得的逻辑问题可以检测到,并且只有检测到的内容才能被纠正。
当然,哨兵值(如NaN )也同样有用。不幸的是。整数没有。
发布于 2012-09-27 01:38:09
初始化可能会影响性能的方式有两种。
首先,初始化变量需要时间。当然,对于单个变量,它可能可以忽略不计,但正如其他人所建议的,它可以与大量的变量、数组等一起使用。
第二,谁说零是合理的违约?对于每一个对零是有用的缺省值的变量,可能还有另一个不适用的变量。在这种情况下,如果你真的初始化为零,那么你将这个变量重新初始化为你想要的任何值,就会招致更多的开销。实际上,您需要支付两次初始化开销,而不是在未发生默认初始化时支付一次。请注意,无论您选择什么作为默认值,零或其他值,这都是正确的。
考虑到开销的存在,不初始化和让编译器捕获对未初始化变量的引用通常更有效。
发布于 2012-09-27 01:26:34
基本上,变量引用内存中可以修改以保存数据的位置。对于一个统一的变量,程序所需要知道的就是这个位置,编译器通常会提前找出这一点,所以不需要指令。但是,当您希望初始化它(也就是说,0)时,程序需要使用额外的指令来实现。
一种想法可能是,在程序启动时,使用memset将整个堆清零,然后初始化所有静态内容,但在读取之前不需要对任何动态设置的堆进行初始化。对于基于堆栈的函数来说,这也是一个问题,每次调用函数时,都需要将它们的堆栈帧归零。简而言之,允许变量默认为未定义更有效,特别是当堆栈经常被新调用的函数覆盖时。
https://stackoverflow.com/questions/12612707
复制相似问题