2012-09-27 2 views
1

셔터이 말한다 :변수를 초기화하는 경우 컴파일러의 성능에 어떤 영향이 있습니까?

"C와 C++ 모두의 낮은 수준의 효율 전통에서 컴파일러는 자주 그것을 명시 적으로 (예를 들어, 지역 변수, 잊어 회원을하지 않는 변수를 초기화 할 필요가 없습니다 생성자 초기화 목록)에서 생략 "

나는 항상 컴파일러가 INT32 같은 프리미티브를 초기화하고 컴파일러를 초기화하는 경우 성능 저하 무엇 0으로 뜨지 않는 이유를 궁금해? 잘못된 코드보다 좋을 것입니다.

+4

힙에 큰 배열이있는 경우를 상상해보십시오. – chris

+1

* "잘못된 코드보다 좋을 것입니다."* 컴파일러의 경고가 올바르게 설정되어 있으면이 문제가 발생하지 않아야합니다. 컴파일러가 이것을 강제해야한다고 표준이 말하면 나는 그것을 아주 부정적인 것으로 간주 할 것이고, 나는 그 문제에 대해 선택의 여지가 없다. – AusCBloke

+6

초기화되지 않은 변수는 문제가 아니며 초기화되지 않은 변수는 문제입니다. 올바른 코드에는 문제가 없으므로 성능에 대한 대가를 치러야합니다. – Blastfurnace

답변

0

기본적으로 변수는 메모리에서 데이터를 보유하도록 수정할 수있는 위치를 참조합니다. 단위화된 변수의 경우, 프로그램에서 알아야 할 것은이 위치가 어디에 있는지이며, 컴파일러는 일반적으로이를 미리 계산하므로 지시가 필요하지 않습니다. 하지만 초기화 (0)를 원할 때, 프로그램은 추가 명령을 사용해야합니다.

하나의 아이디어는 프로그램이 시작될 때 memset을 사용하여 전체 힙을 제로화 한 다음 모든 정적 재료를 초기화하는 것일 수 있지만 읽 기 전에 동적으로 설정된 항목에는 필요하지 않습니다. 이것은 함수가 호출 될 때마다 스택 프레임을 제로로 할 필요가있는 스택 기반 함수에서도 문제가 될 수 있습니다. 즉, 스택이 새로 호출 된 함수로 자주 덮어 쓰이는 경우 특히 변수가 기본값으로 정의되지 않도록 허용하는 것이 훨씬 효율적입니다.

0

-Wmaybe-와 함께 컴파일하고 초기화되지 않았습니다. 그것들은 컴파일러가 기본 초기화를 최적화 할 수없는 유일한 장소입니다. 힙에 관해서는

...

+0

그래도 짜증나는 경고 중 하나입니다. ( –

+0

사실. 코드에서 생성 된 경고 메시지를 계산하면 비용을 적절히 추정 할 수 있으므로 경고를 설정하는 것이 좋습니다. – Joshua

2

는 초기화가 성능에 영향을 줄 수있는 두 가지 방법이 있습니다.

먼저 변수를 초기화하는 데 시간이 걸립니다. 허락하신다면, 단일 변수에 대해서는 무시할 만하다. 그러나 다른 사람들이 제안한 것처럼 많은 변수, 배열 등을 더할 수있다.

둘째, 누가 0을 합리적으로 기본값이라고 할 수 있겠는가? 유용한 기본값 인 0 인 모든 변수에 대해 다른 변수가있을 수 있습니다. 이 경우 0으로 초기화하면 변수를 실제로 원하는 모든 값으로 재 초기화하는 오버 헤드가 발생합니다. 기본 초기화가 발생하지 않으면 기본적으로 한 번이 아닌 두 번 초기화 오버 헤드를 지불해야합니다. 기본값이나 0 또는 다른 값으로 선택하는 경우에도 마찬가지입니다.

오버 헤드가 존재할 경우 초기화하지 않고 컴파일러가 초기화되지 않은 변수에 대한 참조를 캐치하도록하는 것이 일반적으로 더 효율적입니다.

2

이 인수는 실제로 불완전합니다. 단위화된 변수에는 효율성과 적절한 기본값의 부족이라는 두 가지 이유가있을 수 있습니다.

1) 효율

그것은이다, 주로, C 컴파일러는 조립 번역에 단순히 C이었고 어떠한 최적화를 수행하지 옛날의 왼쪽 오버.

요즈음 우리는 똑똑한 컴파일러 및 Dead Store Elimination가있어 대부분의 경우 중복 저장을 제거합니다.데모 :

define i32 @foo(i32 %a) nounwind uwtable readnone { 
    %1 = add nsw i32 %a, 3 
    ret i32 %1 
} 

아직도, 심지어 스마트 컴파일러가 중복 저장을 제거 할 수 있으며,이 영향을 미칠 수 있습니다 경우가 있습니다

int foo(int a) { 
    int r = 0; 
    r = a + 3; 
    return r; 
} 

은로 변환됩니다. 나중에 조금씩 초기화되는 큰 배열의 경우 ... 컴파일러는 모든 값이 초기화되고 종료됩니다 실현하지 않을 수 있으며, 따라서 중복 쓰기를 제거하지 :에 다음 호출에

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이 후보자 중 하나이지만, 가장 나쁜 후보자 중 하나라고 주장 할 수 있습니다. 이는 매우 가능성있는 숫자이며, 따라서 현재 사용중인 유즈 케이스에 대해 특별한 의미를 가질 수 있습니다. 이 의미가 기본이되는 것에 대해 당신이 편안하다고 확신합니까?

생각에 대한

음식 마지막으로, 초기화되지 변수 중 하나 깔끔한 장점이있다 : 그들은 감지 할 수 있습니다. 컴파일러가 경고를 발행 할 수 있습니다 (충분히 똑똑한 경우). Valgrind 오류가 발생합니다. 이렇게하면 논리적 인 문제가 으로 검출 할 수 있으며, 탐지 된 내용 만 수정할 수 있습니다.

물론 NaN과 같은 센티널 값이 유용 할 것입니다. 불행히도 ... 정수에는 아무 것도 없습니다.

관련 문제