萨特说:
我一直想知道为什么编译器不初始化int32之类的原语,并且不将其浮点数设置为0。如果编译器对其进行初始化,将会对性能造成什么影响?它应该比不正确的代码更好。
最佳答案
实际上,该论点是不完整的。统一变量可能有两个原因:效率和缺少适当的默认值。
1)效率
在大多数情况下,这是过去的遗忘,那时C编译器只是C到汇编翻译器,而没有执行任何优化。
如今,我们有了智能的编译器和Dead Store Elimination,它们在大多数情况下将消除冗余存储。演示:
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)默认?
另一个问题是缺乏合适的值(value)。虽然可以将
float
完全初始化为NaN
,但整数是什么?没有整数值表示没有值,根本没有值! 0
是一个候选词(其中一个是候选词),但有人可以说这是最差的候选词:这是一个非常可能的数字,因此对于手边的用例可能具有特定的含义;您确定您对默认的含义感到满意吗?思考的食物
最后,统一变量有一个巧妙的好处:它们是可检测的。编译器可能会发出警告(如果足够聪明的话),并且Valgrind 将引发错误。这使逻辑问题可检测到,并且仅可纠正检测到的问题。
当然,哨兵值(例如
NaN
)将同样有用。不幸的是……没有整数。关于c++ - 如果初始化变量,对编译器的性能有何影响?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/12612707/