[Daily morning study] Huge Pages와 메모리 최적화

#daily morning study

Image


왜 Huge Pages가 필요한가

일반적인 x86-64 시스템에서 기본 페이지 크기는 4KB다. 가상 메모리 시스템은 페이지 테이블을 통해 가상 주소를 물리 주소로 변환하는데, 이 변환 결과를 캐시하는 것이 TLB(Translation Lookaside Buffer)다.

문제는 TLB 용량이 극히 제한적이라는 점이다. 일반적인 CPU의 L1 TLB는 64~128개 엔트리 정도다. 4KB 페이지 기준으로 128개 엔트리는 고작 512KB 범위만 커버한다. 대용량 메모리를 다루는 애플리케이션(데이터베이스, JVM 힙)에서 TLB 미스가 빈번하게 발생하고, 미스가 날 때마다 페이지 테이블 워크(page table walk)를 수행해야 해서 수십~수백 사이클의 지연이 생긴다.

Huge Pages는 페이지 크기 자체를 키워서 TLB 엔트리 하나가 커버하는 메모리 범위를 넓히는 방법이다.

페이지 크기TLB 128개 커버 범위
4KB (기본)512KB
2MB (Huge Page)256MB
1GB (Huge Page)128GB

Huge Pages 종류

표준 Huge Pages (Static Huge Pages / HugeTLBfs)

커널이 부팅 시점이나 관리자 설정으로 미리 예약해두는 방식이다. 명시적으로 mmap이나 shmget에 플래그를 지정한 애플리케이션만 사용할 수 있다.

# 2MB Huge Pages 512개 예약
echo 512 > /proc/sys/vm/nr_hugepages

# 현재 Huge Pages 상태 확인
grep -i huge /proc/meminfo
# HugePages_Total: 512
# HugePages_Free:  510
# HugePages_Rsvd:  2
# Hugepagesize:    2048 kB

애플리케이션에서 사용하는 방법:

// mmap으로 직접 할당
void *ptr = mmap(NULL, length,
                 PROT_READ | PROT_WRITE,
                 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
                 -1, 0);

// hugetlbfs 마운트 후 파일 기반 접근
// mount -t hugetlbfs none /mnt/hugetlbfs
int fd = open("/mnt/hugetlbfs/file", O_CREAT | O_RDWR, 0755);
void *ptr = mmap(NULL, length, PROT_READ | PROT_WRITE,
                 MAP_SHARED, fd, 0);

Transparent Huge Pages (THP)

리눅스 2.6.38부터 도입된 기능으로, 커널이 자동으로 일반 페이지들을 2MB Huge Page로 병합해주는 방식이다. 애플리케이션 수정 없이 자동으로 혜택을 받을 수 있다.

THP의 핵심은 khugepaged 커널 스레드다. 이 데몬이 주기적으로 메모리를 스캔해서 인접한 4KB 페이지들이 2MB 정렬 경계에 맞게 모여있으면 하나의 Huge Page로 합쳐버린다.

# THP 설정 확인 (always / madvise / never)
cat /sys/kernel/mm/transparent_hugepage/enabled

# always: 항상 자동으로 THP 사용
# madvise: madvise(MADV_HUGEPAGE) 힌트를 준 영역만 THP 적용
# never: THP 비활성화

# madvise 모드에서 특정 메모리 영역에 THP 요청
madvise(ptr, length, MADV_HUGEPAGE);

THP의 장단점

장점

  • 애플리케이션 수정 없이 자동 적용
  • TLB 커버리지 증가로 TLB 미스 감소
  • 대규모 연속 메모리 접근 패턴에서 성능 향상

단점

메모리 낭비: 2MB Huge Page를 사용하다가 일부만 필요해지면 나머지 공간이 낭비된다. 4KB 페이지는 필요한 만큼만 할당되지만, Huge Page는 2MB 단위로만 움직인다.

Compaction 오버헤드: 인접한 4KB 페이지들을 합치려면 메모리 콤팩션(compaction)이 필요하다. 이 과정에서 짧은 지연(stall)이 생길 수 있고, 레이턴시에 민감한 서비스에서 문제가 된다.

단편화: 장기간 운영하면 메모리가 단편화되어 2MB 연속 공간을 찾기 어려워진다.

# compaction 통계 확인
grep -i compact /proc/vmstat

# THP 실패 통계 확인
grep thp /proc/vmstat
# thp_fault_alloc: 성공적으로 THP 할당
# thp_collapse_alloc: khugepaged가 성공적으로 합침
# thp_fault_fallback: THP 할당 실패 → 4KB로 폴백

표준 Huge Pages vs THP 비교

항목표준 Huge PagesTHP
설정관리자가 수동 예약커널이 자동 관리
애플리케이션 변경필요 (MAP_HUGETLB 등)불필요 (always 모드)
레이턴시 영향없음 (미리 예약)있음 (compaction 오버헤드)
메모리 효율예약 후 미사용 시 낭비필요할 때만 합침
1GB 페이지 지원가능불가 (2MB만)
주 사용 사례Oracle DB, DPDK, HPC일반 서버 애플리케이션

실제 활용 사례

Oracle Database

오라클 DB는 SGA(System Global Area)를 표준 Huge Pages로 잡는 것을 공식 권장한다. SGA는 수십 GB에 달하는 연속된 공유 메모리 영역이기 때문에 4KB 페이지로 운영하면 페이지 테이블 크기가 엄청나게 커지고 TLB 미스도 급증한다.

# Oracle용 Huge Pages 계산 예시
# SGA 크기: 16GB → 16 * 1024 / 2 = 8192개 2MB 페이지 필요
echo 8192 > /proc/sys/vm/nr_hugepages

JVM (Java Virtual Machine)

JVM 힙을 Huge Pages로 잡으면 GC 성능이 개선된다.

# JVM에서 Huge Pages 활성화
java -XX:+UseLargePages \
     -XX:+UseTransparentHugePages \
     -Xmx16g \
     MyApplication

Redis

Redis처럼 레이턴시에 민감한 서비스에서는 THP를 끄는 것이 일반적인 권장 사항이다. THP의 compaction 오버헤드가 예측 불가능한 지연을 유발하기 때문이다.

# Redis 권장 설정 (THP 비활성화)
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag

1GB Huge Pages

x86-64 시스템에서는 2MB 외에도 1GB 페이지를 지원한다 (CPU의 pdpe1gb 플래그 필요). HPC나 DPDK 같이 TB 단위 메모리를 다루는 환경에서 사용한다.

# GRUB 부팅 파라미터로 1GB 페이지 예약
# /etc/default/grub에 추가:
# GRUB_CMDLINE_LINUX="hugepagesz=1G hugepages=16"

# 확인
grep -i huge /proc/meminfo
# Hugepagesize: 1048576 kB  ← 1GB 모드

성능 분석

perf로 TLB 미스 개선 효과를 측정할 수 있다.

# TLB 관련 하드웨어 이벤트 측정
perf stat -e dTLB-loads,dTLB-load-misses,iTLB-loads,iTLB-load-misses \
    ./my_application

# Huge Pages 적용 전후 TLB 미스율 비교
# 미스율 = dTLB-load-misses / dTLB-loads * 100

대용량 메모리를 연속적으로 스캔하는 워크로드에서는 TLB 미스율이 수십 % → 1% 미만으로 줄어드는 경우도 있다.


정리

  • 4KB 기본 페이지의 TLB 미스 문제를 Huge Pages(2MB, 1GB)로 완화할 수 있다.
  • 표준 Huge Pages: 수동 예약, 예측 가능한 성능, 레이턴시 민감 서비스에 적합
  • THP: 자동 관리, 편리하지만 compaction 오버헤드로 레이턴시 지터 발생 가능
  • 데이터베이스, JVM은 Huge Pages에서 성능 이점이 크고, Redis 등 레이턴시 민감 서비스는 THP를 끄는 게 일반적이다.
  • 실제 효과는 반드시 perf로 측정해서 판단해야 한다.