C ++ 컴파일러가이 조건부 부울 할당을 무조건 할당으로 최적화하지 않는 이유는 무엇입니까?


117

다음 기능을 고려하십시오.

void func(bool& flag)
{
    if(!flag) flag=true;
}

flag에 유효한 부울 값 true이 있으면 다음과 같이 무조건으로 설정하는 것과 동일합니다 .

void func(bool& flag)
{
    flag=true;
}

그러나 gcc도 clang도 이런 방식으로 최적화하지 않습니다. 둘 다 -O3최적화 수준 에서 다음을 생성합니다 .

_Z4funcRb:
.LFB0:
    .cfi_startproc
    cmp BYTE PTR [rdi], 0
    jne .L1
    mov BYTE PTR [rdi], 1
.L1:
    rep ret

내 질문은 : 코드가 최적화하기에는 너무 특수한 경우 입니까? flag아니면 그러한 최적화가 원하지 않는 이유 가 volatile있습니까? flag그럴 수있는 유일한 이유는 그것을 읽는 시점에서 정의되지 않은 동작없이 어떤 식 으로든 비 true또는 false가치를 가질 수 있다는 것입니다. 그러나 이것이 가능한지 확실하지 않습니다.


8
"최적화"라는 증거가 있습니까?
David Schwartz

1
@ 200_success 작동하지 않는 마크 업이있는 코드 줄을 제목으로 넣는 것은 좋은 일이라고 생각하지 않습니다. 좀 더 구체적인 제목을 원한다면 괜찮지 영어 문장을 선택하고 그 안에있는 코드를 피하십시오 (예 : 컴파일러가 조건부 쓰기를 조건부 쓰기가 동등하다는 것을 증명할 수있을 때 최적화하지 않는 이유는 무엇입니까? ). 또한 백틱은 렌더링되지 않으므로 코드를 사용하더라도 제목에 사용하지 마십시오.
Bakuriu

2
@Ruslan은 함수 자체에 대해이 최적화를 수행하지 않는 것처럼 보이지만 코드를 인라인 할 수있을 때 인라인 버전에서는 그렇게하는 것 같습니다. 종종 컴파일 시간 상수 1가 사용됩니다. godbolt.org/g/swe0tc
Evan Teran

답변:


102

이는 캐시 일관성 고려 사항 으로 인해 프로그램 성능에 부정적인 영향을 미칠 수 있습니다 . 호출 될 flag때마다 쓰기 func()는 포함하는 캐시 라인을 더럽 힙니다. 이것은 기록되는 값이 쓰기 전에 대상 주소에서 찾은 비트와 정확히 일치하는지 여부에 관계없이 발생합니다.


편집하다

hvd 는 이러한 최적화를 방지하는 또 다른 좋은 이유 를 제공했습니다 . 그것은 정의되지 않은 동작을 초래할 수 있기 때문에 제안 된 최적화에 대한 더 강력한 주장입니다.

조금 더 숙고 한 후, 컴파일러가 특정 컨텍스트에 대해 변환이 안전하다는 것을 증명할 수없는 경우 무조건 쓰기를 도입하는 것으로부터 컴파일러를 강력하게 금지해야하는 이유를 하나 더 제안 할 수 있습니다. 이 코드를 고려하십시오.

const bool foo = true;

int main()
{
    func(const_cast<bool&>(foo));
}

무조건 쓰기를 사용 func()하면 확실히 정의되지 않은 동작이 트리거됩니다 (읽기 전용 메모리에 쓰기는 쓰기 효과가 작동하지 않더라도 프로그램을 종료합니다).


7
분기를 제거하기 때문에 성능에 긍정적 인 영향을 미칠 수도 있습니다. 그래서 저는이 특별한 경우가 매우 특정한 시스템을 염두에 두지 않고 논의하기에 의미가 없다고 생각합니다.
Lundin

3
@Yakk 동작 정의는 대상 플랫폼의 영향을받지 않습니다. 그것이 프로그램을 종료 할 것이라고 말하는 것은 잘못된 것이지만 UB 자체는 비강 악마를 포함하여 광범위한 결과를 초래할 수 있습니다.
John Dvorak

16
@Yakk "읽기 전용 메모리"가 의미하는 바에 따라 다릅니다. 아니요, ROM 칩에는 없지만 쓰기 액세스가 활성화되지 않은 페이지에로드 된 섹션에있는 경우가 매우 많으며 쓰기를 시도하면 SIGSEGV 신호 또는 STATUS_ACCESS_VIOLATION 예외가 발생합니다.
Random832

5
"이것은 확실히 정의되지 않은 동작을 유발합니다". 아니요. 정의되지 않은 동작은 추상 기계의 속성입니다. UB가 있는지 여부를 결정하는 것은 코드가 말하는 것입니다. 컴파일러는이를 유발할 수 없습니다 (버그가있는 경우 컴파일러로 인해 프로그램이 잘못 작동 할 수 있음).
Eric M Schmidt

7
무조건 쓰기가 아니라 정의되지 않은 동작의 소스 인 데이터수정할 수const 있는 함수로 전달 하는 것은 캐스트 어웨이입니다 . 의사, 아파 내가 할 때 ...
스펜서

48

성능에 대한 Leon의 답변 외에 :

가정하자가 flag있다 true. 두 개의 스레드가 지속적으로을 호출한다고 가정합니다 func(flag). 이 경우 작성된 ​​함수는에 아무것도 저장하지 않으므로 flag스레드로부터 안전해야합니다. 두 개의 스레드가 동일한 메모리에 액세스하지만 읽기 전용입니다. 무조건으로 설정 flag하면 true두 개의 다른 스레드가 동일한 메모리에 쓰게됩니다. 이것은 안전하지 않습니다. 기록중인 데이터가 이미있는 데이터와 동일하더라도 안전하지 않습니다.


9
나는 생각 이 적용한 결과입니다 [intro.races]/21.
Griwes 2016-10-28

10
매우 흥미로운. 나는이 글을 읽을 그래서 같이 컴파일러가되어 결코 추상 기계가 일을하지 않았을 쓰기 작업 "에 최적화"를 허용하지 않습니다.
Martin Ba

3
@MartinBa 대부분 그렇습니다. 그러나 컴파일러가 그것이 중요하지 않다는 것을 증명할 수 있다면, 예를 들어 다른 스레드가 해당 특정 변수에 액세스 할 수 없다는 것을 증명할 수 있기 때문에 괜찮을 수 있습니다.

13
이것은 컴파일러가 대상으로하는 시스템이 안전하지 않은 경우 에만 안전 하지 않습니다 . 0x01이미 0x01"안전하지 않은"동작을 일으키는 바이트에 쓰는 시스템에서 개발 한 적이 없습니다 . 워드 또는 dword 메모리 액세스가있는 시스템에서는 다음과 같이됩니다. 그러나 옵티마이 저는 이것을 알고 있어야합니다. 최신 PC 또는 전화 OS에서는 문제가 발생하지 않습니다. 따라서 이것은 유효한 이유가 아닙니다.
Yakk-Adam Nevraumont

4
@Yakk 사실, 더 많이 생각하면 이것이 일반 프로세서에서도 옳다고 생각합니다. CPU가 메모리에 직접 쓸 수있을 때 당신이 옳다고 생각하지만 flag, copy-on-write 페이지에 있다고 가정 합니다. 이제 CPU 수준에서 동작이 정의 될 수 있지만 (페이지 오류, OS가 처리하도록 함) OS 수준에서는 여전히 정의되지 않을 수 있습니다.

13

여기서는 C ++의 동작에 대해 잘 모르겠지만 메모리에 1이 아닌 0이 아닌 값이 포함되어 있으면 검사에서는 변경되지 않지만 검사에서는 1로 변경되기 때문에 C에서는 메모리가 변경 될 수 있습니다.

하지만 C ++에 능통하지 않기 때문에 이런 상황이 가능한지 모르겠습니다.


이것이 여전히 사실 _Bool일까요?
Ruslan

5
C에서 메모리에 ABI가 해당 유형에 대해 유효하다고 말하지 않은 값이 포함되어 있으면 트랩 표현이고 트랩 표현 읽기는 정의되지 않은 동작입니다. C ++에서는 초기화되지 않은 객체를 읽을 때만 발생할 수 있으며 UB 인 초기화되지 않은 객체를 읽을 때만 발생할 수 있습니다. 당신이 0이 아닌 값이 유형에 대한 유효라는 ABI 찾을 수 있다면 bool/ _Bool수단을 true, 그 특정 ABI에, 당신은 아마 맞다.

1
@Ruslan Itanium ABI를 사용하는 컴파일러와 ARM 프로세서에서 C _Bool및 C ++ bool는 동일한 유형이거나 동일한 규칙을 따르는 호환 유형입니다. MSVC를 사용하면 크기와 정렬이 동일하지만 동일한 규칙을 사용하는지 여부에 대한 공식적인 진술은 없습니다.
저스틴 시간 - 분석 재개 모니카

1
@JustinTime : C <stdbool.h>typedef _Bool bool; x86 (적어도 System V ABI에서는)에서 And yes를 포함하고 , bool/ _Bool는 바이트의 상위 비트가 지워진 상태에서 0 또는 1이어야합니다. 나는이 설명이 타당하다고 생각하지 않는다.
Peter Cordes 2016

1
@JustinTime : 사실입니다. System V ABI의 모든 x86 버전에서 동일한 의미를 가지고 있다는 점을 지적 했어야합니다. 이것이 바로이 질문에 관한 것입니다. (첫 번째 인수 func는 RDI에서 전달되었지만 Windows는 RDX를 사용 하기 때문에 알 수 있습니다 ).
Peter Cordes 2016
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.