컴퓨터의 CPU는 한 번에 하나의 작업만 처리할 수 있습니다. 하지만 우리는 웹 브라우저를 열어 글을 읽으면서 동시에 음악을 듣고, 백그라운드에서는 파일을 다운로드하는 등 여러 작업을 동시에 하는 것처럼 느낍니다. 이는 운영체제가 ‘스케줄러’라는 특별한 프로그램을 이용해 CPU 시간을 아주 짧게 쪼개어 여러 작업에 번갈아 할당하기 때문입니다. 마치 한 명의 바리스타가 여러 손님의 주문을 번갈아 처리하는 것과 비슷합니다.
이 스케줄러의 역할은 매우 중요합니다. 어떤 작업을 먼저 처리하고, 얼마나 오랫동안 CPU를 줄 것인지 결정하는 것은 시스템의 전반적인 성능과 사용자 경험에 큰 영향을 미칩니다. 리눅스 운영체제는 ‘CFS(Completely Fair Scheduler)’라는 스케줄러를 기본으로 사용합니다. CFS는 이름 그대로 ‘완전히 공정한’ 스케줄링을 목표로 설계되었습니다.
CFS란 무엇인가
CFS는 리눅스 커널 2.6.23부터 도입된 기본 프로세스 스케줄러입니다. 이 스케줄러의 핵심 철학은 시스템의 모든 실행 가능한 프로세스(작업)가 CPU 시간을 공정하게 분배받아야 한다는 것입니다. CFS는 각 프로세스가 CPU를 사용한 시간을 ‘가상 실행 시간(vruntime)’이라는 값으로 추적합니다. 그리고 이 vruntime 값이 가장 작은 프로세스, 즉 CPU를 가장 적게 사용한 프로세스에게 우선적으로 CPU를 할당하여 공정성을 유지합니다.
예를 들어, 세 명의 아이가 케이크를 나눠 먹는 상황을 상상해 보세요. CFS는 누가 가장 적게 먹었는지 파악해서 그 아이에게 먼저 케이크를 줍니다. 이런 방식으로 모든 아이가 비슷한 양의 케이크를 먹을 수 있도록 하는 것이죠. 이 덕분에 CFS는 일반적인 데스크톱 환경이나 서버 환경에서 모든 사용자와 애플리케이션이 시스템 자원을 합리적으로 공유하며 전체적인 처리량(throughput)을 높이는 데 탁월한 성능을 발휘합니다.
레이턴시 민감 워크로드란 무엇인가
‘레이턴시(Latency)’는 어떤 요청이 발생한 시점부터 그 요청에 대한 응답이 돌아오기까지 걸리는 시간을 의미합니다. ‘레이턴시 민감 워크로드’는 이 응답 시간이 매우 짧아야만 제 기능을 하거나 사용자 경험을 해치지 않는 작업들을 말합니다. 즉, 짧은 지연 시간(Low Latency)이 핵심 요구사항인 작업들입니다.
- 실시간 오디오/비디오 처리: 음악을 재생하거나 화상 통화를 할 때, 소리나 영상이 늦게 나오거나 끊기면 매우 불편합니다.
- 고주파 매매(HFT): 주식 시장에서 밀리초, 마이크로초 단위로 거래가 이루어지므로, 단 1밀리초의 지연도 큰 손실로 이어질 수 있습니다.
- 온라인 게임: 게임에서 캐릭터의 움직임이나 스킬 사용이 즉각적으로 반영되지 않으면 게임 플레이에 심각한 지장을 줍니다.
- 산업 제어 시스템: 로봇 팔이나 생산 라인을 제어할 때, 명령과 실행 사이에 지연이 발생하면 안전 문제나 생산 차질이 생길 수 있습니다.
- 인터랙티브 사용자 인터페이스: 웹 페이지나 애플리케이션에서 버튼을 눌렀을 때 즉각적으로 반응하지 않으면 답답함을 느낍니다.
이러한 워크로드들은 공정하게 CPU를 나누어 쓰는 것보다, 특정 작업을 최우선으로 즉시 처리하는 것이 훨씬 중요합니다.
CFS가 레이턴시 민감 워크로드에 불리한 이유
CFS의 ‘공정성’이라는 미덕이 레이턴시 민감 워크로드에서는 오히려 약점이 될 수 있습니다. 그 이유는 다음과 같습니다.
모든 작업에 대한 공정한 분배
CFS는 본질적으로 모든 실행 가능한 프로세스에 CPU 시간을 공정하게 분배하려고 합니다. 예를 들어, 시스템에 10개의 작업이 있다면 CFS는 각 작업이 대략 10%의 CPU 시간을 사용하도록 노력합니다. 하지만 레이턴시 민감 워크로드는 ‘나는 10%가 아니라 지금 당장 0.1%만이라도 CPU를 받아서 내 작업을 즉시 완료해야 해!’라고 외치는 작업들입니다. 공정한 분배는 이러한 긴급한 요청의 즉각적인 처리를 지연시킬 수 있습니다.
스케줄링 지연 시간과 최소 단위 시간
CFS는 시스템의 전반적인 응답성을 보장하기 위해 ‘스케줄링 지연 시간(sched_latency_ns)’이라는 목표를 설정합니다. 이 시간 안에 모든 실행 가능한 작업이 한 번씩 CPU를 사용하도록 노력합니다. 또한, 각 작업에 할당되는 CPU의 최소 단위 시간(sched_min_granularity_ns)도 설정되어 있습니다. 아무리 짧은 작업이라도 이 최소 단위 시간만큼은 CPU를 사용하게 됩니다.
문제는 레이턴시 민감 작업이 매우 짧고 즉각적인 응답을 필요로 할 때 발생합니다. 만약 현재 CPU를 사용하고 있는 다른 작업이 자신의 할당량을 다 소진하지 않았다면, 아무리 긴급한 레이턴시 민감 작업이라도 자신의 차례가 올 때까지 기다려야 합니다. 이 ‘기다림’의 시간이 바로 레이턴시를 증가시키는 주범입니다. 특히 작업이 많을수록, 각 작업이 CPU를 한 번씩 할당받기까지 걸리는 총 시간이 길어지므로 레이턴시는 더욱 증가합니다.
잦은 컨텍스트 스위칭 오버헤드
CFS는 공정성을 유지하기 위해 CPU를 자주 다른 작업으로 전환합니다. 이를 ‘컨텍스트 스위칭(Context Switching)’이라고 합니다. 컨텍스트 스위칭은 현재 실행 중인 작업의 상태를 저장하고, 다음 작업의 상태를 불러오는 과정입니다. 이 과정 자체에 CPU 시간과 메모리 자원이 소모됩니다. 작업 전환이 너무 잦으면, 실제 작업을 수행하는 시간보다 전환에 소모되는 시간이 많아져 전체적인 효율성이 떨어지고, 이는 레이턴시를 더욱 악화시킬 수 있습니다.
캐시 무효화 문제
컨텍스트 스위칭은 CPU 캐시에도 부정적인 영향을 미칩니다. 각 프로세스는 자신만의 데이터를 캐시에 올려두고 사용합니다. 하지만 다른 프로세스로 전환될 때, 이전에 사용하던 캐시 데이터는 더 이상 유효하지 않게 되거나 다른 데이터로 덮어씌워질 수 있습니다. 다음 번에 자신의 차례가 돌아와 CPU를 사용하려고 할 때, 필요한 데이터가 캐시에 없어 메인 메모리에서 다시 불러와야 하는 ‘캐시 미스(Cache Miss)’가 발생할 수 있습니다. 메인 메모리 접근은 캐시 접근보다 훨씬 느리므로, 이 또한 레이턴시 증가의 원인이 됩니다.
실생활에서의 활용 방법과 유용한 팁
CFS의 한계를 인지했다면, 레이턴시 민감 워크로드를 다룰 때 어떻게 접근해야 할까요? 다음은 실용적인 팁들입니다.
우선순위 조정을 통한 스케줄링 정책 변경
리눅스에서는 단순히 CFS의 공정성만 따르는 것이 아니라, 특정 작업에 더 높은 우선순위를 부여하거나 아예 다른 스케줄링 정책을 적용할 수 있습니다. 이를 통해 레이턴시를 개선할 수 있습니다.
nice및renice명령: 일반적인 CFS 작업의 우선순위를 조정할 수 있습니다.nice값은 -20(가장 높은 우선순위)부터 19(가장 낮은 우선순위)까지 설정할 수 있습니다. 낮은nice값은 해당 프로세스가 CPU를 더 자주, 더 오래 할당받을 수 있도록 돕습니다. 하지만 여전히 CFS의 공정성 원칙 내에서 작동하므로, 극단적인 레이턴시 개선에는 한계가 있습니다.chrt명령과 실시간 스케줄링 정책: 극도의 레이턴시 민감 작업에는 CFS 대신 ‘실시간 스케줄링 정책’을 사용할 수 있습니다. 리눅스에는SCHED_FIFO(선입선출)와SCHED_RR(라운드 로빈)이라는 두 가지 실시간 스케줄링 정책이 있습니다. 이 정책들은 CFS보다 훨씬 높은 우선순위를 가지며, 커널 스케줄러가 일반 CFS 작업보다 실시간 작업을 항상 먼저 처리합니다.SCHED_FIFO: 한 번 CPU를 할당받으면 더 높은 우선순위의 실시간 작업이 나타나지 않는 한, 해당 작업이 스스로 CPU를 포기하거나 블록될 때까지 계속 실행됩니다.SCHED_RR:SCHED_FIFO와 유사하지만, 정해진 시간 할당량(timeslice)만큼만 실행된 후, 동일한 우선순위의 다른SCHED_RR작업에게 CPU를 넘겨줍니다.
주의사항: 실시간 스케줄링은 매우 강력하므로, 잘못 사용하면 다른 시스템 작업들을 완전히 멈추게 하거나 시스템 불안정을 초래할 수 있습니다. 반드시 충분한 테스트와 이해를 바탕으로 사용해야 합니다.
CPU 코어 고정
특정 레이턴시 민감 작업을 특정 CPU 코어에 고정(CPU affinity)시키는 방법입니다. 이렇게 하면 해당 코어는 다른 작업의 방해를 덜 받고, 고정된 작업의 캐시 효율성도 높아져 레이턴시를 줄일 수 있습니다. taskset 명령어를 사용하여 프로세스를 특정 CPU 코어에 고정할 수 있습니다.
예를 들어, taskset -c 0,1 my_latency_sensitive_app 명령은 my_latency_sensitive_app을 CPU 코어 0과 1에서만 실행되도록 설정합니다.
커널 파라미터 튜닝
CFS의 동작 방식을 제어하는 커널 파라미터들을 조정하여 레이턴시를 개선할 수 있습니다. /proc/sys/kernel/sched_ 경로에 있는 파일들을 통해 설정할 수 있습니다.
sched_latency_ns: CFS가 모든 실행 가능한 작업을 한 번씩 스케줄링하려고 시도하는 목표 시간입니다. 이 값을 줄이면 각 작업이 더 자주 CPU를 얻게 되어 응답성이 높아질 수 있지만, 컨텍스트 스위칭 오버헤드가 증가할 수 있습니다.sched_min_granularity_ns: 각 작업이 CPU를 한 번 할당받았을 때 최소한으로 실행되는 시간입니다. 이 값을 줄이면 작업 전환이 더 자주 일어나 레이턴시가 줄어들 수 있으나, 이 역시 컨텍스트 스위칭 오버헤드를 증가시킬 수 있습니다.sched_wakeup_granularity_ns: 잠자고 있던 작업이 깨어났을 때, 현재 실행 중인 작업보다 얼마나 더 중요해야 CPU를 빼앗아 올 수 있는지 결정하는 임계값입니다. 이 값을 줄이면 깨어난 작업이 더 빨리 CPU를 얻을 수 있습니다.
경고: 이러한 커널 파라미터 튜닝은 시스템 전체의 동작에 영향을 미치므로, 매우 신중하게 접근하고 충분한 테스트를 거쳐야 합니다. 잘못된 설정은 오히려 시스템 성능을 저하시키거나 불안정하게 만들 수 있습니다.
인터럽트 처리 최적화
네트워크 카드, 디스크 등 하드웨어 장치에서 발생하는 인터럽트도 CPU 시간을 사용합니다. 레이턴시 민감 워크로드에서는 인터럽트 처리로 인한 지연도 최소화해야 합니다. 특정 CPU 코어를 인터럽트 처리 전용으로 할당하거나, irqbalance 데몬을 비활성화하여 인터럽트 분배를 수동으로 제어하는 방법을 고려할 수 있습니다.
시스템 자원 격리 및 노이즈 감소
레이턴시 민감 애플리케이션이 실행되는 환경에서는 불필요한 백그라운드 서비스나 데몬을 최소화해야 합니다. 또한, Cgroup과 같은 리소스 관리 도구를 사용하여 CPU, 메모리, I/O 자원을 격리함으로써 다른 작업의 간섭을 줄일 수 있습니다.
흔한 오해와 사실 관계
오해1 CFS는 성능이 나쁜 스케줄러다
CFS는 일반적인 데스크톱, 서버, 개발 환경 등 대부분의 범용 워크로드에서 매우 훌륭한 성능을 제공하는 스케줄러입니다. 전체 시스템의 처리량(throughput)을 극대화하고, 모든 프로세스에 공정한 기회를 제공하여 사용자에게 부드럽고 반응성 좋은 경험을 선사합니다. CFS가 레이턴시 민감 워크로드에 불리하다는 것은 특정 요구사항에 대한 상대적인 약점일 뿐, CFS 자체의 결함이 아닙니다.
오해2 실시간 스케줄러를 사용하면 모든 레이턴시 문제가 해결된다
실시간 스케줄러(SCHED_FIFO, SCHED_RR)는 레이턴시를 극적으로 줄일 수 있지만, 만능 해결책은 아닙니다. 실시간 스케줄러는 일반 CFS 작업보다 훨씬 높은 우선순위를 가지므로, 잘못 사용하면 다른 모든 작업을 굶겨 죽일 수 있습니다. 이는 시스템 전체의 안정성과 반응성을 심각하게 저해할 수 있습니다. 실시간 스케줄러는 애플리케이션 설계 단계부터 레이턴시를 고려하여 신중하게 사용해야 하며, 사용 시에도 해당 프로세스의 CPU 사용량이 과도하지 않도록 주의해야 합니다.
오해3 더 많은 CPU 코어가 레이턴시 문제를 해결한다
더 많은 CPU 코어가 항상 레이턴시를 줄여주는 것은 아닙니다. 코어 수가 많아지면 병렬 처리 능력이 향상되어 전체 처리량은 늘어날 수 있지만, 레이턴시는 개별 작업의 응답 시간에 더 크게 좌우됩니다. 만약 레이턴시 문제가 CPU 스케줄링이 아닌, I/O 대기, 메모리 접근, 락 경합 등 다른 요인에 의해 발생한다면, 코어 수를 늘리는 것은 큰 효과가 없을 수 있습니다. 오히려 스케줄러가 관리해야 할 코어가 많아져 오버헤드가 증가할 수도 있습니다.
전문가의 조언
레이턴시 민감 워크로드를 다룰 때는 다음 사항들을 기억하는 것이 중요합니다.
- 측정과 프로파일링이 핵심: 어떤 부분이 레이턴시를 유발하는지 정확히 파악하는 것이 중요합니다.
perf,ftrace,strace와 같은 리눅스 도구들을 활용하여 시스템 호출, 함수 실행 시간, 컨텍스트 스위치 발생 빈도 등을 분석해야 합니다. 추측에 의존한 최적화는 시간 낭비일 뿐만 아니라 오히려 문제를 악화시킬 수 있습니다. - 애플리케이션 레벨 최적화 우선: 커널 파라미터 튜닝이나 스케줄링 정책 변경과 같은 시스템 레벨의 최적화는 강력하지만 위험 부담도 큽니다. 먼저 애플리케이션 코드 자체의 효율성을 높이는 데 집중해야 합니다. 불필요한 계산 줄이기, 효율적인 자료구조 사용, I/O 최소화, 락 경합 줄이기 등이 여기에 해당합니다.
- 워크로드 특성 이해: 당신의 애플리케이션이 어떤 패턴으로 CPU를 사용하는지, 얼마나 자주 I/O를 수행하는지, 메모리 사용량은 어떤지 등 워크로드의 특성을 정확히 이해해야 합니다. 이에 따라 스케줄링 정책이나 튜닝 전략이 달라질 수 있습니다.
- 점진적인 변화와 테스트: 어떤 변경이든 한 번에 여러 가지를 바꾸기보다는 한 번에 하나씩 변경하고, 변경 전후의 성능을 면밀히 비교 분석해야 합니다. 이는 문제의 원인을 정확히 파악하고, 최적화의 효과를 검증하는 데 필수적입니다.
비용 효율적인 활용 방법
하드웨어를 무작정 업그레이드하기보다는 소프트웨어적인 최적화를 통해 비용 효율적으로 레이턴시 문제를 해결하는 방법들도 있습니다.
- 코드 최적화에 집중: 가장 비용 효율적인 방법은 애플리케이션 코드를 최적화하는 것입니다. 불필요한 계산을 줄이고, 메모리 할당 및 해제를 최소화하며, 효율적인 알고리즘과 자료구조를 사용하는 것은 하드웨어 업그레이드 없이도 레이턴시를 크게 개선할 수 있습니다.
- 최소한의 하드웨어 요구사항 파악: 레이턴시 요구사항을 만족시키기 위해 실제로 필요한 CPU 속도, RAM 용량, 디스크 I/O 성능 등을 정확히 파악해야 합니다. 과도한 하드웨어 스펙은 불필요한 비용 낭비로 이어집니다. 적절한 모니터링과 벤치마킹을 통해 병목 지점을 정확히 찾아내는 것이 중요합니다.
- 가상화 및 컨테이너 환경의 신중한 사용: 가상 머신(VM)이나 컨테이너(Docker, Kubernetes)는 자원 효율성을 높여주지만, 가상화 오버헤드가 레이턴시에 영향을 줄 수 있습니다. 극도의 레이턴시 민감 워크로드의 경우, 베어 메탈(Bare Metal) 서버에서 직접 실행하는 것이 유리할 수 있습니다. 가상화 환경을 사용해야 한다면, CPU 패스스루(CPU passthrough)나 전용 코어 할당과 같은 기능을 활용하여 오버헤드를 최소화해야 합니다.
- 지속적인 모니터링 및 분석: 시스템의 성능을 지속적으로 모니터링하고 데이터를 분석하는 것은 비용을 절감하는 데 큰 도움이 됩니다. 문제가 발생하기 전에 잠재적인 병목 현상을 식별하고, 불필요한 자원 낭비를 줄이며, 적시에 최적화 조치를 취할 수 있습니다. 오픈 소스 모니터링 도구(Prometheus, Grafana 등)를 활용하면 비용 부담 없이 시스템을 감시할 수 있습니다.
자주 묻는 질문과 답변
Q. CFS 환경에서 실시간 태스크를 실행하면 어떻게 되나요
리눅스 커널은 CFS 스케줄러가 관리하는 일반 태스크보다 SCHED_FIFO나 SCHED_RR과 같은 실시간 스케줄링 정책이 적용된 태스크에 항상 더 높은 우선순위를 부여합니다. 따라서, 실시간 태스크가 실행 가능한 상태가 되면, CFS 태스크가 CPU를 사용 중이더라도 즉시 선점하여 CPU를 할당받습니다. 이는 실시간 태스크의 레이턴시를 최소화하기 위한 설계입니다.
Q. 레이턴시 문제를 식별하는 데 도움이 되는 도구는 무엇인가요
리눅스에는 레이턴시 문제를 식별하고 분석하는 데 유용한 여러 도구가 있습니다.
perf: CPU 사용량, 캐시 미스, 컨텍스트 스위치 등 다양한 성능 카운터를 측정하고 분석하는 데 사용됩니다.ftrace: 커널 내부의 함수 호출을 추적하여 특정 이벤트가 발생했을 때의 지연 시간을 파악하는 데 유용합니다.strace: 프로세스가 어떤 시스템 호출을 하는지, 그리고 각 호출에 얼마나 시간이 걸리는지 추적하여 I/O나 시스템 호출로 인한 지연을 찾을 수 있습니다.htop/top: 실시간으로 프로세스별 CPU, 메모리 사용량을 확인하고,nice값 등을 조정하는 데 사용됩니다.latencytop: 커널이 어떤 이유로 프로세스를 대기시켰는지(I/O, 락 경합 등) 보여주어 레이턴시의 원인을 파악하는 데 도움을 줍니다.
Q. CFS 외에 다른 스케줄러가 있나요
네, 리눅스 커널에는 CFS 외에도 여러 스케줄링 정책이 있습니다.
SCHED_OTHER(CFS): 기본 스케줄러로, 공정성을 목표로 합니다.SCHED_BATCH: CPU 집약적인 백그라운드 작업에 적합합니다. 컨텍스트 스위치를 줄여 전체 처리량을 높이는 데 중점을 둡니다. 대화형 응답성에는 불리합니다.SCHED_IDLE: 가장 낮은 우선순위의 스케줄러로, 시스템에 유휴 자원이 있을 때만 실행됩니다.SCHED_FIFO/SCHED_RR: 위에서 설명한 실시간 스케줄러입니다.
이러한 스케줄러들은 특정 워크로드의 요구사항에 맞춰 최적화된 동작을 제공합니다. 하지만 일반적으로 대부분의 사용자는 CFS의 기본 설정으로 충분한 성능을 경험할 수 있습니다.