‘페이지 매핑 단위가 커지면 관리할 페이지 수가 줄어드니 TLB 효율이 좋아져서 성능이 빨라지겠지’라는 개념적 사실만 알고, 리눅스에서 이것이 어떻게 구현되는지 찾아볼 기회가 없었습니다. 현대 리눅스 커널은 이 거대한 페이지들을 구체적으로 어떻게 구현해서 관리하고 있는지, 그리고 과연 어떤 상황에서 켜는 게 정답인지에 대한 운영 가이드를 정리해 봅니다.

1. 4KB의 한계와 Huge Pages의 등장

리눅스 커널의 기본 메모리 관리 단위는 4KB입니다. 하지만 64GB 이상의 대용량 메모리를 사용하는 환경에서는 이 단위가 시스템에 큰 부담이 됩니다.

  • 기본 페이지(4KB): 64GB를 4KB로 쪼개면 약 1,600만 개의 페이지를 관리해야 합니다.
  • Huge Pages(2MB): 관리 단위가 512배 커지면서 페이지 수가 약 3만 개로 확 줄어듭니다.

핵심: TLB 캐시 효율

CPU가 메모리에 접근할 때 가상 주소를 물리 주소로 바꾸는 매핑 정보를 담아두는 고속 캐시를 **TLB(Translation Lookaside Buffer)**라고 합니다.

  • TLB Miss: 관리해야 할 페이지가 너무 많으면 캐시 용량이 부족해 자꾸 미스가 나고, CPU는 주소 변환을 위해 느린 RAM을 뒤져야 합니다.
  • 성능 향상: 2MB 페이지를 쓰면 하나의 TLB 엔트리가 커버하는 범위가 넓어져 히트율이 극적으로 올라갑니다. 메모리 집중적인 작업(특히 DB)에서 10~30% 성능 향상을 가져오는 핵심 이유입니다.

2. 세상에 공짜는 없다: 트레이드오프(Trade-offs)

성능 향상 뒤에는 반드시 지불해야 할 비용이 있습니다.

  1. 내부 파편화: 1KB만 저장해도 2MB를 점유하므로, 작은 데이터를 다루는 프로세스가 많으면 메모리 낭비(Bloat)가 생깁니다.
  2. CoW(Copy-on-Write) 비용: 단 1바이트를 수정하려 해도 2MB 전체를 복사해야 하므로 fork()가 잦은 앱에선 불리할 수 있습니다.
  3. 할당 지연(Stall): 연속된 2MB 물리 공간을 확보하기 위해 커널이 메모리를 재배치(Compaction)하는 과정에서 시스템이 일시적으로 멈칫할 수 있습니다. (후술할 THP의 경우)

3. 커널의 두 가지 구현 방식: SHP vs THP

리눅스는 **‘누구를 위해, 어떻게 할당하느냐’**에 따라 두 가지 메모리 풀(Pool)을 운영합니다.

① Standard Huge Pages (SHP, 정적 할당)

  • 개념: 부팅 시 설정을 통해 미리 물리 메모리를 떼어놓는 **“전용 예약석”**입니다.
  • 구현: 독립된 전용 풀을 사용하며 일반 프로세스는 접근할 수 없습니다. 스왑(Swap)되지 않는 안정성을 보장합니다.
  • 전략: 앱이 명시적으로 요청할 때만 할당됩니다. 성능에 사활을 거는 앱(DB 등)이 운영의 번거로움을 감수하고 선택합니다.

② Transparent Huge Pages (THP, 동적 할당)

  • 개념: 일반 4KB 풀에서 커널 데몬(khugepaged)이 상황을 봐가며 합쳐주는 **“가변 자율석”**입니다.
  • 구현: 별도 예약 없이 일반 풀을 공유하며, 앱이 모르는 사이에 커널이 투명하게 처리합니다.
  • madvise 모드 (권장): 시스템 전체에 강제하는 대신, 앱이 madvise(MADV_HUGEPAGE) 힌트를 준 영역에 대해서만 최선을 다해 합쳐주는 Best Effort 방식입니다.

4. SHP vs THP(madvise) 비교

비교 항목 Standard Huge Pages (SHP) THP (madvise 모드)
확보 시점 부팅/설정 시 (정적) 런타임 요청 시 (동적)
메모리 풀 독립된 전용 풀 (Isolated) 일반 4KB 풀 공유 (Shared)
요청 방식 명시적 할당 요청 필요 madvise() 힌트 제공
보장성 100% 보장 (예약된 만큼) Best-effort (공간 없으면 실패)
특징 메모리 낭비 위험 있으나 확실한 성능 낭비는 적으나 합치는 오버헤드 존재

5. 운영 결론: 언제 무엇을 쓸 것인가?

운영의 핵심은 **“메모리 낭비를 줄이면서 필요한 곳에 성능을 몰아주는 것”**입니다.

  1. 데이터베이스 (PostgreSQL, MySQL 등):
    • 권장: SHP(Standard Huge Pages)
    • 이유: DB는 고정된 크기의 대규모 공유 메모리(Shared Buffer)를 사용합니다. 미리 자리를 확보하고 이를 사용하도록 강제하여 페이지 테이블 오버헤드를 원천 차단하는 것이 가장 안정적입니다. 성능 최적화를 위해 운영의 번거로움을 기꺼이 지불하는 선택입니다.
  2. 일반적인 워크로드 및 개인 장비:
    • 권장: THP(madvise)
    • 이유: 메모리 낭비를 최소화하면서, 대용량 메모리가 필요한 앱이 요청할 때만 커널이 ‘최선을 다해’ 도와주는 방식이 효율적입니다. 굳이 예약석을 비워두어 자원을 놀릴 필요가 없습니다.

마치며

공유메모리와 실제 리눅스 운영에서 어떤 값을 봐야 하는지 등 충분히 다루지 못 한 부분이 많습니다. 오늘은 이정도 마무리하고, 다음에 기회가 있으면 오늘 다룬 부분에서 빠진 부분을 살펴보겠습니다.