ONE QUESTION AT A TIME.

천천히 생각하고, 분명하게.

TODAY’S QUESTION322 / 1500
성능난이도 하5분 답변 목표 · 보강본

두 스레드가 서로 다른 카운터만 수정하는데 함께 실행하면 느려집니다. 데이터 경합이 없어도 이런 일이 생길 수 있으며, 어떻게 확인하고 줄이나요?

잠깐, 답변을 보기 전에
먼저 내 말로 설명해 볼까요?

답변과 점검 포인트 펼치기답변 접기
01

구두 답변

정의를 외우기보다, 원리와 조건을 연결해 설명해 보세요.

서로 다른 스레드가 서로 다른 변수만 수정해도 느려질 수 있습니다. CPU cache coherence는 변수 하나가 아니라 cache line이라는 묶음 단위로 쓰기 소유권을 관리합니다. 두 코어의 카운터가 같은 cache line에 있으면 한 코어가 값을 쓸 때 다른 코어의 line이 무효화되고, 다음 쓰기에서 line이 다시 이동합니다. 논리적으로는 공유하지 않지만 물리적으로 같은 line을 공유해 생기는 비용을 거짓 공유(false sharing)이라고 합니다.

실제 경합과 나눠 봅니다

작업자별 카운터 배열이 연속으로 배치된 경우 각자가 자기 인덱스만 갱신해도 작업자 수가 늘수록 coherence 트래픽이 증가할 수 있습니다. 반면 하나의 전역 카운터를 모두 증가시키는 것은 같은 메모리 위치를 쓰는 true sharing입니다. 원자 증가라면 성능 경합은 있어도 그 접근 자체는 C++ data race가 아니며, 비원자 증가라면 정확성 문제도 생깁니다. 둘은 관찰되는 처리량 저하가 비슷할 수 있지만 수정 방법이 다릅니다. 갱신을 제거하거나 작업자별 저장 공간을 넓게 분리한 대조 실험, 동일 line에 배치된 필드의 주소 확인, coherence·cache miss 성능 카운터로 거짓 공유를 검증하겠습니다.

원인이 맞다면 자주 쓰는 독립 상태를 cache line 경계에 맞춰 분리하거나, 작업자 로컬에서 누적한 뒤 주기적으로 한 번만 합칩니다. 전자는 메모리와 cache footprint를 늘리고, 후자는 통계의 최신성과 집계 지연을 만듭니다. 모든 구조체에 패딩을 넣으면 읽기 중심 필드의 지역성이 나빠질 수 있고, NUMA 환경에서는 line 분리 뒤에도 원격 접근 비용이 남습니다. 따라서 갱신 빈도와 데이터 크기가 큰 필드에만 선택적으로 적용하겠습니다.

부하가 실제 사용을 닮아야 합니다

단일 스레드, 작업자 수 증가, 카운터 갱신 제거, 구조체 패딩, 로컬 집계 구성을 같은 데이터와 반복 횟수로 비교합니다. 처리량·CPU 시간·cache miss·coherence 이벤트·메모리 증가·집계 최신성을 함께 기록합니다. 단순히 스레드 수가 늘어 느려졌다는 이유만으로 거짓 공유를 확정하지 않고 락 경합, 메모리 대역폭, 작업 불균형과의 대조 결과를 확인하겠습니다.

전역 카운터 하나를 여러 스레드가 증가시키는 경우는 같은 메모리 위치를 공유하는 true sharing과 원자 연산 경쟁입니다. atomic 증가라면 C++ data race는 아니지만 coherence와 serialization 비용으로 확장성이 떨어질 수 있습니다. 그 카운터가 비원자라면 data race와 정의되지 않은 동작까지 추가됩니다. 반면 worker별 카운터가 같은 cache line에 놓인 경우는 논리적으로 다른 값을 쓰는 false sharing입니다.

주소를 확인하고 전역 atomic 제거, worker별 line 분리, 로컬 누적 후 합산을 각각 대조하겠습니다. 패딩은 메모리·TLB·LLC footprint를 늘리고, 로컬 누적은 통계 최신성과 합산 동기화를 바꿉니다. 패딩 후에도 true sharing이나 메모리 대역폭이 병목일 수 있으므로 coherence 이벤트·cache miss·CPU 시간·메모리 증가와 정확성 테스트를 함께 보겠습니다.

전역 atomic counter를 여러 스레드가 증가시키는 것은 true sharing과 원자 연산의 직렬화 문제입니다. atomic이므로 그 자체로 C++ data race는 아니지만 cache line과 메모리 위치를 경쟁합니다. 비원자 counter라면 data race와 정의되지 않은 동작이 추가됩니다. 이 구분 뒤에야 worker별 패딩을 평가하겠습니다.

구조체 패딩은 cache line 경계를 가정하므로 플랫폼·정렬 변화에 민감합니다. 패딩으로 line ping-pong을 줄여도 메모리 footprint와 TLB·LLC 압박이 커질 수 있고, 로컬 누적은 합산 시점의 동기화와 최신성을 바꿉니다. 전역 atomic, worker별 패딩, 로컬 집계와 비원자 오류를 분리한 대조 실험으로 처방을 검증하겠습니다.

02

득점 포인트

이해가 드러나는 지점을 확인해 보세요.

  • cache line 단위 coherence와 논리적 데이터 공유를 구분한다.
  • 대조 실험으로 거짓 공유와 실제 경합을 가른다.
  • 패딩·로컬 집계의 메모리·최신성·지역성 비용을 설명한다.
03

감점 포인트

놓치기 쉬운 조건과 오해를 짚어 보세요.

  • 락이 없으면 코어 간 캐시 경합도 없다고 말한다.
  • 모든 스레드 확장 저하를 거짓 공유로 단정한다.
  • 모든 필드에 패딩을 넣으면 항상 최적이라고 말한다.
04

더 파고들 거리

하나 더 질문하며 이해의 범위를 넓혀 보세요.

득점·감점 포인트는 학습용 자기 점검 기준이며, 특정 회사의 실제 채점표가 아닙니다.

원리부터 공부하고 싶다면

답변을 보충하는 개념·예제·슈도코드를 읽어 보세요.

← 전체 질문 보기