{ A=a; B=b; }의 경우 "B=b" 이전에 "A=a"가 엄격히 실행되나?
가정하다A
B
a
, 그리고b
변수들, 모든 수 있는 .A
B
a
, 그리고b
하다하다. 과 같은 코드에 대해라다음 코드의 경우:
A = a;
B = b;
C 및 C++ 표준이 명시적으로 필요한지 여부A=a
전에 엄격히 집행되다B=b
의 가 ? ?인 것을 하면.A
B
a
, 그리고b
모두 다른가? 컴파일러가 최적화 등의 목적으로 두 문장의 실행 순서를 바꿀 수 있는가?
내 질문에 대한 대답이 C와 C++에서 다르다면 둘 다 알고 싶다.
편집: 질문의 배경은 다음과 같다.보드 게임 AI 설계에서 최적화를 위해 사람들은 자물쇠가 없는 공유 해시 테이블을 사용하며, 정확성은 우리가 추가하지 않으면 실행 순서에 따라 크게 달라진다.volatile
제한의
두 표준 모두 이러한 지침이 관찰 가능한 행동을 변경하지 않는 한 질서 없이 수행될 수 있도록 허용한다.이를 AS-If 규칙이라고 한다.
논평에서 지적된 바와 같이, "관찰할 수 있는 행동"이 의미하는 것은 정의된 행동을 가진 프로그램의 관찰 가능한 행동이라는 점에 유의한다.만약 당신의 프로그램에 정의되지 않은 행동이 있다면, 컴파일러는 그것에 대한 추론에서 면제된다.
컴파일러는 프로그램의 관찰 가능한 행동을 모방할 의무가 있을 뿐이므로, 재주문이 그 원칙을 위반하지 않는다면 그것은 허용될 것이다.동작이 잘 정의되어 있다고 가정할 때, 만약 당신의 프로그램에 데이터 레이스와 같이 정의되지 않은 동작이 포함되어 있다면, 프로그램의 동작은 예측할 수 없을 것이며, 코멘트한 대로 중요한 부분을 보호하기 위해 어떤 형태의 동기화를 사용해야 할 것이다.
A 유용한 참조 자료
이를 다루는 흥미로운 기사는 컴파일 시간에서의 메모리 오더인데 다음과 같이 쓰여 있다.
컴파일러 개발자와 CPU 벤더가 일반적으로 따르는 메모리 재주문 기본 규칙은 다음과 같이 표현할 수 있다.
단일 스레드 프로그램의 동작을 수정하지 마십시오.
예
이 기사는 우리가 이 재주문을 볼 수 있는 간단한 프로그램을 제공한다.
int A, B; // Note: static storage duration so initialized to zero
void foo()
{
A = B + 1;
B = 0;
}
그리고 더 높은 최적화 레벨에서B = 0
전에 끝나다A = B + 1
그리고 우리는 godbolt를 사용하여 이 결과를 재현할 수 있다. godbolt를 사용하는 동안,-O3
다음을 생산한다(실시간 참조).
movl $0, B(%rip) #, B
addl $1, %eax #, D.1624
왜?
컴파일러가 재주문하는 이유는?이 기사는 아키텍처의 복잡성 때문에 프로세서가 그렇게 하는 것과 정확히 같은 이유라고 설명한다.
처음부터 언급했듯이, 컴파일러는 프로세서가 하는 것과 같은 이유로 메모리 상호작용의 순서를 수정한다. 즉 성능 최적화.이러한 최적화는 현대 CPU 복잡성의 직접적인 결과물이다.
표준
초안 C++ 표준에서 이 내용은 섹션에 설명되어 있다.1.9
프로그램 실행(내 미래 지향적인 내용 강조):
이 국제 표준의 의미적 설명은 매개변수화된 비결정론적 추상적 기계를 정의한다.이 국제 표준은 구현을 준수하는 구조에는 어떤 요구사항도 두지 않는다.특히 추상적인 기계의 구조를 모방하거나 모방할 필요는 없다.오히려 아래에 설명한 추상적인 기계의 관찰 가능한 동작을 에뮬레이션(만)하기 위해서는 적절한 구현이 필요하다.5
주십시요.5
이를 AS-If 규칙이라고도 한다.
프로그램의 관찰 가능한 행동에서 결정될 수 있는 한, 구현은 이 국제 표준의 어떤 요건도 무시하는 것이 자유롭기 때문에 이 조항을 "있는 그대로" 규칙이라고도 한다.예를 들어, 실제 구현은 그 가치를 사용하지 않고 프로그램의 관찰 가능한 동작에 영향을 미치는 부작용이 발생하지 않는다고 추론할 수 있다면 표현식의 일부를 평가할 필요가 없다.
초안 C99와 초안 C11 표준은 이것을 섹션에 포함한다.5.1.2.3
C 표준에서도 as-if 규칙이라고 불리는 것을 확인하기 위해 지수로 가야 하지만 프로그램 실행:
as-if 규칙, 5.1.2.3
잠금 해제 고려 사항 업데이트
잠금 없는 프로그래밍에 대한 소개 기사는 이 주제를 잘 다루며, 잠금 없는 공유 해시 테이블 구현에 관한 OP에 대해서는 이 섹션이 아마도 가장 관련이 있을 것이다.
메모리 순서 지정
흐름도에서 알 수 있듯이 멀티코어(또는 모든 대칭 멀티프로세서)에 대해 잠금 없는 프로그래밍을 수행할 때마다 환경이 순차적인 일관성을 보장하지 않으므로 메모리 재주문을 방지할 수 있는 방법을 고려해야 한다.
오늘날의 아키텍처에서 올바른 메모리 순서를 적용하는 도구는 일반적으로 세 가지 범주로 분류되며, 이는 컴파일러 재순서와 프로세서 재순서를 모두 방지한다.
의미론 획득은 프로그램 순서에 따르는 작업의 메모리 재순서를 방지하고, 릴리즈 의미론은 그 이전의 작업의 메모리 재순서를 방지한다.이러한 의미론들은 특히 생산자/소비자 관계가 있는 경우에 적합하다. 한 줄기는 어떤 정보를 발표하고 다른 줄기는 그것을 읽는 것이다.나 또한 이 일에 대해 향후 포스트에서 더 이야기하겠다.
지침의 종속성이 없는 경우, 최종 결과에 영향을 미치지 않는 경우에도 지침이 순서 없이 실행될 수 있다.더 높은 최적화 수준에서 컴파일된 코드를 디버깅하는 동안 이를 관찰할 수 있다.
A = a; 및 B = b;는 데이터 의존성 측면에서 독립적이기 때문에 이 문제는 문제가 되지 않는다.후속 지침의 입력에 영향을 미치는 이전 지침의 출력/발표가 있는 경우, 주문 사항(그렇지 않은 경우)이것은 엄밀히 말하면 순차적 실행이다.
필자는 C++ 표준에 의해 작동하기 위해 이것이 필요하다고 읽었지만, 만약 당신이 이것을 멀티스레딩 제어에 사용하려고 한다면, 여기 레지스터가 올바른 순서로 메모리에 기록되도록 보장할 수 있는 것은 아무것도 없기 때문에, 그것은 그 맥락에서 작동하지 않는다.
당신의 편집에서 알 수 있듯이, 당신은 그것이 작동하지 않을 곳에 정확히 그것을 사용하려고 한다.
이렇게 하면 다음과 같은 것이 흥미로울 수 있다.
{ A=a, B=b; /*etc*/ }
세미콜론 대신 쉼표를 기록해 두십시오.
그러면 콤마 연산자의 피연산자는 항상 왼쪽에서 오른쪽으로 평가되기 때문에 C++ 사양과 모든 확인 컴파일러는 실행 순서를 보장해야 한다.이것은 실제로 최적기가 당신의 스레드 동기화를 재주문하는 것을 방지하는데 사용될 수 있다.쉼표는 사실상 재주문이 허용되지 않는 장벽이 된다.
참조URL: https://stackoverflow.com/questions/25847349/for-a-a-b-b-will-a-a-be-strictly-executed-before-b-b
'programing' 카테고리의 다른 글
vue CLI 프로젝트에 vuex를 설치하는 방법 (0) | 2022.05.18 |
---|---|
왜 "while (!fe (file) )"가 항상 틀리는가? (0) | 2022.05.18 |
왜 이렇게 빨갛지?IntelliJ는 모든 선언/방법들이 발견/해결될 수 없다고 생각하는 것 같다. (0) | 2022.05.18 |
캐치 블록 내부에 던져진 예외 - 다시 잡힐까? (0) | 2022.05.18 |
vue 구성 요소의 데이터를 편집할 때 입력 유형 날짜의 값을 표시하는 방법은? (0) | 2022.05.18 |