8장 : 메모리 계층

🧠 컴퓨터의 메모리 계층 구조와 캐시 메모리의 모든 것

1. 기억 장치 계층 구조란?

컴퓨터 내부에는 데이터를 저장하고 불러오는 다양한 저장 장치들이 있습니다. 이들을 성능과 특성에 따라 정리한 것이 바로 "기억장치 계층 구조"입니다. 이 계층은 보통 위로 갈수록 작고 빠르며 비싸고, 아래로 갈수록 크고 느리며 싸다는 특징을 가집니다.

  • 레지스터: CPU 명령어 실행에 직접 사용되는 가장 빠른 저장 장치

  • 캐시 메모리 (L1, L2, L3): CPU 속도를 보조하는 중간 저장소

  • 메인 메모리 (RAM): 실행 중인 프로그램과 데이터를 저장하는 공간

  • 보조 저장장치 (SSD, HDD): 전원을 꺼도 유지되는 장기 저장 장치

이 구조 덕분에 속도와 비용, 효율성을 모두 고려한 데이터 처리 시스템이 구현됩니다.

2. CPU는 왜 캐시 메모리를 사용할까?

CPU는 명령어를 처리할 때 다음의 기본 사이클을 따릅니다:

  1. 명령어 Fetch: 메모리에서 명령어를 읽어온다

  2. Operand Fetch: 명령어 실행에 필요한 데이터를 메모리에서 가져온다

  3. Execute: 레지스터에서 연산을 수행한다

  4. Write Back: 결과를 다시 메모리에 기록한다

하지만 이 중 Fetch 단계가 매우 느립니다. 레지스터에서 처리하는 계산 시간에 비하면 메모리 접근 속도는 무척 느립니다. 메모리 접근이 CPU 연산보다 훨씬 오래 걸리기 때문이죠.

EX. 어떤 PC에서는 레지스터 계산은 1회당 약 1나노초 미만이지만, 메모리 접근은 1회당 수 십 나노초가 걸립니다. 따

그래서 등장한 것이 캐시 메모리입니다.

캐시는 자주 쓰는 데이터를 메모리 대신 저장하고, 훨씬 더 빠르게 접근할 수 있게 도와주는 중간 저장소입니다.

3. 캐시 메모리의 동작 원리

캐시 라인(Cache Line) 단위로 저장

캐시는 메모리 주소 1개만 가져오는 것이 아니라, 일정 크기의 데이터를 묶음(Cache Line)으로 가져옵니다. 캐시 메모리에 캐시라인이라고 부 르는 단위로 데이터를 읽어서 그 데이터를 레지스터로 옮깁니다. 예: 64Byte

가상적인 CPU로 캐시 메모리를 살펴보자.

  • 레지스터는 RO과 R1 두 종류이며, 둘 다 크기는 10바이트

  • 캐시 메모리 크기는 50바이트

  • 캐시라인 크기는 10바이트

  1. CPU의 RO에 메모리 주소 300에 접근

  1. CPU가 주소 300의 데이터를 다시 읽는다면, 예를 들어 RI으로 읽어 온다면 메모리에서 가져오 는 대신에 캐시 메모리에 곧바로 접근하면 되니까 빠르게 처리가 가능합니다.

  1. RO 값을 변경하고 변경된 내용을 메모리 주소 300에 반영한다면 메모 리에 쓰기 전에 캐시 메모리에 먼저 저장합니다.

이때 캐시라인에는 메모리에서 읽어 들인 데이 터가 변경되었다는 것을 뜻하는 표시를 붙입니다. 이런 표시가 붙은 캐시라인을 더티하다라 고 이야기합니다

더티 비트(Dirty Bit)

  • CPU가 값을 바꾸면 캐시에서 먼저 값이 바뀜

  • 나중에 이 변경된 값을 메모리에 반영할 때만 쓰기 작업 수행

  • 이때 더티 비트를 체크해, 실제로 바뀐 것만 쓰도록 함

메모리에 데이터를 쓰는 방법: 라이트 스루 vs 라이트 백

전략
설명
장점
단점

Write-through

캐시에 저장하면서 동시에 메모리에도 저장

데이터 유실 없음

느림

Write-back

일단 캐시에만 저장하고, 나중에 메모리에 저장

빠름

전원 꺼지면 손실 위험

  1. 더티 표시 붙은 캐시라인을 메모리에 반영하기

캐시 메모리가 가득 찼는데 캐시에 존재하지 않는 데이터를 읽어 들이면 기존의 캐시라인 중에 서 하나를 버리고 빈 캐시라인에 새로운 데이터를 넣습니다.

  1. 캐시 메모리가 가득 찬 상태일 때 캐시라인에서 데이터 버리기

예시로주소 350의 데이터를 읽으면 캐시라인의 데이터 하나를 버리고(주소 340-350 필드)

  1. 새로운 데이터를 캐시라인에 복사

지금 비운 캐시 라인에 읽어 올 주소의 데이터를 복사합니다.

이때 버리는 캐시라인이 더티 상태라면 메모리에 데이터를 저장하는 클린 처리를 하 고 버립니다.

4. 캐시 교체 알고리즘과 스래싱

캐시 메모리는 용량이 작기 때문에, 자주 사용하는 데이터만 유지하고 오래 안 쓰는 것은 버립니다. 이를 위한 알고리즘이 대표적으로 LRU(Least Recently Used) 등입니다.

하지만 어떤 프로그램이 너무 많은 메모리 범위를 널뛰듯 접근하면, 캐시가 계속 버리고 읽는 상태가 되면서 성능이 급감하는 스래싱(thrashing) 현상이 발생합니다.

5. 캐시의 작동원리 : 지역성(Locality)의 법칙

"미리 쓸만한 데이터를 메인 메모리에서 캐시에 옮겨놓자"

캐시 메모리를 관통하는 핵심 개념은 데이터 지역성 (Locality) 입니다. 캐시 메모리는 이 원리로 작동합니다

  1. 시간적 지역성(Temporal locality): 최근에 사용한 데이터는 가까운 미래에 또 사용된다 (예: 반복문 안 변수)

  2. 공간적 지역성(Spatial locality): 한 번 접근한 메모리 근처가 다음에 또 사용된다 (예: 배열 순회)

  3. 순차적 지역성( Sequential Locality) : 비순차적 실행이 아닌 이상 명령어들이 메모리에 저장된 순서대로 실행되는 특성을 고려하여 다음 순서의 데이터가 곧 사용될 가능성이 높다.

예를 들어, 공간 지역성은 A[0], A[1] 로 구성되는 배열과 같이 참조된 데이터 근처의 데이터가 잠시후에 사용될 가능성이 높다는 것입니다.

반복문 돌릴 때 i, i+1, i+2 이런 식으로 접근하잖아요? 이걸 미리 예측해서 데이터를 캐시에 담아두는 겁니다.

결국 캐시는 이렇게 생각하면 돼요:

"CPU가 가장 아끼는 친구! 항상 옆에 붙어 다니는 비서 같은 존재"

이 지역성을 활용하면 적은 캐시 용량으로도 성능을 대폭 향상시킬 수 있습니다.

6. 캐시 계층 구조 (L1 / L2 / L3)

L1 캐시

  • CPU 내부에 존재

  • 매우 빠르고 작음 (32KB ~ 64KB)

  • 명령어와 데이터를 구분해 저장하는 경우도 있음

L2 캐시

  • CPU 칩 내부 또는 외부

  • 속도는 L1보다 느리지만, 용량이 큼 (256KB ~ 512KB)

L3 캐시

  • 여러 코어가 공유하는 캐시

  • 수 MB~수십 MB 수준의 용량

7. 실험: 캐시 접근 속도 시각화

리눅스 환경에서 Go 언어와 Python을 이용해 다음 실험을 수행했습니다.

실험 목적

  • 캐시 메모리(L1/L2/L3)와 메인 메모리(RAM)의 접근 속도 차이를 측정합니다.

실험 구조

  1. 2ⁿ(KiB)의 크기를 가지는 버퍼를 반복 생성 (4KB ~ 64MB)

  2. 각 버퍼의 모든 캐시라인에 접근

  3. 접근 시간(나노초 단위)을 기록

  4. matplotlib로 결과 그래프 작성

실험 코드 (요약)

// cache.go - Go로 메모리 접근 속도 측정
data, err := syscall.Mmap(-1, 0, bufSize, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_ANON|syscall.MAP_PRIVATE)
...
data[j] = 0  // 캐시라인 접근
...
# plot-cache.py - 결과 시각화
ax.set_xlabel("[2^x KiB]")
ax.set_ylabel("접근 속도[접근횟수/나노초]")

8. 실험 결과: 접근 속도 차이

아래 그래프는 버퍼 크기에 따른 접근 속도 변화를 보여줍니다:

전반적인결과 해석

  • L1, L2, L3 캐시의 경계에서 속도가 계단식으로 변화합니다.

  • 캐시보다 큰 버퍼를 접근할수록 속도가 급격히 느려집니다. (L2→L3→RAM 이동)

  • 특정 용량(예: 32KB, 256KB, 4MB) 전후로 성능이 달라지는 이유는 해당 캐시 용량을 넘어서기 때문입니다.

  • 작은 버퍼에서는 매우 빠름 (L1 캐시 내에서 동작)

그래프 구조 해석

  • X축: 버퍼 크기 (2^x KiB 단위)

    • 2⁰ = 1KiB, 2¹ = 2KiB, ..., 2¹⁶ = 64MiB까지

    • 즉, 버퍼 크기를 기하급수적으로 증가시키며 측정한 실험입니다.

  • Y축: 접근 속도 (접근횟수/나노초)

    • 값이 클수록 빠른 처리 속도를 의미합니다.

    • 단위는 "얼마나 많은 캐시라인을 1나노초에 접근했는가"입니다.

🔍 계단형 그래프가 의미하는 것

1. 4~64KiB 구간

  • 그래프는 1.4~1.0 사이에서 상대적으로 빠른 속도를 유지합니다.

  • → 이 구간은 L1 캐시(보통 32~64KiB)에 접근이 가능한 영역입니다.

  • 시간적/공간적 지역성 덕분에 대부분의 데이터가 L1에 캐시되어 있어 빠르게 처리됩니다.

2. 64KiB~512KiB 사이

  • 접근 속도가 급격히 떨어지기 시작합니다.

  • → 이는 L1 캐시를 초과했기 때문이며, L2 캐시로 전환되었음을 나타냅니다.

  • 속도는 떨어지지만 L2는 L1보다는 느려도 RAM보다는 빠름.

3. 512KiB~4MiB 구간

  • 두 번째 계단처럼 속도가 다시 하락 → L2 캐시도 초과됨.

  • 이 구간은 L3 캐시 또는 일부 RAM 접근이 시작되는 구간입니다.

4. 4MiB 이후 (대략 x=12 이상)

  • 접근 속도는 0.2~0.4 수준으로 낮아집니다.

  • 이 시점부터는 대부분의 접근이 **메인 메모리(RAM)**를 대상으로 이루어집니다.

  • 캐시의 이점을 거의 누릴 수 없으며, 캐시 미스로 인한 성능 저하가 뚜렷해집니다.

💡 왜 처음엔 속도가 점점 빨라지는가?

  • 프로그램은 단순히 메모리만 접근하는 것이 아니라, 루프 변수 증가나 조건문(if) 처리 등의 부가적인 명령어도 함께 실행합니다.

  • 버퍼가 작을 때는 부가 명령어의 실행 시간이 상대적으로 크게 작용하여, 접근 속도가 왜곡되어 더 낮게 나올 수 있음.

  • 버퍼가 커질수록 순수 메모리 접근 시간이 지배적으로 작용하게 되므로 측정이 더 정확해집니다.

결론 요약

구간
캐시 계층 추정
해석

4KiB~64KiB

L1 캐시 영역

가장 빠른 접근, 캐시 히트율 높음

64KiB~512KiB

L2 캐시 영역

속도 감소 시작

512KiB~4MiB

L3 캐시 또는 RAM 일부 접근

추가 감소

4MiB~64MiB

메인 메모리(RAM)

속도 최저, 캐시 미스 다수 발생

  • 캐시 메모리의 효과는 명확히 존재하며, 적절한 프로그램 구조는 성능을 비약적으로 향상시킬 수 있습니다.

  • 특히 반복문 안에서 사용하는 배열은 공간 지역성을 최대한 활용해야 하며,

  • 데이터 구조 설계 시 캐시 크기를 고려하는 습관이 중요합니다.

“좋은 프로그램은 캐시를 이해하는 데서 시작된다.” – 컴퓨터 아키텍처 설계자들이 입을 모아 강조하는 말입니다

8. 메모리 접근 성능 실험(cache.go)

실험 개요

  • 점점 더 큰 버퍼(4KB~64MB)를 만들고

  • 각 캐시라인을 순차적으로 접근

  • 접근 횟수 대비 소요 시간 측정

결과

이 실험을 통해 캐시의 존재와 효용성을 수치로 증명할 수 있습니다.

9. 결론: 메모리 계층 구조의 존재 이유

왜 이렇게 복잡한 메모리 계층을 만들었을까요?

"빠르고 비싼 기억장치는 작게, 느리고 저렴한 기억장치는 크게."

이 전략을 통해 우리는 컴퓨터 시스템에서

  • 속도 (캐시)

  • 유지 비용 (RAM/디스크)

  • 유연성 (계층형 메모리 활용) 을 모두 확보할 수 있게 된 것입니다.

캐시 최적화를 위해 할 수 있는 일

  • 코드에서 지역성(locality) 을 높이도록 설계해야 합니다.

  • 배열은 순차적으로 접근하고, 메모리 접근 패턴을 예측 가능하게 해야 합니다.

  • 고성능 연산에서는 캐시라인을 고려한 데이터 정렬(alignment) 도 중요합니다.

🧠 리눅스 메모리 계층과 최적화 기술

1. SMT(Simultaneous Multi Threading): CPU 유휴 자원 활용

1) 개념

데이터 전송 대기 이외에도 CPU 계산 자원이 놀고 있는 이유는 많습니다. 예를 들어 CPU에는 정수 연산을 하는 유닛과 부동 소수점 연산을 하는 유닛이 존재하는데, 정수 연산을 하는 동안부동 소수점 연산 유닛은 놀고 있는 상태가 됩니다.

이런 비어 있는 자원을 하드웨어의 Simultaneous Multi Threading(동시 멀티스레딩) 기능은 유용하게 활용할 수 있습니다.

++ SMT에서 말하는 스레드는 프로세스에서 말하는 스레드와 별개의 단어

  • CPU 코어 내부의 유휴 계산 자원(정수 연산 유닛, 부동소수점 유닛 등)을 효율적으로 활용하기 위해, 하나의 CPU 코어가 여러 스레드를 동시에 실행할 수 있도록 한 기술.

    • SMT는 하나의 CPU 코어에서 여러 스레드를 동시에 처리하는 기술입니다.

  • SMT로 인해 한 CPU 코어당 2개 이상의 논리 CPU가 생김.

  • 리눅스는 이들 스레드를 논리 CPU로 인식합니다.

    • 예: 물리 코어가 4개인 CPU에 SMT가 적용되면, 논리 CPU는 8개로 보입니다.

2) 예시 상황

  • 한 스레드가 메모리 접근을 기다릴 때, 다른 스레드가 대기 중인 계산 자원을 사용해 실행됨.

    • 각 논리 CPU는 레지스터 등의 자원을 별도로 소유하며, 코어 내부 자원을 공유함.

    • 자원이 비어 있는 경우, 다른 스레드가 이를 활용해 처리할 수 있습니다.

  • 정수 연산만 사용하는 스레드와 부동소수점 연산만 사용하는 스레드가 SMT로 함께 실행되면 효과 극대화.

예시 상황:
- 논리 CPU 10에서 프로세스 p0 실행 중
- 논리 CPU 11에 프로세스 p1 실행 대기 중
→ p0이 정수 연산 중이고, p1이 부동소수점 연산을 한다면,
   p1이 부동소수점 유닛을 가져다 사용 가능!

3) 효과와 한계

상황
SMT 효과

두 스레드의 자원 사용이 겹치지 않음

✅ 성능 향상

자원 사용이 겹침

⚠ 오히려 SMT 미사용보다 성능 저하 가능

4) SMT 실험 결과 예시

실험: cpuperf.sh -m 12 실행 → 최대 12개 프로세스 부하

평균 턴어라운드 타임

“사용자가 어떤 작업을 시작했을 때, 그 작업이 언제 끝나는지를 보는 것”

예시로 알아보기

프로세스
도착 시간
종료 시간
턴어라운드 타임

P1

0

5

5

P2

2

9

7

P3

4

12

8

평균 턴어라운드 타임 = (5 + 7 + 8) / 3 = 6.67

왜 중요할까?

  • 사용자 입장에서 "얼마나 빨리 끝났는가"를 체감하는 지표

  • CPU 성능 평가스케줄러의 효율성 측정에 사용됨

  • 특히 동시 프로세스 수가 늘어날 때 시스템이 얼마나 잘 대응하는지를 확인할 수 있음

한 마디로, "작업 완료까지 얼마나 걸렸는지"를 여러 작업에 대해 평균낸 값이에요!

실험 결과

  • 논리 CPU 수(8개)를 넘는 시점부터 지연 증가

  • 코어 수(4개) 이상에서 효과 감소

스루풋(Throughput)

  • 8개를 넘는 시점부터 처리량이 더는 늘지 않음

  • SMT가 모든 워크로드에 유리한 건 아님

추가정리 턴어라운드 타임 vs 스루풋

항목
평균 턴어라운드 타임
스루풋

의미

각 작업이 끝나는 데 걸린 평균 시간

일정 시간 안에 완료된 작업 수

단위

초 (s)

작업/초 (jobs/sec)

작을수록 좋음?

(작업 빨리 끝남)

(많은 작업 처리됨)

사용자 입장

“내 작업이 빨리 끝났나?”

“시스템이 얼마나 많이 일했나?”

예시 상황

문서 인쇄가 얼마나 빨리 끝났는지

프린터가 1분 동안 몇 개 인쇄했는지

  • 1분 동안 작업 6개가 끝났다면? → 스루풋 = 6 / 60초 = 0.1 작업/초

  • 프로세스 3개가 각각 5초, 10초, 15초 걸렸다면? → 평균 턴어라운드 타임 = (5+10+15)/3 = 10초

두 개념을 쉽게 비유하면…

지표
비유

평균 턴어라운드 타임

“주문한 음식이 내 테이블까지 오기까지 걸린 시간”

스루풋

“식당이 1시간에 요리를 몇 개나 완성했는가?”

요약하면:

  • 턴어라운드 타임은 개별 사용자 만족도

  • 스루풋은 전체 시스템의 생산성

두 지표는 서로 영향을 주고받습니다. 너무 많은 작업을 동시에 처리하려고 하면 스루풋은 오르지만, 각 작업의 턴어라운드 타임이 길어질 수 있어요. 따라서 적절한 균형이 필요합니다

✔️ SMT 성능 실험 결과

  • 논리 CPU 수가 많아도, 실제 병목은 물리 코어 수(예: 4개)를 넘기면서부터 발생.

  • SMT가 무조건 성능 향상으로 이어지진 않음 → 워크로드와의 상성 중요.

2. TLB(Translation Lookaside Buffer, 변환 색인 버퍼)

1) 왜 필요한가?

  • 프로세스가 가상 주소 → 물리 주소 변환할 때:

    1. 페이지 테이블 접근 → 주소 변환

    2. 물리 주소로 메모리 접근

  • 문제: 이 과정이 느리다!

2) 해결책 – TLB

  • TLB는 주소 변환 결과(가상→물리)를 캐싱한 영역

  • 페이지 테이블 접근을 생략하고, 바로 변환 가능

3) Huge Page와의 관계

  • Huge Page(4장 설명)는 페이지를 크게 묶어 사용

  • ➕ 페이지 수가 줄어듦 → 페이지 테이블도 작아짐 → TLB도 줄어듦

  • TLB Miss 줄고, 전체 성능 향상

📌 핵심 요약

항목
설명

SMT

CPU 내부 자원 공유로 논리 CPU 2개 이상 생성

효과

유휴 자원 활용하여 병렬 처리 성능 향상 가능

주의

자원 충돌 시 SMT가 오히려 역효과일 수 있음

TLB

주소 변환 캐시 → 메모리 접근 속도 개선

Huge Page

TLB의 효율을 높이고 페이지 테이블 부담 감소

내용 출처 : https://server-technology.tistory.com/450

page table은 메모리에 저장되는데, CPU는 프로세스를 사용하기 위해 메모리에 두 번 접근해야 합니다.

  1. 메모리에 저장된 page table에 접근

    1. 가상 주소를 물리 주소로 변환하기 위해 page table에 접근

  2. page table 기반으로 실제 메모리에 접근

    1. page 테이블에서 얻은 물리 주소를 통해 실제 물리 메모리에 접근

CPU에서 메모리에 접근하는 것은 시간이 소요되는 작업입니다. 따라서 메모리에 두 번의 접근이 필요하면 상당한 시간이 소요됩니다 이러한 문제를 해결하기 위해서는 메모리에 접근하는 횟수를 줄여야 합니다.

TLB와 메모리 접근 최적화 구조 정리

1. 왜 TLB가 필요한가?

단계
설명

CPU가 가상 주소를 접근하면...

해당 주소가 어떤 물리 주소인지 확인해야 함

이 작업을 위해선 page table에 접근 필요

그 다음에야 실제 메모리 접근 가능

즉, 메모리에 2번 접근해야 함 → 느림! → 이를 최적화하기 위한 장치가 TLB

2. TLB (Translation Lookaside Buffer)

TLB는 가상 주소와 물리 주소 간의 매핑 정보를 저장한 고속 캐시입니다.

💡 동작 원리

단계
설명

CPU는 가상 주소에 접근

먼저 TLB에 해당 페이지 번호가 있는지 조회

TLB Hit

바로 물리 주소 얻고 메모리에 접근 (빠름)

TLB Miss

→ Page Table 접근 → 물리 주소 얻음 → TLB에 등록 → 메모리 접근 (느림)

📦 TLB 구성

항목
설명

페이지 번호

가상 주소의 일부 (논리 주소의 Page Index)

프레임 번호

해당 페이지에 대응되는 물리 주소 (Physical Frame Index)

3. TLB Flush (초기화)

TLB Flush란? : TLB에 저장된 (page → frame) 매핑 정보를 전부 지우는 것

💡 왜 필요할까?

컨텍스트 스위칭이 발생하면, → 이전 프로세스의 TLB 정보가 현재 프로세스와 충돌할 수 있기 때문!

이유
설명

충돌 방지

서로 다른 프로세스는 독립된 가상 주소 공간을 가짐

변환 일관성 유지

이전 프로세스의 TLB 정보가 남아 있으면, 잘못된 주소 매핑이 발생할 수 있음

4. 컨텍스트 스위칭 과정에서의 TLB 동작

[1] 현재 프로세스의 상태 저장 (PCB)
[2] 타이머 인터럽트 발생 → Ready Queue 이동
[3] 🔄 TLB Flush (기존 매핑 정보 제거)
[4] 새로운 프로세스 상태 복원 (PCB → 레지스터)
[5] 새로운 프로세스가 사용하는 주소 → TLB에 새롭게 등록됨

"TLB는 가상 주소를 물리 주소로 빠르게 변환하기 위한 캐시이고, 컨텍스트 스위칭 시에는 혼란을 방지하기 위해 초기화된다.

추가정리 : 스레드 실행 방식 5가지

CPU에서 여러 스레드를 처리하는 다양한 방식을 시각적으로 비교한 자료입니다. 각각의 방식은 멀티스레드 실행 전략의 대표 예시이고, 시간(프로세서 사이클) 동안 스레드들이 어떤 방식으로 실행되는지를 보여줍니다.

1. Superscalar (슈퍼스칼라 구조)

  • 💡 단일 스레드에서 여러 명령어를 동시에 실행

  • 한 스레드(Thread 1)만 실행되며, 파이프라인이 여러 개여서 여러 명령을 병렬로 처리함.

  • ✅ 장점: 단일 스레드 성능 극대화

  • ❌ 단점: 명령어 병렬성이 낮으면 파이프라인이 놀게 됨 (idle slot 발생)

2. Fine-Grained Multithreading (세밀한 다중 스레딩)

  • 💡 매 사이클마다 실행할 스레드를 전환

  • Thread 1~5를 한 번씩 번갈아가며 실행 → 파이프라인을 쉬지 않고 계속 활용

  • ✅ 장점: 파이프라인 낭비가 적음

  • ❌ 단점: 각 스레드 실행 간격이 길어짐 → 지연 증가 가능

3. Coarse-Grained Multithreading (거친 다중 스레딩)

  • 💡 스레드 실행 중 stall(멈춤)이 생기면 다른 스레드로 전환

  • 예: Thread 1이 메모리 대기 → Thread 3으로 전환

  • ✅ 장점: context switch 적게 발생 → 오버헤드 줄임

  • ❌ 단점: stall 동안은 idle slot 발생 가능

4. Multiprocessing (다중 프로세서 구조)

  • 💡 여러 CPU 코어가 각자 스레드를 동시에 실행

  • Thread 1~4가 서로 다른 코어에서 병렬로 실행됨 (선 그어진 것)

  • ✅ 장점: 진정한 병렬 처리 (멀티코어)

  • ❌ 단점: 하드웨어 코어 수 제한 있음 → 전력 소모도 큼

5. Simultaneous Multithreading (SMT, 동시 멀티스레딩)

  • 💡 단일 코어가 여러 스레드를 동시에 처리

  • 각 사이클마다 사용 가능한 유닛에 스레드를 배정해서 동시에 처리

  • ✅ 장점: 유휴 자원을 최대한 활용함

  • ❌ 단점: 자원 충돌 발생 시 성능 저하 가능

📌 정리 요약

방식
특징
병렬성
자원 활용
스레드 전환 비용

Superscalar

단일 스레드 병렬 실행

낮음

낮음

없음

Fine-Grained

매 사이클 전환

중간

높음

높음

Coarse-Grained

Stall 발생 시 전환

중간

중간

낮음

Multiprocessing

다중 코어 병렬

높음

높음

없음

SMT

단일 코어 다중 스레드

높음

매우 높음

없음

페이지 캐시(Page Cache)

1. 페이지 캐시란?

✔ 정의

페이지 캐시는 리눅스 커널이 파일 I/O 성능을 높이기 위해 사용하는 메모리 캐시입니다.

  • 저장 장치(디스크)의 데이터는 바로 메모리로 들어오는 것이 아니라, 커널의 페이지 캐시에 먼저 들어와서 처리됩니다.

✔ 역할

  • CPU가 파일에 접근할 때마다 디스크에 직접 접근하면 속도가 느림.

  • 리눅스는 파일 데이터를 메모리에 캐싱해서 속도 향상.

  • 파일 읽기 = 페이지 캐시 확인 → 있으면 빠르게 메모리에서 제공.

  • 파일 쓰기 = 일단 페이지 캐시에 저장(→ 더티 페이지로 표시), 나중에 디스크에 반영(→ 라이트백).

✔️ 더티 페이지

  • 디스크와 내용이 달라진 캐시 페이지.

  • 특정 시점에 디스크에 반영됨.

  • 전원이 꺼지기 전에 기록되지 않으면 데이터 손실 가능성 → O_SYNC 플래그 사용 시 바로 기록 가능.

2. 페이지 캐시의 동작 원리

1) 파일 읽기 시 동작 흐름

단계
설명

프로세스가 파일을 읽음

커널이 먼저 페이지 캐시에 파일을 복사

페이지 캐시 → 프로세스 메모리로 복사

이후 동일한 파일을 읽을 경우, 디스크가 아닌 캐시에서 읽음 (훨씬 빠름)

📌 이것이 바로 "파일이 메모리에 올라왔다"는 표현의 의미입니다.

  1. 프로세스가 파일을 읽음

  2. 커널이 먼저 페이지 캐시에 파일을 복사

  3. 페이지 캐시 → 프로세스 메모리로 복사

  1. 커널은 페이지 캐시에 캐시한 영역의 관련 정보를 관리하는 영역을 커널 메모리 안에 둡니다

  1. 동일한 파일을 읽을 경우, 디스크가 아닌 캐시에서 읽음 (훨씬 빠름)

  1. 페이지에 캐시 쓰기 : 프로세스가 데이터를 파일에 쓰면 커널 페이지 캐시에만 데이터를 기록

이때 데이터 내용이 저장 장치에 있는 것보다 최신이라는 표시를 관리 영역 내부의 변경 된 페이지 관련 항목에 남깁니다.

이런 표시가 있는 페이지를 더티 페이지라고 부릅니다

2) 파일 쓰기 시 동작 흐름

단계
설명

프로세스가 파일을 씀

해당 내용은 일단 페이지 캐시에만 반영됨

일정 시간 후 실제 디스크에 기록됨 (→ 이를 Write Back이라고 함)

이 상태의 페이지를 더티 페이지(Dirty Page)라고 부름

🧨 만약 전원이 꺼지면 더티 페이지는 날아갈 수 있음!

3. 페이지 캐시의 문제점과 해결책

문제 상황
설명

갑작스러운 전원 차단

캐시에만 있고 디스크에 기록되지 않은 데이터가 손실될 수 있음

해결 방법

파일을 열 때 O_SYNC 플래그 사용 → 쓰는 즉시 디스크에도 기록 이 플래그를 사용하면 파일을 대상으로 write() 시스템 콜을 호출할 때 페이지 캐시뿐만 아니라 저장 장치에도 동시에 데이터를 기록 합니다

4. 실습: 캐시 효과 확인

[1] 파일 쓰기 속도 비교

# 동기화 쓰기 (느림)
$ dd if=/dev/zero of=testfile oflag=sync bs=1G count=1
# → 약 1.58초

# 일반 쓰기 (빠름 - 캐시 덕분)
$ dd if=/dev/zero of=testfile bs=1G count=1
# → 약 0.79초

→ 페이지 캐시 덕분에 약 2배 빠름

[2] 페이지 캐시 제거 (drop_caches)

# 캐시 사용량 확인
$ free

# 캐시 제거
$ sudo su
# echo 3 > /proc/sys/vm/drop_caches
# free

buff/cache 값이 4GiB → 200MiB로 줄어듦 (캐시 삭제 성공

[3] 읽기 속도 비교

# 첫 번째 읽기 (디스크에서 직접 읽음)
$ dd if=testfile of=/dev/null bs=1G count=1
# → 약 0.59초

# 두 번째 읽기 (페이지 캐시에서 읽음)
$ dd if=testfile of=/dev/null bs=1G count=1
# → 약 0.36초

약 40% 속도 향상: 페이지 캐시 덕분!

🔁 페이지 캐시 vs 캐시 메모리 비교

구분
캐시 메모리 (CPU)
페이지 캐시 (커널)

위치

CPU 내부

커널 메모리

대상

메모리 데이터

파일 시스템 데이터

단위

캐시라인

페이지 (보통 4KB)

더티 개념

있음

있음

동기화 방식

Write-through, Write-back

Write-back + sync 옵션

페이지 캐시는 저장 장치 I/O 성능을 향상시키기 위한 리눅스 커널의 캐시 메커니즘입니다. 읽기 성능 향상은 물론, 쓰기 시에도 디스크에 직접 쓰지 않고 캐시에 저장함으로써 전체 시스템의 I/O 속도를 극대화합니다.

추가 개념: /proc/sys/vm/drop_caches

  • 페이지 캐시를 강제로 삭제해서 캐시 미적용 상태를 만드는 설정 파일.

  • echo 3 > /proc/sys/vm/drop_caches 명령어로 실험 가능.

  • 실제 운영 환경에서는 잘 사용하지 않지만, 성능 분석이나 테스트에서는 유용.


🧠 버퍼 캐시, 더티 페이지, 직접 입출력 정리

1. 버퍼 캐시(Buffer Cache)

1) 정의

  • 디스크 블록의 메타 정보 및 비파일 데이터를 메모리에 캐시하는 영역입니다.

  • 대표적인 사용 예:

    • 파일 시스템을 사용하지 않고 디바이스 파일로 저장 장치에 직접 접근할 때

    • 파일 시스템 메타데이터(아이노드, 디렉터리 정보 등) 접근 시

  • 버퍼 캐시도 페이지 캐시와 마찬가지로 버퍼 캐시에 쓴 데이터가 아직 디스크에는 반영되지 않 은 더티 상태가 존재합니다

2) 페이지 캐시와의 차이

구분
페이지 캐시
버퍼 캐시

캐싱 대상

파일 데이터

파일 시스템 메타데이터 또는 비파일 디바이스

위치

커널 메모리

커널 메모리

특징

대용량 파일 처리에 특화

파일 시스템 메타 정보 및 디바이스 I/O 대응

예외

Btrfs는 메타데이터도 페이지 캐시로 통합

관련 블로그 글 : https://brunch.co.kr/@alden/25

2. 더티 페이지(Dirty Page)와 쓰기 타이밍

1) 더티 페이지란?

  • 페이지 캐시 또는 버퍼 캐시에 존재하는 데이터 중 디스크와 내용이 다른(아직 기록되지 않은) 데이터를 말합니다.

2) 쓰기 동작 흐름

프로세스 → 커널(페이지 캐시/버퍼 캐시) → 디스크

즉, write() 호출 시 디스크에 바로 쓰지 않고, 메모리 캐시에 먼저 저장됨.

3. 라이트백(Write Back) 동작 조건

1) 자동 쓰기 타이밍은 두 가지 기준

조건
설명
기본값

시간 기반

주기적으로 쓰기

vm.dirty_writeback_centisecs = 500 → 기본값은5초마다 1회

양 기준

더티 페이지가 많아질 경우

vm.dirty_background_ratio = 10%부터 백그라운드 쓰기 시작

2) 즉시 디스크로 쓰는 기준

  • 더티 페이지가 vm.dirty_ratio (기본 20%) 이상이 되면:

    • 파일 쓰기 호출이 동기적으로 디스크 기록을 수행함.

3) 관련 커널 파라미터 정리

파라미터
설명
기본값

vm.dirty_writeback_centisecs

자동 writeback 주기 (centiseconds 단위)

500 (5초)

vm.dirty_background_ratio

백그라운드 writeback 시작 비율

10

vm.dirty_ratio

write() 시 동기 쓰기 강제 시작 비율

20

vm.dirty_bytes, vm.dirty_background_bytes

비율 대신 바이트 단위로 지정할 경우 사용

0 (미설정)

📌 주의: 너무 낮게 설정하면 writeback이 과도하게 자주 발생해 시스템 성능 저하 or OOM 위험 발생.

4. 직접 입출력(Direct I/O, O_DIRECT)

1) 개념

  • 페이지 캐시나 버퍼 캐시를 전혀 사용하지 않고, 디스크에 직접 I/O를 수행

  • 커널 캐시를 우회하는 입출력 방식

2) 언제 사용하나요?

상황
이유

백업, 한 번만 읽고 쓸 파일

캐싱 효과 미미 + 캐시 낭비

앱 자체가 자체 캐시 구현한 경우

OS 캐시 중복 낭비 방지

대용량 처리에서 정확한 디스크 I/O 성능 측정이 필요할 때

캐시 간섭 없음

3) 사용 방법

$ dd if=/dev/zero of=testfile bs=1G count=1 oflag=direct,sync

oflag=direct: 페이지 캐시 사용하지 않음 sync: I/O 완료까지 기다림 (O_DIRECT는 기본적으로 비동기)

4) 실습 결과 비교

  • 일반 쓰기 → buff/cache 값 증가 (캐시 사용)

  • Direct I/O 쓰기 → 캐시 사용량 거의 변하지 않음

5. 주의 사항

  • 캐시 사용은 일반적인 상황에선 매우 효율적임

  • Direct I/O는 예외적인 상황에서만 사용하는 것이 좋음

  • 파일 시스템 마운트 중인 장치에 dd로 직접 접근 시, 페이지 캐시와 싱크가 안 맞아 정합성 문제 발생 가능

🔚 결론

리눅스 시스템은 페이지 캐시 + 버퍼 캐시를 통해 고속 I/O를 실현합니다. 하지만 캐시는 언제나 완벽한 건 아니며, 데이터 정합성 보장, 특수한 입출력 상황, 성능 테스트 목적에서는 직접 입출력과 커널 파라미터 조정이 필요할 수 있습니다.

🧠 스왑(Swap)과 스래싱(Thrashing), 그리고 페이지 폴트 흐름 정리

1. 스왑(Swap)이란?

1) 정의

  • 스왑은 물리 메모리가 부족할 경우, 일시적으로 저장 장치(HDD, SSD)의 일부 공간을 메모리처럼 사용하는 것입니다.

  • 이 공간을 "스왑 영역(Swap Area)"이라고 부릅니다.

2) 왜 필요한가?

  • OOM(Out Of Memory)을 지연시키고 방지하기 위해

  • 물리 메모리 부족 상황에서 덜 사용 중인 페이지를 디스크로 내보내고 메모리 공간을 확보함

2. 페이지 아웃(Page Out) & 페이지 인(Page In)

1) [Step1] 메모리가 가득 찬 상태

  • 프로세스 B가 새 메모리 공간을 요청했지만, 메모리가 가득 참

  • 페이지 폴트 발생

2) [Step2] 페이지 아웃(Page Out)

  • 커널이 오래 사용되지 않은 프로세스 A의 메모리 일부를 스왑 영역으로 내보냄(페이지 아웃, 스왑 아웃)

  • → 해당 물리 페이지는 비워지고 재사용 가능해짐

  • 그림에서는 프로세스 A의 가상 주소 100~200과 연결된 물리 주소 600~700의 페이지에 해당

  • 대피시킨 페이지의 스왑 영역 위치가 페이지 테이블 엔트리에 적혀 있는 것처럼 보이지만 실제로는 커널 메모리 안에 기록

3) [Step3] 새로운 프로세스에 메모리 할당

  • 비워진 공간을 프로세스 B에 할당

4) [Step4] 페이지 인(Page In), 스왑 인

  • 이후 프로세스 A가 스왑 영역에 있던 자신의 페이지에 접근하면,

  • 디스크에서 다시 메모리로 데이터를 불러옴 → Page In

3. 페이지 폴트의 종류

구분
설명

🟢 마이너 폴트(Minor Fault)

해당 페이지가 이미 메모리에 있지만, 프로세스의 주소 공간과 연결이 안 되어 있을 때

🔴 메이저 폴트(Major Fault)

해당 페이지가 메모리에 없고 디스크에서 Page In 해야 할 때

👉 vmstat, top, ps 등의 커맨드에서 majflt 또는 fault/s 항목으로 확인 가능

4. 스래싱(Thrashing)

1) 정의

  • 계속해서 Page In ↔ Page Out이 반복되는 상황

  • → 디스크 I/O가 과도하게 증가하며 시스템 전체가 느려짐 또는 정지(OOM 발생)

2) 원인

  • 사용 가능한 물리 메모리보다 프로세스들이 너무 많은 메모리를 요청

  • 작업량이 메모리 한계를 넘으면, 커널이 계속 페이지 교체만 하느라 실제 작업은 진행되지 않음

5. 스왑 사용은 만능일까?

🚫 문제점

  • 저장장치(HDD, SSD)의 접근 속도는 RAM보다 수십~수천 배 느림

  • 스왑을 너무 많이 쓰면:

    • I/O 병목 발생

    • 스래싱 유발

    • 결국 성능 저하 또는 OOM

✅ 권장 사항

  • 스왑은 임시 완충지일 뿐

  • 시스템이 자주 스왑을 사용하면:

    • 메모리 증설이 근본적인 해결책

6. 실습 예시 명령어 요약

1) 페이지 캐시 삭제 (drop_caches)

sudo sh -c "echo 3 > /proc/sys/vm/drop_caches"

2) 스왑 확인

free -h
swapon --show

3) 스왑 비활성화 & 활성화

sudo swapoff -a  # 비활성화
sudo swapon -a   # 활성화

🔚 결론 요약

항목
설명

스왑

RAM 부족 시 일시적으로 저장장치를 메모리처럼 사용

페이지 아웃

메모리에서 덜 사용 중인 페이지를 디스크로 내보냄

페이지 인

스왑에서 다시 RAM으로 불러옴

페이지 폴트

Page Table에 없는 주소 접근 시 발생

스래싱

메모리 과부하로 Page In/Out이 계속 반복되어 시스템이 마비되는 현상

해결 방법

메모리 증설, 스왑 사용량 제한, OOM 감지 및 설정 조정


📊 리눅스에서 메모리 캐시 및 스왑 통계 정보 정리

1. 메모리 상태 확인: sar -r

sar -r 명령어는 메모리 상태, 캐시, 더티 페이지 등을 실시간으로 모니터링할 수 있는 유용한 도구입니다.

필드명
설명

kbmemfree

비어 있는 물리 메모리 용량 (KiB)페이지 캐시나 스왑은 포함되지 않음

kbavail

실제로 사용 가능한 메모리 (캐시 포함)

kbbuffers

버퍼 캐시 용량 (파일 메타데이터 등)

kbcached

페이지 캐시 용량 (파일 데이터 캐시)

kbdirty

더티 페이지(디스크에 반영되지 않은 데이터)의 총 용량

주의할 점

  • kbdirty 값이 커진다면 곧 디스크에 write-back 작업이 실행될 수 있음

  • kbcachedkbbuffers는 시스템 성능에 직접적으로 영향을 미침

2. 페이지 I/O 상태 확인: sar -B

sar -B 명령어는 페이지 폴트, 페이지 인/아웃 등 메모리 페이지 상태를 보여줍니다.

필드명
설명

pgpgin/s

초당 페이지 인 수 (KiB)디스크 → 메모리 방향

pgpgout/s

초당 페이지 아웃 수 (KiB)메모리 → 디스크 방향

fault/s

초당 페이지 폴트 수 (Minor + Major)

majflt/s

초당 Major 폴트 수 (디스크 접근이 필요한 경우)

📌 활용 팁

  • majflt/s 값이 계속해서 높다면 스왑을 너무 자주 쓰고 있다는 의미로, 성능 저하의 원인일 수 있음

  • pgpgin/s, pgpgout/s가 높다면 페이지 캐시나 버퍼 캐시의 입출력이 많다는 뜻

3. 스왑 영역 상태 확인

현재 사용 중인 스왑 정보 확인: swapon --show

$ swapon --show
필드
설명

NAME

스왑 장치(파티션 또는 파일) 이름

TYPE

파티션인지 파일인지

SIZE

스왑 영역 총 크기

USED

현재 사용 중인 용량

PRIO

우선순위 (낮을수록 우선)

스왑 전체 사용량 확인: free

$ free -h
  • Swap: 행에서 사용량을 확인할 수 있습니다.

  • 스왑이 점점 차고 있으면 메모리 압박이 있다는 의미입니다.

4. 실시간 스왑 입출력 확인: sar -W

$ sar -W 1
필드
설명

pswpin/s

초당 스왑 인 수 (디스크 → 메모리)

pswpout/s

초당 스왑 아웃 수 (메모리 → 디스크)

활용 팁

  • 두 값 모두 0이 아닌 상태가 지속된다면, 스왑에 의한 I/O 병목이 발생 중일 수 있음

  • 특히 pswpout/s 값이 높을 경우 스레싱 가능성을 의심해야 함

5. 스왑 사용률 확인: sar -S

$ sar -S 1
필드
설명

kbswpfree

비어 있는 스왑 용량 (KiB)

kbswpused

사용 중인 스왑 용량 (KiB)

%swpused

스왑 사용률 (%)

kbswpcad

캐시된 스왑 용량

%swpcad

캐시된 스왑 비율

주의

  • kbswpused가 빠르게 증가하면 위험

  • %swpused가 50% 이상이라면 메모리 부족 상태가 의심됨

6. 요약 정리표

지표
설명
상태 확인 도구

페이지 캐시

파일 내용을 메모리에 저장한 캐시

sar -r, free, vmstat

버퍼 캐시

파일 메타데이터를 위한 캐시

sar -r, vmstat

스왑 사용률

물리 메모리 부족 시 사용하는 저장 장치 공간

free, sar -S, swapon --show

페이지 폴트

필요한 페이지가 메모리에 없을 때 발생

sar -B, vmstat, perf stat

스왑 입출력

메모리 ↔ 스왑 간의 데이터 이동

sar -W

Last updated