“강하게 전에 일어난다”는 무슨 뜻입니까?


9

C ++ 초안 표준에서는 "강하게 발생합니다"라는 문구가 여러 번 사용되었습니다.

예 : 종료 [basic.start.term] / 5

std :: atexit ([support.start.term] 참조)를 호출하기 전에 정적 저장 기간이있는 객체의 초기화 완료가 강력하게 발생하면 함수 호출이 std :: atexit에 전달되었습니다. 객체의 소멸자를 호출하기 전에 시퀀스됩니다. 정적 저장 시간으로 객체의 초기화가 완료되기 전에 std :: atexit에 대한 호출이 강하게 발생하면 함수에 대한 호출이 std :: atexit에 전달되기 전에 객체의 소멸자에 대한 호출이 순서화됩니다. . std :: atexit에 대한 호출이 std :: atexit에 대한 다른 호출 전에 강력하게 발생하는 경우, 두 번째 std :: atexit 호출에 전달 된 함수에 대한 호출은 함수에 대한 호출이 첫 std :: atexit 호출.

그리고 데이터 레이스 [intro.races] / 12 에서 정의

다음 중 하나에 해당하면 평가 A가 평가 D보다 먼저 발생합니다.

(12.1) A가 D보다 먼저 시퀀싱되거나

(12.2) A는 D와 동기화되고 A와 D는 순차적으로 일관된 원자 연산 ([atomics.order])이거나

(12.3) 평가 B와 C가있어서 A가 B보다 먼저 시퀀싱되고, B가 단순히 C보다 먼저 일어나고, C가 D보다 먼저 시퀀싱된다.

(12.4) A가 B보다 먼저 일어나고 B가 D보다 먼저 일어나도록 평가 B가있다.

[참고 : 비공식적으로, A가 B보다 먼저 발생하면 모든 상황에서 A가 B보다 먼저 평가되는 것으로 보입니다. 제외 작업이 제외되기 전에 발생합니다. — 끝 참고]

"강력하게 전에"일어난 이유는 무엇입니까? 직관적으로 "이전에 일어난 일"과의 차이점과 관계는 무엇입니까?

노트에서 "A는 모든 상황에서 B보다 먼저 평가되는 것"은 무엇을 의미합니까?

(참고 :이 질문에 대한 동기 부여는 피터 코르의 의견은 아래에 이 대답 .)

추가 표준 인용문 (Peter Cordes 덕분에)

순서와 일관성 [atomics.order] / 4

펜스를 포함하여 모든 memory_order :: seq_cst 작업에는 다음과 같은 제약 조건을 충족하는 단일 총 차수 S가 있습니다. 먼저 A와 B가 memory_order :: seq_cst 연산이고 A가 B보다 먼저 발생하는 경우 A는 S에서 B보다 우선합니다. 둘째, 객체 M의 모든 원자 연산 A와 B에 대해 A는 일관성 순서로 정렬됩니다. B 이전에 S는 다음 4 가지 조건을 만족해야합니다.

(4.1) A와 B가 모두 memory_order :: seq_cst 연산이면 A는 S에서 B보다 우선합니다. 과

(4.2) A가 memory_order :: seq_cst 연산이고 B가 memory_order :: sseq_cst 펜스 Y 전에 발생하면 A가 S에서 Y보다 우선합니다. 과

(4.3) memory_order :: seq_cst 펜스 X가 A보다 먼저 발생하고 B가 memory_order :: sseq_cst 연산이면 X가 S에서 B보다 우선합니다. 과

(4.4) memory_order :: seq_cst 펜스 X가 A보다 먼저 발생하고 B가 memory_order :: sseq_cst 펜스 Y보다 먼저 발생하면 S에서 X가 Y보다 먼저 나옵니다.


1
현재의 초안 표준은 참조를 위해 적용하는 규칙에 대한 조건으로 "A 강하게 B 전에 발생" seq_cst에, : 아토 31.4 주문과 일관성 4 . 이는 C ++ 17 n4659 표준 에 해당하지 않습니다 . 여기서 32.4-3영향을받는 모든 위치에 대한 "이전에 발생하는"순서 및 수정 순서와 일치하는 seq_cst ops의 단일 전체 순서가 존재 함을 정의합니다 . "강하게"는 이후 초안에 추가되었습니다.
Peter Cordes

2
@PeterCordes 소비를 제외한 의견은 HB가 "모든 맥락에서"/ "강하다"고 기능 포인터에 대한 호출에 대해 말하는 것은 죽은 선물입니다. 멀티 스레드 프로그램 atexit()이 하나의 스레드와 exit()다른 스레드 exit()를 호출하는 경우, 동일한 스레드가 호출 한 경우와 결과가 다르기 때문에 이니셜 라이저가 소비 기반 종속성 만 수행하는 것만으로는 충분하지 않습니다 . 나의 오래된 대답 은이 차이에 관한 것입니다.
Idonotexist Idonotexist


@IwillnotexistIdonotexist MT 프로그램을 종료 할 수 있습니까? 기본적으로 깨진 아이디어가 아닙니까?
curiousguy

1
@curiousguy의 목적입니다 exit(). 모든 스레드는 종료하여 전체 프로그램을 종료하거나 기본 스레드는 return-ing으로 종료 할 수 있습니다 . atexit()처리기를 호출 하고 수행중인 모든 스레드의 죽음을 초래합니다.
Iwillnotexist Idonotexist

답변:


5

"강력하게 전에"일어난 이유는 무엇입니까? 직관적으로 "이전에 일어난 일"과의 차이점과 관계는 무엇입니까?

"간단히 발생하기"전에 자신을 보호하십시오! 이 cppref의 현재 스냅 샷을 살펴보십시오 https://en.cppreference.com/w/cpp/atomic/memory_order

여기에 이미지 설명을 입력하십시오

C ++ 20에 "간단히 발생하기"가 추가 된 것 같습니다.

단순히 이전에 일어난 일

스레드에 관계없이 다음 중 하나에 해당하는 경우 평가 A는 평가 B 이전에 간단히 발생합니다.

1) A가 B보다 먼저 순서화 됨

2) A는 B와 동기화

3) A는 X 이전에 발생하고 X는 B 이전에 발생합니다.

참고 : 소비 작업이 없으면 단순히 이전과 이전과의 관계가 동일합니다.

따라서 Simply-HB와 HB는 소비 작업을 처리하는 방법을 제외하고 동일합니다. HB 참조

일어나기 전에

스레드에 관계없이 다음 중 하나에 해당하는 경우 평가 A는 평가 B 이전에 발생합니다.

1) A가 B보다 먼저 순서화 됨

2) B 이전에 스레드 간 발생

구현은 필요한 경우 추가 동기화를 도입하여 비 정기적 관계가 비 주기적임을 보장하는 데 필요합니다 (소비 작업이 관련된 경우에만 필요함, Batty et al 참조).

소비면에서 어떻게 다른가요? Inter-Thread-HB 참조

스레드 간 발생

다음 중 하나라도 해당되는 경우 평가 B 전에 평가 A 스레드 간이 발생합니다.

1) A는 B와 동기화

2) A는 B보다 우선 순위가 높습니다

삼) ...

...

종속성 순서가 지정된 작업 (즉, 릴리스 / 소비 사용)은 HB이지만 Simply-HB 일 필요는 없습니다.

소비는 획득보다 더 편안합니다. 올바르게 이해하면 HB가 Simply-HB보다 더 편안합니다.

강하게 일어나기 전에

스레드에 관계없이 다음 중 하나에 해당하는 경우 평가 A는 평가 B 이전에 강력하게 발생합니다.

1) A가 B보다 먼저 순서화 됨

2) A는 B와 동기화되며 A와 B는 순차적으로 일관된 원자 연산입니다.

3) A는 X 이전에 시퀀싱되고 X는 Y 이전에 간단하게 발생하며 Y는 B 이전에 시퀀싱됩니다

4) A는 X보다 먼저 발생하고 X는 B보다 먼저 발생합니다.

참고 : 비공식적으로, A가 B보다 강력하게 발생하면 모든 컨텍스트에서 A가 B보다 먼저 평가되는 것으로 보입니다.

참고 : 소비 작업을 제외하기 전에 강력하게 발생합니다.

따라서 릴리스 / 소비 작업은 HB가 될 수 없습니다.

릴리스 / 취득은 HB 및 Simply-HB (릴리스 / 취득이 동기화되어 있기 때문에) 일 수 있지만 반드시 Strongly-HB 일 필요는 없습니다. Strongly-HB는 A가 B와 동기화해야하며 순차적으로 일관된 작업이어야한다고 구체적으로 설명하기 때문입니다.

                            Is happens-before guaranteed?

                        HB             Simply-HB          Strongly-HB

relaxed                 no                 no                 no
release/consume        yes                 no                 no      
release/acquire        yes                yes                 no
S.C.                   yes                yes                yes

노트에서 "A는 모든 상황에서 B보다 먼저 평가되는 것"은 무엇을 의미합니까?

모든 컨텍스트 : 모든 스레드 / 모든 CPU는 동일한 순서를 봅니다 (또는 "동의합니다"). 이는 모든 변수의 전체적인 전체 수정 순서 인 순차 일관성을 보장합니다. 획득 / 릴리즈 체인은 체인에 참여하는 스레드에 대해 인식 된 수정 순서 만 보장합니다. 체인 외부의 스레드는 이론적으로 다른 순서를 볼 수 있습니다.

Strongly-HB와 Simply-HB가 소개 된 이유를 모르겠습니다. 어쩌면 소비 주위를 운영하는 방법을 명확히하는 데 도움이 될 수 있습니까? Strongly-HB는 좋은 특성을 가지고 있습니다. 하나의 스레드가 B보다 A를 강력하게 요구하면 모든 스레드가 동일한 것을 관찰한다는 것을 알고 있습니다.

소비의 역사 :

Paul E. McKenney는 C 및 C ++ 표준을 준수해야합니다. 소비는 포인터 할당과 그것이 가리키는 메모리 사이의 순서를 보장합니다. DEC 알파 때문에 발명되었습니다. DEC Alpha는 추론 적으로 포인터를 역 참조 할 수 있으므로이를 방지하기위한 메모리 펜스도 있습니다. DEC Alpha는 더 이상 만들어지지 않으며 오늘날에는이 동작을 수행하는 프로세서가 없습니다. 소비는 매우 이완되도록 고안되었습니다.


1
맙소사. 나는 그 질문을 거의 후회한다. 나는 태클 다시 가고 싶지 쉬운 경우 템플릿의 멤버에서 기본 클래스의 이름을 조회 외모, 그리고 때를 C ++ 반복자 무효화의 규칙 같은 문제, 인수 종속 이름 조회, 템플릿 사용자 정의 변환 연산자 템플릿 인수 공제를, 객체 생성을 시작할 때 가상 기반으로 변환 할 수 있습니다.
curiousguy

다시 : 소비. 소비 주문의 운명이 DEC Alpha의 운명과 관련이 있으며 특정 아치 외부의 가치가 없다고 주장합니까?
curiousguy

1
그건 좋은 질문이야. 더 자세히 살펴보면 소비가 이론적으로 ARM 및 PowerPC와 같이 약하게 정렬 된 아치의 성능을 향상시킬 수있는 것처럼 들립니다. 좀 더 살펴볼 시간을주세요.
Humphrey Winnebago

1
알파 이외의 약한 순서의 ISA로 인해 소비가 존재한다고 말하고 싶습니다 . Alpha asm에서 유일한 옵션은 종속성 순서가 아닌 완화 및 획득 (및 seq-cst)입니다. mo_consume실제 CPU에서 데이터 종속성 순서를 활용하고 분기 예측을 통해 데이터 종속성을 깰 수 없도록 공식화합니다. 예를 들어 특정 포인터 값이 공통 일 것으로 예상되는 이유가 있으면 int *p = load(); tmp = *p;컴파일러 소개로 인해 깨질 수 있습니다 if(p==known_address) tmp = *known_address; else tmp=*p;. 그것은 편안하지만 소비하지 않는 것이 합법적입니다.
Peter Cordes

@PeterCordes right ... 순서가 약한 아치는 획득을 위해 메모리 장벽을 방출해야하지만 이론적으로는 소비하지 않습니다. 알파가 존재하지 않았다면 여전히 소비했을 것이라고 생각하는 것 같습니다. 또한 기본적으로 소비는 멋진 (또는 "표준") 컴파일러 장벽이라고 말합니다.
Humphrey Winnebago
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.