12. 확장성의 한계: 암달의 법칙·구스타프슨 법칙과 경합
난이도 중내 답안
모범답안
해설 — 확장성의 한계: 암달·구스타프슨과 경합
난이도: 중상
1. 암달의 법칙
직렬 비율 s, 병렬 비율 1 - s, 코어 N 개일 때 속도향상:
Speedup(N) = 1 / ( s + (1 - s)/N )
병렬 부분은 N 으로 나뉘지만 직렬 부분 s 는 그대로 남는다. N → ∞ 면 (1-s)/N → 0 이므로:
Speedup_max = 1 / s
- s = 5% (0.05) → 상한
1 / 0.05 = 20배. 코어가 1000개여도 20배를 못 넘는다. 참고로N=20에서 이미1/(0.05+0.95/20)=10.3배,N=100에서1/(0.05+0.0095)=16.8배— 수확 체감이 가파르다. - 핵심 통찰: 직렬 비율이 천장을 정한다. 병렬화 노력보다 직렬 구간을 줄이는 게 결정적이다.
2. 구스타프슨의 법칙
암달은 문제 크기를 고정하고 "같은 일을 더 빨리"를 본다. 구스타프슨은 시간을 고정하고 "코어가 늘면 같은 시간에 더 큰 일을"을 본다(문제 크기를 코어에 비례해 키움). 스케일드 스피드업:
Speedup(N) = N - s·(N - 1) (s = 직렬 비율)
이 식은 N 에 대해 (직렬 비율 페널티를 뺀) 거의 선형으로 증가한다. 현실적 의미: 대규모 서버에서 코어/장비를 늘리는 목적은 보통 "같은 응답시간으로 더 많은 동접/엔티티/존을 처리"다 — 즉 문제 크기가 자원과 함께 커진다. 이때는 암달의 비관적 천장보다 구스타프슨의 관점이 운영 현실에 가깝다. 단, 직렬 병목(공유 자원) 이 크면 문제를 키워도 그 부분이 경합으로 막혀 구스타프슨의 낙관도 깨진다. 둘은 모순이 아니라 "고정된 일 vs 늘어나는 일" 두 시나리오를 보는 것.
3. 직렬 구간의 정체 (s 를 키우는 것들)
게임서버 틱 루프에서 흔한 직렬 병목:
- 전역 락 하나로 보호되는 월드 상태: 모든 워커가 그 락을 직렬로 통과 → 사실상 단일 코어.
s가 급등. - 단일 작업 큐/단일 송신 스레드: 모든 코어가 한 큐에 push/pop 경쟁(큐 락·캐시 핑퐁). 입출력 경로의 직렬화.
- 순차 커밋/순서 보장 지점: 틱 끝의 일괄 저장, 순서 있는 브로드캐스트 등 "모두 모여 한 줄로" 처리되는 배리어.
- false sharing: 논리적으로 독립인 per-thread 카운터가 같은 캐시라인에 있어 코어 간 무효화 트래픽 → 동기화 안 했는데도 직렬처럼 느려짐.
이들은 코드상 "병렬"이어도 실행이 한 자원에서 줄 서므로
s에 합산된다.
4. 경합과 음의 확장성
코어를 늘렸는데 처리량이 떨어지는 경우:
- 락 경합: 대기·깨우기·스핀 비용이 코어 수에 따라 초선형으로 증가. 임계영역이 짧아도 진입 빈도가 높으면 폭증.
- 캐시 코히어런시 트래픽: 공유 쓰기 라인의 MESI 무효화·전송이 코어 수에 비례해 늘어 메모리 대역을 잡아먹음.
- 컨텍스트 스위칭/오버서브스크립션: 스레드 > 코어면 스위칭·캐시 오염 비용.
- NUMA 원격 접근: 다른 소켓의 메모리 접근 지연으로 추가 코어가 손해.
측정/진단: 코어 수 1,2,4,8,... 로 처리량/지연을 찍어 확장성 곡선을 그린다. 선형선과 갈라지는 지점이 병목 시작점. perf/lock contention 프로파일러로 어느 락·라인에서 시간이 새는지, perf c2c 로 false sharing/원격 캐시 접근을 본다. "스레드를 늘려도 CPU 사용률은 오르는데 처리량은 정체"면 경합 신호.
5. 설계 함의 — 직렬 비율 줄이기
- 샤딩/파티셔닝: 월드를 존/그리드/엔티티 버킷으로 쪼개 락을 분할(각 샤드 독립 처리). 전역 락 → 다수의 무경합 락.
- 락프리/불변 스냅샷: 읽기 핫 데이터는 불변 스냅샷 + 원자 포인터 교체로 reader 경합 제거(읽기-쓰기 분리).
- per-core 자료구조: 스레드 로컬 큐/카운터로 합산은 나중에(작업 훔치기, 샤딩된 카운터). false sharing 은
alignas(64)패딩으로 분리. - 배치·입도 조절: 너무 잘게 나누면 동기화 오버헤드가, 너무 굵으면 부하 불균형이 커진다 — 작업 입도를 튜닝.
- 사고 과정: ① 직렬 병목(공유 자원)을 찾는다 → ② 그 자원을 분할/복제/불변화해 경합을 없앤다 → ③ 남은 동기화 지점을 배치·비동기로 줄인다 → ④ 확장성 곡선으로 재측정. "코어를 더 주면 빨라지나?"가 아니라 "무엇이 직렬인가?"를 먼저 묻는다.
면접 포인트
- 암달:
Speedup = 1/(s + (1-s)/N), 상한1/s. s=5% → 최대 20배. 직렬 비율이 천장을 결정한다. - 구스타프슨은 문제 크기를 자원과 함께 키우는 관점 — 대규모 서버의 "동접 확장"에 더 부합하지만, 직렬 병목이 크면 그 낙관도 깨진다.
- 확장의 적은 공유 자원의 경합(락·캐시 코히어런시·NUMA)이다. 샤딩·락프리·per-core 화 로
s를 줄이는 게 코어를 더 꽂는 것보다 중요하다.