잘못된 공유가 여전히 비 원자성에 영향을 미치지 만 원 자성보다는 훨씬 적은 이유는 무엇입니까?

알렉스 구 테니 예프

잘못된 공유 존재 를 증명하는 다음 예를 고려하십시오 .

using type = std::atomic<std::int64_t>;

struct alignas(128) shared_t
{
  type  a;
  type  b;
} sh;

struct not_shared_t
{
  alignas(128) type a;
  alignas(128) type b;
} not_sh;

한 스레드 a는 1 단계 씩 증가 하고 다른 스레드는 b. lock xadd결과가 사용되지 않더라도 증분 은 MSVC로 컴파일됩니다 .

구성 내용 ab분리되어, 몇 초에 축적되어있는 값에 대한 약 10 배 이상 not_shared_t보다 shared_t.

지금까지 예상 된 결과 : 별도의 캐시 라인이 L1d 캐시에서 핫 상태를 유지하고, lock xadd처리량에 병목 현상이 증가하고 , 잘못된 공유는 캐시 라인을 핑퐁하는 성능 재앙입니다. (편집자 주 : 최신 MSVC 버전 lock inc은 최적화가 활성화 된 경우 사용합니다. 이로 인해 경합과 비경쟁 간의 차이가 벌어 질 수 있습니다.)


이제 나는 using type = std::atomic<std::int64_t>;평범한std::int64_t

(비원 자적 증분은로 컴파일됩니다 inc QWORD PTR [rcx]. 루프의 원자 적로드는 컴파일러가 루프가 종료 될 때까지 레지스터에 카운터를 유지하는 것을 막습니다.)

에 대한 도달 횟수 not_shared_t는 여전히 for 보다 크지 shared_t만 이제 두 번 미만입니다.

|          type is          | variables are |      a=     |      b=     |
|---------------------------|---------------|-------------|-------------|
| std::atomic<std::int64_t> |    shared     |   59’052’951|   59’052’951|
| std::atomic<std::int64_t> |  not_shared   |  417’814’523|  416’544’755|
|       std::int64_t        |    shared     |  949’827’195|  917’110’420|
|       std::int64_t        |  not_shared   |1’440’054’733|1’439’309’339|

비 원자 사례가 성능면에서 훨씬 더 가까운 이유는 무엇입니까?


다음은 재현 가능한 최소 예제를 완성하기위한 나머지 프로그램입니다. (또한 MSVC를 사용하는 Godbolt 에서 컴파일 / 실행 준비 완료)

std::atomic<bool> start, stop;

void thd(type* var)
{
  while (!start) ;
  while (!stop) (*var)++;
}

int main()
{
  std::thread threads[] = {
     std::thread( thd, &sh.a ),     std::thread( thd, &sh.b ),
     std::thread( thd, &not_sh.a ), std::thread( thd, &not_sh.b ),
  };

  start.store(true);

  std::this_thread::sleep_for(std::chrono::seconds(2));

  stop.store(true);
  for (auto& thd : threads) thd.join();

  std::cout
    << " shared: "    << sh.a     << ' ' << sh.b     << '\n'
    << "not shared: " << not_sh.a << ' ' << not_sh.b << '\n';
}
피터 코 데스

비 원자 메모리 증분은 자체 저장된 값을 다시로드 할 때 저장 전달의 이점을 누릴 수 있습니다. 이는 캐시 라인이 유효하지 않은 경우에도 발생할 수 있습니다. 코어는 상점이 결국 발생한다는 것을 알고 있으며 메모리 순서 지정 규칙을 통해이 코어는 전 세계적으로 표시되기 전에 자체 상점을 볼 수 있습니다.

저장 전달은 원자 적 RMW 증분을 수행하기 위해 캐시 라인에 대한 배타적 액세스를 필요로하는 대신 정지하기 전에 증분의 저장 버퍼 수의 길이를 제공합니다 .

이 코어가 결국 캐시 라인의 소유권을 얻게되면 1 시간에 여러 저장소를 커밋 할 수 있습니다. 이는 메모리 대상 증분으로 생성 된 종속성 체인보다 6 배 더 빠릅니다. 저장 / 재로드 대기 시간 ~ 5 회 + ALU 대기 시간 1 회입니다. 따라서 실행은 비원 자적 경우에서 코어가 소유하는 동안 배수 할 수있는 비율의 1/6로 SB에 새 저장소를 넣는 것 입니다. 이것이 공유 원자와 비공유 원자 사이에 큰 차이가없는 이유입니다.

확실히 메모리 순서 지정 기계가 지워질 것입니다. 그 및 / 또는 SB 가득 참은 잘못된 공유 사례에서 처리량을 낮추는 원인 일 수 있습니다. 하이퍼 형제와 비 하이퍼 형제간에 메모리 위치를 공유하는 생산자-소비자의 대기 시간 및 처리량 비용은 얼마입니까? 에 대한 답변과 의견을 참조하십시오 . 이와 같은 다른 실험을 위해.


A lock inc또는 lock xadd작업 전에 저장소 버퍼를 강제로 비우고 작업의 일부로 L1d 캐시에 대한 커밋을 포함합니다. 이로 인해 스토어 포워딩이 불가능하고 캐시 라인이 Exclusive 또는 Modified MESI 상태에있는 경우에만 발생할 수 있습니다.

관련 :

이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.

침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제

에서 수정
0

몇 마디 만하겠습니다

0리뷰
로그인참여 후 검토

관련 기사

Related 관련 기사

뜨겁다태그

보관