萨特说:



我一直想知道为什么编译器不初始化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/

10-12 18:21