TECH 으로 돌아가기
TECH HACKER NEWS 오늘 8분 읽기 29 READS

'병렬 프로그래밍은 어렵다' 교과서가 알려주는 동시성의 진짜 난이도

'병렬 프로그래밍은 어렵다' 교과서가 알려주는 동시성의 진짜 난이도
SOURCE IMAGE · HACKER NEWS

멀티코어가 당연해진 시대에도 병렬 프로그래밍은 여전히 대다수 개발자에게 뮤텍스와 메시지 큐 정도로 요약되는 영역이다. 그러나 락(lock)에 의존하지 않는 고성능 동시성 코드로 내려가면 이야기가 완전히 달라진다. 리눅스 커널의 RCU 동기화 메커니즘을 만든 폴 E. 매케니(Paul E. McKenney)가 쓴 무료 온라인 교과서 『Is Parallel Programming Hard, And, If So, What Can You Do About It?』에 대한 한 개발자의 독후감은, 분산 시스템과 형식 검증에 능숙했던 사람조차 단일 CPU 위의 동시성 앞에서는 초심자가 된다는 점을 솔직하게 보여준다. 이 글은 그 리뷰를 실마리 삼아, 실무 개발자가 이 책에서 무엇을 얻을 수 있고 어디서 막히는지를 정리한 것이다.

하드웨어를 모르면 동시성도 없다

책의 본격적인 시작점은 3장 '하드웨어와 그 습성'이다. 현대 CPU가 왜 빠르고 무엇 때문에 느려지는지를 다루는데, 특히 3.2.1절에서 CPU 코어가 캐시에 없는 메모리 주소에 값을 쓰려 할 때 내부적으로 무슨 일이 벌어지는지를 단계별로 따라간다. 리뷰어가 지적한 가장 큰 아쉬움은 캐시 일관성의 핵심인 MESI 프로토콜이 본문이 아니라 부록에만 등장한다는 점이다. 그는 이 절을 이해하려고 별도로 MESI를 검색해 공부한 뒤에야 책의 나머지 내용이 훨씬 선명해졌다고 말한다. 실무자에게 주는 교훈은 분명하다. 캐시가 동시 읽기·쓰기를 어떻게 중재하는지에 대한 직관이 없으면, 그 위에 쌓인 동시성 논의는 공중에 뜬 이야기가 된다.

MESI를 이해하면 흔한 오해 하나가 깨진다. x86 CPU는 메모리에 직접 쓰지 않고 우선 자기 캐시에만 쓴 뒤 나중에 메모리로 흘려보낸다. 그리고 어떤 코어가 특정 주소에 쓰려면 그 주소를 담은 캐시라인의 배타적 소유권을 가져야 한다. 다른 코어가 같은 주소에 동시에 쓰려 하면 소유권이 넘어올 때까지 기다려야 하므로, 여러 코어가 한 주소에 문자 그대로 '동시에' 쓰는 일은 실제로 일어나지 않는다. 다만 쓰려는 데이터가 두 캐시라인에 걸쳐 있으면 쓰기가 찢어지는(torn) 현상은 생길 수 있다. 물리적으로 병렬인 이벤트를 다룰 때는 이런 하드웨어 수준의 사실이 곧 정확성의 전제 조건이 된다.

컴파일러와 CPU라는 이중의 함정

4장 '연장통(Tools of the Trade)'은 리뷰어가 '머리가 어질어질했다'고 표현한 대목이다. 충분한 주의 없이 병렬 코드를 짜면 컴파일러가 상상을 초월하는 최적화를 시도해 코드가 엉뚱하게 동작한다. 4.3.4.1절 '공유 변수의 장난질'은 로드 찢김·스토어 찢김, 로드 융합·스토어 융합, 코드 재배치, 없던 로드·스토어를 만들어내는 변환, 스토어를 로드로 바꾸는 변환, 죽은 코드 제거 같은 사례를 열거한다. 이 관문을 통과해도 이번엔 CPU가 실행 시점에 또 다른 재배치를 가한다. 컴파일러와 하드웨어라는 이중의 함정 때문에, 익숙한 뮤텍스나 메시지 패싱을 벗어난 순간 병렬 코드를 '머릿속으로' 추론하기가 급격히 어려워진다는 것이 핵심이다.

한계도 뚜렷하다. 이 장은 철저히 리눅스 커널 맥락에 맞춰져 있어 C11과 C++11이 std::memory_order 등으로 병렬 프로그래밍을 언어 표준 차원에서 형식화한 성과는 4.2.6, 4.2.7절에서 짧게만 다뤄진다. 리뷰어는 이 책을 통해 ACCESS_ONCE()나 WRITE_ONCE() 매크로가 대상을 volatile* 로 캐스팅해 컴파일러의 장난을 막는다는 정도의 감만 얻었을 뿐, 무해한 데이터 경쟁을 오류로 볼 것인가 같은 흥미로운 역사적 논쟁은 건너뛰었다고 지적한다. 응용 개발자라면 커널 관용구와 표준 라이브러리 사이의 간극을 스스로 메워야 한다는 뜻이다.

카운터 하나로 배우는 성능의 경제학

책의 간판 격인 5장 '카운팅'은 여러 스레드가 하나의 카운터를 증가시키는, 겉보기엔 사소한 문제를 열 가지 안팎의 방식으로 풀어낸다. 가장 뻔한 정답인 원자적 증가 명령이 실제로는 처참하게 느리다는 것을 초반에 보여주는데, 여기서 앞서 익힌 MESI 지식이 왜 느린지를 설명해 준다. 캐시라인 소유권이 코어 사이를 계속 오가야 하기 때문이다. 리뷰어가 특히 좋아한 것은 스레드별 통계 카운터를 배열로 두는 방식으로, 분산 시스템의 충돌 없는 복제 데이터타입(CRDT)과 닮았다. 동시에 이 예제는 거짓 공유(false sharing)의 성능 영향을 가르치는 좋은 소재이기도 하다. 스레드별 카운터를 한 배열에 촘촘히 붙여 놓으면 서로 다른 코어가 같은 캐시라인을 두고 다투게 되어 오히려 느려지는 것이다.

그 이후 장들은 소유권·분할·지연 처리처럼 분산 시스템 지식으로 어느 정도 옮겨 읽을 수 있는 개념적 내용이 많다. 형식 검증 장은 Promela와 Spin을 쓰고, 검증 장의 11.6.4절 '하이젠버그 사냥'은 재현하기 어려운 동시성 버그를 다룬다. 다만 정작 락-프리 프로그래밍은 14장 '고급 동기화'에 가서야, 메모리 순서(memory ordering)는 15장에 가서야 본격적으로 등장한다. 락-프리와 메모리 모델을 빠르게 파고들고 싶은 독자에게는 이 배치가 답답하게 느껴질 수 있고, 실제로 리뷰어도 그 지점에서 락-프리 자료구조를 전문으로 다루는 다른 책으로 옮겨갔다.

결국 이 책의 가치는 완결된 매뉴얼이라기보다 동시성의 지형도를 그려주고 더 깊이 파고들 의욕을 심어주는 데 있다. 앞의 다섯 장만 정독해도 x86에서 정렬된 로드·스토어가 원자적이라는 사실, 릴리스-컨슘 순서 형식화의 실패, DEC 알파의 유별나게 약한 메모리 모델, 결정적 시뮬레이션 테스트가 아직 락-프리 알고리즘을 제대로 검증하지 못한다는 한계 같은 화제가 줄줄이 딸려 나온다. 한국의 실무자에게 이 리뷰가 남기는 메시지는 실용적이다. 고성능 동시성은 라이브러리 API 사용법이 아니라 캐시·컴파일러·CPU가 벌이는 일을 이해하는 문제이며, 그 출발점으로 무료로 열려 있는 이 교과서의 초반부는 충분히 투자할 가치가 있다는 것이다.

SOURCE · HACKER NEWS
원문 전체 보기 → https://ahelwer.ca/post/2026-09-21-concurrency-textbook/
SHARE
NEXT · CHOOSE

변화를 읽었다면,
내가 만들 수익 구조를 고릅니다.

정보를 더 모으는 데서 멈추지 않고, 광고·외주·판매·중개·구독 중 내 상황에 맞는 출발점을 정해보세요.

21가지 수익 구조 살펴보기 →
처리 중...