0

Wikipedia에서 다음의 구현 :메모리 장벽

volatile unsigned int produceCount = 0, consumeCount = 0; 
TokenType buffer[BUFFER_SIZE]; 

void producer(void) { 
    while (1) { 
     while (produceCount - consumeCount == BUFFER_SIZE) 
      sched_yield(); // buffer is full 

     buffer[produceCount % BUFFER_SIZE] = produceToken(); 
     // a memory_barrier should go here, see the explanation above 
     ++produceCount; 
    } 
} 

void consumer(void) { 
    while (1) { 
     while (produceCount - consumeCount == 0) 
      sched_yield(); // buffer is empty 

     consumeToken(buffer[consumeCount % BUFFER_SIZE]); 
     // a memory_barrier should go here, the explanation above still applies 
     ++consumeCount; 
    } 
} 

메모리 장벽 버퍼에 액세스하는 라인과 Count를 업데이트 라인 사이에 사용되어야 함을 말한다 변하기 쉬운.

이것은 CPU가 펜스 위의 지시 사항과 함께 지시 사항을 재정렬하는 것을 방지하기 위해 수행됩니다. Count 변수는 버퍼로 인덱싱되기 전에 증가되지 않아야합니다.

울타리를 사용하지 않으면 이러한 종류의 순서 재 지정이 코드의 정확성을 위반하지 않습니까? CPU는 버퍼에 인덱스하기 전에 Count의 증가분을 수행해서는 안됩니다. 명령 reordering 동안 CPU가 데이터 의존성을 처리하지 않습니까?

감사 울타리를 사용하지 않는 경우

+1

@ user3286661 : 이는 메모리 장벽이 잘 정의 된 C++ 위에 코드를 만들 수 없다는 것을 의미합니다. 그것은 답변에 도움이되지 않습니다, 그것은 왜 당신의 질문의 전제 결함이 설명합니다. – MSalters

+0

@ user3286661 우리는 성능에 신경 쓰지 않습니까? 이것은 정확성에 관한 것입니다. (왜냐하면'sched_yield'에서 돌아가는 코드의 성능이 끔찍할 것이기 때문입니다.) 이것은 의사 코어이거나 플랫폼 특정 코드 여야합니다. '휘발성'이 메모리 장벽과 스레드와 상호 작용하는 방식에 대한 이식성 규칙은 없습니다. –

+0

질문은 기억 장벽의 개념에 관한 것입니다. 우리는 성과에 신경 쓰지 않습니다. – user3286661

답변

2

귀하의 질문은 "을 증분하고 코드 동작을 변경하지 않고도 buffer을 재주문 할 수 있습니까?".

코드 tansformation 다음 고려 : 쓰기 전에, 프로그램의 상태가 동일 어떻게 읽을 휘발성 변수에서 하나 개의 읽기, 휘발성 한 쓰기 :

int count1 = produceCount++; 
buffer[count1 % BUFFER_SIZE] = produceToken(); 

공지 사항이 코드는 정확히 하나의 원본으로 작동합니다. 그러나 다른 스레드는 produceCount 증분 및 buffer 수정 순서와 관련하여 다른 그림을 보게됩니다.

컴파일러와 CPU는 메모리 펜스가 없으면 해당 변환을 수행 할 수 있으므로이 두 작업을 올바른 순서로 수행해야합니다.

3

는 재정렬 이런 종류의 코드의 정확성을 위반하지 않습니다? CPU는 버퍼에 색인하기 전에 Count의 증가를 수행하면 안됩니다. 명령 reordering 동안 CPU가 데이터 의존성을 처리하지 않습니까?

좋은 질문입니다.

C++에서 어떤 형태의 메모리 배리어 (원자, 뮤텍스 등)가 사용되지 않는 한 컴파일러는 코드가 단일 스레드라고 가정합니다. 이 경우, if-if 규칙은 컴파일러가 좋아하는 코드를 내보내고, 관찰 가능한 효과는 마치 코드가 순차적으로 실행되었다는 것을 전제로합니다.

주석에서 설명한 바와 같이

, volatile 반드시 단순히 변수 (이 같은가 다른 스레드에 의해 수정되지 이다) 액세스 사이에서 변경 될 수 있음을 구현 정의 힌트 받아이 변경되지 않는다.

메모리 장벽이없는 멀티 스레드 코드를 작성하는 경우 컴파일러가 다른 스레드를 사용해서는 안되기 때문에 다른 스레드에서 한 스레드의 변수 변경 사항을 확인할 수 있습니다 똑같은 기억을 만지십시오.

실제로 관찰 할 것은 정의되지 않은 동작입니다.

+0

그리고 메모리 울타리의 위치는 어떻습니까? 왜 그 두 진술 사이에 무엇입니까? – user3286661

+0

@ user3286661 메모리 펜스가 있으면 현재 스레드뿐만 아니라 다른 스레드가 관찰 한 펜스를 통해 메모리 쓰기 순서를 변경할 수 없습니다 *. 이것이 중요한 부분입니다. 메모리 업데이트를 스레드 간 신호로 사용할 수 있습니다. –

2

울타리를 사용하지 않으면 이러한 종류의 순서 재 지정이 코드의 정확성을 위반하지 않습니까?

아니요. 그 차이를 알 수있는 이식 가능한 코드를 만들 수 있습니까?

CPU는 버퍼에 색인화되기 전에 Count 증가를 수행하면 안됩니다. 명령 reordering 동안 CPU가 데이터 의존성을 처리하지 않습니까?

왜 안 되나요? 발생한 비용에 대한 보상은 무엇입니까? 쓰기 결합 및 추측 적 인출과 같은 것들은 거대한 최적화이며이를 사용 불가능하게하는 것은 비 시발점입니다.

volatile만으로 해결해야한다고 생각하면 간단하지 않습니다. volatile 키워드에는 C 또는 C++에서 정의 된 스레드 동기화 의미가 없습니다. 일부 플랫폼에서 작동 할 수도 있고 다른 플랫폼에서 작동하지 않을 수도 있습니다. Java에서 volatile은 스레드 동기화 의미를 정의했지만 비 휘발성 항목에 대한 액세스 순서는 제공하지 않습니다.

그러나 메모리 장벽에는 잘 정의 된 스레드 동기화 의미가 있습니다. 데이터를보기 전에 어떤 스레드도 데이터를 사용할 수 있는지 확인할 수 없도록해야합니다. 또한 데이터를 덮어 쓸 수있는 것으로 표시하는 스레드가 해당 데이터로 끝나기 전에 스레드가 표시되지 않도록해야합니다.