"CPU가 프로세스를 바꿀 때 정확히 어떤 일이 일어나고, 그 비용은 얼마나 될까?"

컨텍스트 스위칭은 운영체제 수업에서 처음 배울 때는 "아, 프로세스 전환할 때 뭔가 저장하고 복원하는 거구나" 정도로 넘어가기 쉽습니다. 그런데 공부하다 보니, 진짜 비용이 어디서 발생하는지를 알아야 스레드가 왜 가벼운지, Virtual Thread는 뭐가 다른지가 연결되더라고요.

컨텍스트 스위칭이란

CPU가 현재 실행 중인 프로세스(또는 스레드)를 멈추고 다른 프로세스로 전환하는 과정입니다. 이때 현재 상태를 저장하고, 다음에 실행할 프로세스의 상태를 복원합니다.

전환이 일어나는 시점

  • **타임 슬라이스 만료 **: 스케줄러가 할당한 시간이 끝났을 때
  • **I/O 대기 **: 디스크나 네트워크 요청으로 블로킹될 때
  • ** 인터럽트 발생 **: 하드웨어 인터럽트 처리가 필요할 때
  • ** 시스템 콜 **: 커널 모드 진입이 필요한 호출 시

PCB 저장과 복원 — 전환의 핵심 단계

컨텍스트 스위칭의 기본은 PCB(Process Control Block) 에 현재 CPU 상태를 저장하고, 다음 프로세스의 PCB에서 상태를 꺼내오는 것입니다.

PCB에 저장되는 정보

  • 프로그램 카운터(PC): 다음에 실행할 명령어 주소
  • **CPU 레지스터 **: 범용 레지스터, 스택 포인터, 상태 레지스터 등
  • ** 프로세스 상태 **: Running → Ready 또는 Waiting
  • ** 메모리 관리 정보 **: 페이지 테이블 베이스 레지스터(PTBR)
  • ** 스케줄링 정보 **: 우선순위, 큐 포인터

전환 과정 요약

PLAINTEXT
1. [현재 프로세스] 커널 모드 진입
2. [현재 프로세스] PCB에 CPU 상태 저장
3. [스케줄러] 다음 실행할 프로세스 선택
4. [다음 프로세스] PCB에서 CPU 상태 복원
5. [다음 프로세스] 유저 모드 복귀 → 실행 재개

여기까지만 보면 "레지스터 몇 개 복사하는 건데 뭐가 비싸지?"라고 생각할 수 있습니다. 진짜 비용은 다음 단계에 있습니다.

TLB 플러시 — 숨겨진 진짜 비용

프로세스마다 가상 메모리 공간이 다릅니다. 프로세스를 전환하면 TLB(Translation Lookaside Buffer) 에 캐시된 가상→물리 주소 변환 정보가 전부 무효화됩니다.

TLB가 비워지면 일어나는 일

  • 모든 메모리 접근이 페이지 테이블 워킹(Page Table Walking) 을 거쳐야 함
  • 4단계 페이지 테이블 기준, 한 번의 주소 변환에 최대 4번의 메모리 접근
  • TLB 미스 하나당 수십 나노초의 추가 지연

이것이 프로세스 전환의 가장 큰 숨겨진 비용입니다. 레지스터 저장/복원은 나노초 단위지만, TLB가 다시 웜업되기까지 수천~수만 번의 TLB 미스가 발생할 수 있습니다.

워킹 셋이 클수록 비용도 커진다

TLB 엔트리 수는 제한적입니다(보통 수백~수천 개). 프로세스가 사용하는 메모리(워킹 셋)가 클수록:

  • 플러시 후 다시 채워야 할 TLB 엔트리가 많아지고
  • 캐시 미스도 연쇄적으로 증가합니다
  • 결과적으로 워킹 셋이 큰 프로세스일수록 컨텍스트 스위칭 비용이 기하급수적으로 증가 합니다

CPU 파이프라인과 캐시 미스

TLB만 문제가 아닙니다. 컨텍스트 스위칭 후에는 CPU의 다른 캐시도 영향받습니다.

파이프라인 리필

현대 CPU는 명령어를 파이프라인으로 처리합니다. 컨텍스트 스위칭이 일어나면:

  • 파이프라인에 있던 명령어가 전부 무효화
  • 새 프로세스의 명령어로 파이프라인을 다시 채우는 데 10~50 사이클 소요
  • 분기 예측 기록도 리셋되어 초반 예측 정확도 하락

L1/L2 캐시 오염

  • 이전 프로세스의 데이터가 캐시에 남아 있지만 새 프로세스에는 쓸모없음
  • 새 프로세스가 실행되면서 캐시를 자기 데이터로 교체 → ** 콜드 미스 폭증**
  • L1 캐시 미스: ~1ns → L2 조회: ~5ns → L3 조회: ~15ns → RAM: ~80ns

이 연쇄적인 캐시 미스가 체감 성능 저하의 핵심입니다.

프로세스 vs 스레드 컨텍스트 스위칭

여기서 핵심 질문이 나옵니다. 왜 스레드 전환은 프로세스 전환보다 가벼울까요?

같은 프로세스 내 스레드가 공유하는 것

  • ** 가상 메모리 공간** (코드, 힙, 데이터 영역)
  • ** 페이지 테이블**
  • ** 파일 디스크립터**
  • ** 시그널 핸들러**

스레드만 따로 가지는 것

  • ** 스택**
  • ** 레지스터 세트** (PC, SP 포함)
  • ** 스레드 로컬 스토리지(TLS)**

핵심은 이것입니다: ** 같은 프로세스 내 스레드 전환에서는 가상 메모리가 바뀌지 않으므로 TLB 플러시가 필요 없습니다.**

벤치마크로 확인하는 비용 차이

5개의 프로세스/스레드를 생성하고 각각 1000만 회 컨텍스트 스위칭을 수행한 벤치마크 결과입니다.

BASH
# 프로세스 기반 (fork)
$ time ./process_switch_bench
real  0m3.120s
user  0m2.380s
sys   0m0.740s    # ← 커널 시간

# 스레드 기반 (pthread)
$ time ./thread_switch_bench
real  0m0.520s
user  0m0.436s
sys   0m0.084s    # ← 커널 시간
항목프로세스 전환스레드 전환차이
sys time0.740s0.084s** 약 8.8배**
주요 원인TLB 플러시 + 캐시 미스레지스터만 교체

약 9배 차이가 나는 이유는 TLB 무효화와 그에 따른 캐시 미스 때문입니다. 워킹 셋이 커질수록 이 차이는 더 벌어집니다.

프로세스 전환 vs 스레드 전환 비용 정리

PLAINTEXT
프로세스 전환:
  ① PCB 저장/복원           → ~수백 ns
  ② 페이지 테이블 교체       → ~수십 ns
  ③ TLB 전체 플러시          → 직접 비용은 작지만...
  ④ TLB 미스 연쇄            → 수천~수만 ns (워킹 셋 크기에 비례)
  ⑤ L1/L2 캐시 콜드 미스     → 수천 ns
  ⑥ CPU 파이프라인 리필      → 10~50 사이클

스레드 전환:
  ① 레지스터(PC, SP 등) 저장/복원  → ~수백 ns
  ② TLB 유지                       → 0 (가상 메모리 동일)
  ③ 캐시 일부 공유 가능             → 미스 감소

Virtual Thread와 컨텍스트 스위칭

Java 21에서 도입된 Virtual Thread는 컨텍스트 스위칭 비용을 한 단계 더 줄입니다.

기존 플랫폼 스레드의 한계

  • 1:1 매핑 (Java 스레드 1개 = OS 스레드 1개)
  • 전환할 때마다 커널 모드 진입 필요
  • 스레드 수천 개만 넘어가도 컨텍스트 스위칭 오버헤드가 체감됨

Virtual Thread의 접근

JAVA
// 수만 개의 Virtual Thread를 가볍게 생성
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    IntStream.range(0, 100_000).forEach(i -> {
        executor.submit(() -> {
            // I/O 작업 시 자동으로 캐리어 스레드에서 언마운트
            Thread.sleep(Duration.ofSeconds(1));
            return i;
        });
    });
}
  • **M:N 매핑 **: 수만 개의 Virtual Thread가 소수의 캐리어(OS) 스레드 위에서 실행
  • ** 유저 공간 스케줄링 **: JVM이 직접 관리하므로 커널 모드 전환 없이 스위칭
  • ** 스택 크기 **: 플랫폼 스레드 ~1MB vs Virtual Thread ~수 KB
  • I/O 블로킹 시 캐리어 스레드에서 ** 언마운트** → 다른 Virtual Thread를 바로 마운트

결국 컨텍스트 스위칭 비용의 핵심 체인은 이렇게 연결됩니다.

PLAINTEXT
프로세스 전환 (가장 무거움)
  ↓ TLB 플러시 제거
스레드 전환 (가벼움)
  ↓ 커널 모드 전환 제거
Virtual Thread 전환 (가장 가벼움)

정리

컨텍스트 스위칭의 비용은 "레지스터 복사"가 아니라 TLB 플러시 → 캐시 미스 연쇄 에 있습니다.

  • **프로세스 전환 **: 가상 메모리 교체 → TLB 전체 플러시 → 캐시 콜드 스타트 (sys 0.740s)
  • ** 스레드 전환 **: 가상 메모리 공유 → TLB 유지 → 레지스터만 교체 (sys 0.084s, 약 9배 빠름)
  • Virtual Thread: 유저 공간 스케줄링 → 커널 전환 자체를 건너뜀
  • 워킹 셋이 클수록 프로세스 전환 비용은 기하급수적으로 증가합니다
  • CPU 파이프라인 리필(10~50 사이클)까지 고려하면 실제 비용은 교과서보다 훨씬 큽니다

공부하면서 "스레드가 가볍다"는 말을 여러 번 들었는데, ** 왜 가벼운지 **를 TLB 관점에서 설명할 수 있으면 이 주제는 확실히 정리된 것이라고 생각합니다.

댓글 로딩 중...