하드웨어 중급 #2 CPU 심화 — 터보, 스로틀링, 스틸 타임
1편에서 지표 읽는 틀을 잡았으니, 자원 하나씩 살펴갑니다. 첫 번째는 CPU입니다. 기초 2편에서 코어, 스레드, 클럭, 캐시의 개념을 잡았다면, 이번 글은 그 부품들이 운영 중에 보이는 변덕을 다룹니다. 사양표대로 돌지 않는 클럭, 내 것인데 내 것이 아닌 vCPU, 그리고 코어가 놀고 있는데도 느린 서버 같은 것들입니다.
클럭은 고정값이 아닙니다 #
사양표에 “3.0GHz"라고 적혀 있어도, 실제 클럭은 매 순간 출렁입니다. 두 가지 메커니즘 때문입니다.
- 터보 부스트(turbo boost) — 전력과 온도에 여유가 있을 때 기본 클럭보다 높게 올려 주는 기능입니다. 핵심은 조건부라는 점입니다. 한두 코어만 일할 때는 높게 올라가지만, 모든 코어가 바쁘면 전력 한도 때문에 낮은 터보에 머뭅니다. “코어 1개 테스트에서는 빨랐는데 전 코어 부하에서는 코어당 성능이 떨어지는” 현상의 정체입니다.
- 서멀 스로틀링(thermal throttling) — 온도가 한계에 닿으면 CPU가 자신을 지키려고 클럭을 강제로 낮추는 동작입니다. 냉각이 부실한 서버나 밀도 높은 랙에서, 부하 시작 몇 분 뒤부터 성능이 떨어지는 패턴으로 나타납니다.
그래서 CPU 성능 문제를 볼 때는 사용률과 함께 현재 클럭을 확인하는 습관이 필요합니다. 사용률 100%인데 클럭이 기본값 아래라면, 문제는 일의 양이 아니라 전력이나 냉각 쪽입니다.
여기에 OS의 전력 정책이 한 겹 더 있습니다. 리눅스의 CPU 거버너(governor, 클럭을 어떤 정책으로 조절할지 정하는 설정)가 powersave로 잡혀 있으면, 부하가 와도 클럭을 천천히 올립니다. 지연에 민감한 서버가 이 설정 하나로 굼떠지는 일이 실제로 있습니다. 데이터베이스나 저지연 서비스 머신이라면 performance 거버너를 검토합니다.
스틸 타임 — 하이퍼바이저가 가져간 시간 #
1편의 CPU 내역에서 미뤄 둔 st(steal)를 볼 차례입니다. 스틸 타임(steal time)은 가상 머신이 CPU를 쓰려고 했지만 하이퍼바이저가 물리 CPU를 다른 곳에 주는 바람에 못 돈 시간입니다. 기초 7편에서 본 것처럼 vCPU는 물리 코어를 시분할로 나눠 받는 약속이라, 같은 호스트의 다른 가상 머신이 바쁘면 내 차례가 늦게 옵니다.
운영 관점의 기준은 이렇습니다.
- st가 0 근처를 유지하면 정상입니다.
- st가 지속적으로 수 퍼센트를 넘으면, 같은 호스트에 시끄러운 이웃이 있거나 호스트가 과적(오버커밋)된 상태입니다.
- 내 가상 머신 안에서는 고칠 방법이 없다는 것이 핵심입니다. 처방은 인스턴스를 다른 호스트로 옮기거나(재시작·재배포), 더 격리된 인스턴스 유형으로 바꾸는 것입니다.
클라우드에서 버스트형 인스턴스(t 계열 같은 크레딧 기반)라면 비슷한 증상의 다른 원인도 있습니다. CPU 크레딧이 바닥나면 기준 성능으로 묶입니다. st 와 크레딧 잔량을 같이 보면 두 경우를 구분할 수 있습니다.
컨텍스트 스위칭 — 일 바꾸기의 비용 #
코어 수보다 훨씬 많은 스레드가 돌면, CPU는 일을 잘게 번갈아 처리합니다. 이 전환이 컨텍스트 스위칭(context switching, 실행 중인 작업의 상태를 저장하고 다른 작업으로 갈아타는 일)이고, 공짜가 아닙니다. 상태 저장·복원의 직접 비용에 더해, 기초 2편에서 본 캐시에 쌓아 둔 데이터가 새 작업에게는 남이라서 캐시 미스가 늘어나는 간접 비용이 붙습니다.
증상은 1편의 내역으로 드러납니다. us는 낮은데 sy가 높고, 초당 컨텍스트 스위칭 수가 평소의 몇 배라면, 일 자체보다 일 바꾸기에 시간을 쓰고 있는 것입니다. 흔한 원인은 코어 수 대비 과도한 워커 스레드 설정, 잠금 경합으로 깨었다 잠들기를 반복하는 스레드들입니다. 처방은 하드웨어가 아니라 설정 쪽입니다. 워커 수를 코어 수 기준으로 줄이는 것부터 시작합니다.
CPU 핀닝 — 자리를 고정해 캐시를 지키기 #
기본적으로 스케줄러는 작업을 아무 코어에나 배치하고 수시로 옮깁니다. 대부분의 워크로드에는 그게 최선이지만, 지연에 극도로 민감한 프로세스는 옮겨 다닐 때마다 캐시를 잃습니다. CPU 핀닝(CPU pinning, 프로세스를 특정 코어에 고정하는 설정)은 그 이동을 막아 캐시 적중률과 지연의 일관성을 지키는 기법입니다.
# PID 1234 프로세스를 2,3번 코어에 고정
taskset -cp 2,3 1234다만 핀닝은 양날입니다. 고정된 코어가 바쁠 때 다른 코어가 한가해도 못 빌립니다. 일반 서비스에는 불필요하고, 저지연 네트워크 처리나 실시간성 워크로드처럼 지연의 들쭉날쭉함 자체가 문제인 경우에만 꺼내는 도구입니다. 4편의 NUMA에서 이 주제가 메모리 위치와 함께 다시 등장합니다.
자주 만나는 함정 #
- 사양표 클럭으로 성능을 약속한다 — 전 코어 부하의 실효 클럭은 단일 코어 터보보다 낮습니다. 용량 계획은 전 코어 부하 기준으로 잽니다.
- st를 내 서버 문제로 진단한다 — 스틸은 호스트 사정입니다. 가상 머신 안에서 프로세스를 아무리 최적화해도 안 줄어듭니다. 이주가 처방입니다.
- 스레드를 늘려 속도를 올리려 한다 — 코어가 포화면 스레드 증설은 컨텍스트 스위칭만 늘립니다. 늘리기 전에 1편의 포화 지표부터 확인합니다.
정리 #
이번 글에서 잡은 그림입니다.
- 클럭은 터보와 스로틀링, 거버너 정책에 따라 출렁입니다. 사용률과 함께 실제 클럭을 봅니다.
- 스틸 타임은 하이퍼바이저 수준의 경합 신호이고, 처방은 내부 튜닝이 아니라 이주입니다.
- 컨텍스트 스위칭 폭주는 sy 상승으로 드러나며, 흔한 원인은 과도한 스레드 설정입니다.
- CPU 핀닝은 지연 일관성이 중요한 워크로드 전용 도구입니다.
다음 — 메모리 심화 #
다음 글인 “하드웨어 중급 #3 메모리 심화"에서는 두 번째 자원으로 갑니다. 기초 3편에서 페이지 캐시와 OOM Killer의 개념을 잡았다면, 이번에는 free 출력을 정확히 읽는 법, 쓰기 폭주를 만드는 더티 페이지, 그리고 컨테이너 시대의 메모리 한도까지 운영의 층을 올리겠습니다.