성능 최적화 (Performance Optimization)

Linux 커널 성능 최적화를 위한 계측 중심 실전 가이드입니다. perf(annotate/c2c/diff/bench), PMU(PEBS/IBS), ftrace/tracepoints, Flame Graph, io_uring 분석을 조합해 CPU·메모리·I/O 병목을 찾아내고, 스케줄러/락 경합/캐시 미스/NUMA 불균형을 정량화하며, 벤치마크 설계부터 회귀 검증까지 포함한 재현 가능한 튜닝 절차를 단계적으로 다룹니다.

전제 조건: ftrace/TracepointsCPU 캐시 문서를 먼저 읽으세요. 최적화는 추측이 아니라 계측 기반 반복 작업이므로, 병목 위치와 지표를 먼저 고정한 뒤 변경해야 회귀를 줄일 수 있습니다.
일상 비유: 이 주제는 레이싱 차량 세팅과 비슷합니다. 출력만 높이면 안정성이 떨어질 수 있듯이, 커널 최적화도 처리량·지연·안정성 균형을 함께 맞춰야 합니다.

핵심 요약

  • 전제 결합 — 보안, 성능, 아키텍처 지식을 함께 적용합니다.
  • 경계 명확화 — API 경계와 ABI 영향 범위를 먼저 확인합니다.
  • 위험 관리 — UAF, race, side-effect 가능성을 우선 점검합니다.
  • 계측 기반 판단 — 추측 대신 데이터로 개선 여부를 판단합니다.
  • 점진 적용 — 실험 범위를 작게 시작해 단계적으로 확장합니다.

단계별 이해

  1. 가설 수립
    문제와 개선 목표를 수치로 정의합니다.
  2. 제약 분석
    호환성, 안정성, 보안 제약을 먼저 확인합니다.
  3. 실험 적용
    최소 변경으로 효과와 부작용을 측정합니다.
  4. 정식 반영
    검증된 변경만 문서화해 반영합니다.
관련 표준: Intel SDM (PMU, 성능 카운터), AMD APM (IBS, 성능 모니터링) — 커널 성능 프로파일링에서 참조하는 하드웨어 성능 모니터링 규격입니다. 종합 목록은 참고자료 — 표준 & 규격 섹션을 참고하세요.

성능 최적화 개요

커널 성능 최적화는 병목 지점을 정확히 파악하는 것에서 시작합니다. Linux는 perf, ftrace, BPF 등 강력한 프로파일링/추적 도구를 기본 제공하며, 이 도구들을 조합한 체계적 워크플로가 필요합니다.

성능 최적화 반복 워크플로 1. 측정 perf stat · vmstat PSI · PCM 2. 식별 perf record · Flame Graph bcc tools · ftrace 3. 분석 perf annotate · toplev perf c2c · lockstat 4. 최적화 코드 변경 · sysctl CONFIG · 구조체 정렬 5. 검증 (perf diff · 벤치마크 회귀 테스트) → 반복 핵심 원칙: 추측하지 말고 측정하라 (Don't guess, measure!) — 모든 변경은 데이터로 검증

성능 모니터링 원리 (PMU)

커널 성능 분석의 기반은 CPU에 내장된 PMU(Performance Monitoring Unit)입니다. PMU는 하드웨어 수준에서 CPU 이벤트를 카운팅하는 전용 회로로, 소프트웨어 오버헤드 없이 정밀한 성능 데이터를 수집합니다.

PMU 동작 메커니즘

구성 요소역할예시
성능 카운터 레지스터특정 이벤트 발생 횟수를 카운팅x86: PMC0~PMCn (일반적으로 4~8개)
이벤트 선택 레지스터카운팅할 이벤트 종류를 지정cycles, instructions, cache-misses, branch-misses
오버플로 인터럽트카운터가 지정값에 도달하면 인터럽트 발생PMI(Performance Monitoring Interrupt)

샘플링 vs 카운팅

perf는 두 가지 방식으로 PMU를 활용합니다:

ℹ️

이벤트 멀티플렉싱: 하드웨어 PMC 수(보통 4~8개)보다 많은 이벤트를 동시에 모니터링하면, 커널이 시분할로 PMC를 전환합니다. 이 경우 결과에 추정값(<not counted> 또는 스케일링 계수)이 포함될 수 있습니다.

프로파일링 방법론

체계적인 성능 분석 워크플로는 다음 순서를 따릅니다:

  1. 전체 지표 수집 (perf stat): IPC(Instructions Per Cycle), 캐시 미스율, 분기 예측 실패율 등 시스템 전체의 성능 특성을 파악합니다. IPC < 1이면 메모리/I/O 바운드, IPC > 2이면 컴퓨트 바운드 가능성이 높습니다.
  2. 핫스팟 식별 (perf record + Flame Graph): CPU 시간 소모가 집중되는 함수와 콜 체인을 식별합니다.
  3. 정밀 분석 (perf annotate, ftrace): 핫스팟 함수의 명령어별 사이클 분포, 함수 호출 흐름, 지연 원인을 파악합니다.
  4. 가설 검증 및 최적화: 원인에 맞는 최적화를 적용하고 다시 측정하여 효과를 검증합니다.

USE 방법론

Brendan Gregg의 USE(Utilization, Saturation, Errors) 방법론은 모든 하드웨어 리소스에 대해 세 가지 지표를 체계적으로 점검하여 병목을 빠르게 좁힙니다.

USE 방법론: 리소스별 3가지 점검 리소스 Utilization (사용률) Saturation (포화도) Errors (에러) CPU mpstat, top (%cpu) perf stat (IPC) vmstat r열 (런큐 길이) runqlat, PSI cpu perf stat (machine clears) MCE 로그 메모리 free, /proc/meminfo pcm-memory (BW) vmstat si/so (swap) PSI memory, oomkill edac-util (ECC 에러) dmesg (mce, OOM) 디스크/SSD iostat %util sar -d iostat avgqu-sz (큐 깊이) biolatency, PSI io smartctl (SMART 에러) /sys/block/*/stat 네트워크 sar -n DEV (BW) ip -s link ifconfig (overruns, drops) ss -s (backlog), softnet_stat ip -s link (errors, dropped) ethtool -S (NIC 에러)

TSA 방법론 (Thread State Analysis)

워크로드의 스레드가 어떤 상태에서 시간을 소비하는지를 분석합니다. On-CPU(실행 중), Runnable(대기 중), Sleep(블로킹) 시간의 분포를 보면 병목 유형이 즉시 드러납니다.

스레드 상태의미진단 도구최적화 방향
On-CPUCPU에서 실행 중perf record (On-CPU Flame Graph)알고리즘, 컴파일러 최적화, SIMD
Runnable실행 가능하나 CPU 대기runqlat, perf sched latencyCPU 추가, 친화도, 부하 분산
Sleep (I/O)I/O 완료 대기offcputime, biolatencyI/O 최적화, 캐싱, 비동기 I/O
Sleep (Lock)잠금 획득 대기perf lock, offcputime잠금 세분화, RCU, lockless
Sleep (Network)네트워크 응답 대기tcplife, offcputime연결 풀, 비동기, 배치 처리
Idle작업 없음mpstat (%idle)더 많은 작업 할당, 병렬화
💡

방법론 선택: USE 방법론은 "어떤 리소스에 문제가 있나"를 빠르게 좁히고, TSA는 "스레드가 왜 느린가"를 분석합니다. 실전에서는 USE로 리소스 병목을 먼저 파악한 후, 해당 리소스에서 TSA/Flame Graph로 근본 원인을 추적합니다.

perf 종합 가이드

perf는 Linux 커널에 내장된 성능 분석 프레임워크로, perf_event_open(2) 시스템 콜을 통해 PMU 하드웨어 카운터, 소프트웨어 이벤트, tracepoint, kprobe/uprobe 등을 통합적으로 활용합니다. 위의 PMU 원리 섹션에서 설명한 카운팅/샘플링 모드를 실전에서 사용하는 도구입니다.

설정 및 권한

perf를 사용하려면 커널 설정과 시스템 권한이 필요합니다:

# 필수 커널 설정
CONFIG_PERF_EVENTS=y          # perf_event 서브시스템
CONFIG_HW_PERF_EVENTS=y       # 하드웨어 PMU 지원
CONFIG_DEBUG_INFO=y           # 심볼/소스 매핑 (annotate, probe)
CONFIG_DEBUG_INFO_BTF=y       # BPF 타입 정보 (선택)
CONFIG_KALLSYMS_ALL=y         # 모든 커널 심볼 (권장)

# 설치 (배포판별)
sudo apt install linux-tools-$(uname -r)   # Debian/Ubuntu
sudo dnf install perf                       # Fedora/RHEL
perf_event_paranoid허용 범위설명
-1모든 이벤트, 모든 사용자제한 없음 (개발/테스트 환경)
0커널+사용자 공간CPU 이벤트 허용, raw tracepoint 제한
1사용자 공간만커널 프로파일링에 root 또는 CAP_PERFMON 필요 (배포판 기본값은 상이)
2사용자 공간 (CPU 제한)시스템 전체 모니터링 불가
3거부perf_event_open 완전 차단
# 현재 설정 확인 및 변경
cat /proc/sys/kernel/perf_event_paranoid
sudo sysctl kernel.perf_event_paranoid=-1    # 임시 해제

# Linux 5.8+: CAP_PERFMON capability (root 대신 사용)
sudo setcap cap_perfmon+ep /usr/bin/perf

perf list: 이벤트 탐색

perf가 지원하는 이벤트를 확인합니다. 이벤트는 카테고리별로 분류됩니다:

카테고리예시소스
Hardwarecycles, instructions, cache-misses, branch-missesPMU 카운터
Softwarecontext-switches, page-faults, cpu-migrations커널 소프트웨어 카운터
CacheL1-dcache-load-misses, LLC-load-missesPMU 캐시 이벤트
Tracepointsched:sched_switch, block:block_rq_issue커널 tracepoint
PMUcpu/event=0xc0,umask=0x01/플랫폼 특정 PMU
RawrNNN (예: r003c = unhalted cycles)하드웨어 인코딩 직접 지정
# 전체 이벤트 목록
perf list

# 카테고리별 필터
perf list hw              # 하드웨어 이벤트만
perf list sw              # 소프트웨어 이벤트만
perf list cache           # 캐시 이벤트
perf list tracepoint      # tracepoint 이벤트
perf list pmu             # PMU 특정 이벤트

# 키워드 검색
perf list | grep -i 'branch'

# Raw 이벤트: Intel SDM/AMD APM에서 인코딩 확인
# rNNN 형식: r + EventSelect(8bit) + UnitMask(8bit)
perf stat -e r003c ./program   # CPU_CLK_UNHALTED.CORE

perf stat: 이벤트 카운팅 심화

perf stat은 카운팅 모드로 이벤트 총계를 수집합니다. 기본 사용법은 디버깅 페이지를 참조하고, 여기서는 분석적 해석에 집중합니다.

# 상세 카운터 (-d: L1, -dd: L1+LLC, -ddd: L1+LLC+TLB+분기)
perf stat -ddd ./program

# 반복 측정으로 통계적 신뢰도 확보
perf stat -r 5 -e cycles,instructions ./program
# 출력: 평균 ± 표준편차 (stddev)

# 인터벌 모드: 1초 간격으로 카운터 출력 (추세 분석)
perf stat -I 1000 -e cycles,instructions -a

# 이벤트 그룹: 동일 PMC 타임슬라이스에서 측정 (비율 정확도 향상)
perf stat -e '{cycles,instructions}' ./program

# CPU별 통계
perf stat -e cycles -A -a -- sleep 5
💡

IPC 해석 가이드:

  • IPC < 1.0: 메모리/I/O 바운드 가능성 — 캐시 미스, TLB 미스, 메모리 대역폭 포화 확인
  • IPC 1.0~2.0: 혼합 워크로드 — 분기 예측 실패, 파이프라인 스톨 확인
  • IPC > 2.0: 컴퓨트 바운드 — 알고리즘/SIMD 최적화가 효과적

cache-misses / cache-references 비율이 5% 이상이면 캐시 최적화가 필요합니다.

perf record / report: 샘플링 프로파일링

perf record는 샘플링 모드로 프로파일 데이터를 perf.data 파일에 기록하고, perf report로 분석합니다.

# 샘플링 주파수 vs 주기
perf record -F 99 ./program          # -F: 초당 99 샘플 (주파수)
perf record -c 100000 ./program      # -c: 100000 이벤트마다 1 샘플 (주기)
# -F 99: 커널이 주파수를 유지하도록 주기를 자동 조절 (권장)
# -c: 정확한 이벤트 수 기준 (이벤트 빈도가 변할 때 유용)

# 콜 그래프 포함 기록 (다음 섹션에서 방법 비교)
perf record -g -F 99 -p $(pidof myapp) -- sleep 30

# 시스템 전체 + 커널 심볼
perf record -ag -F 99 -- sleep 10

# report 주요 옵션
perf report                          # TUI 모드 (대화식)
perf report --stdio                  # 텍스트 출력 (스크립트/로그용)
perf report --hierarchy              # 콜 체인 계층 구조
perf report --sort=dso,sym           # 라이브러리/심볼별 정렬
perf report --percent-limit=1        # 1% 미만 항목 필터링

콜 그래프 수집 방법 비교

perf record -g로 콜 그래프(콜 스택)를 수집할 때 세 가지 방법을 선택할 수 있으며, 각각 트레이드오프가 다릅니다:

방법옵션오버헤드정확도스택 깊이요구사항
Frame Pointer (fp)--call-graph fp최소중간무제한-fno-omit-frame-pointer 컴파일
DWARF--call-graph dwarf높음높음무제한CONFIG_DEBUG_INFO, 디버그 심볼
LBR--call-graph lbr최소높음8~32단계Intel CPU (Haswell+), CONFIG_PERF_EVENTS_INTEL_LBR
# Frame Pointer: 빠르지만 -fno-omit-frame-pointer로 빌드된 코드 필요
perf record --call-graph fp -F 99 ./program

# DWARF: 가장 정확하지만 perf.data 크기 큼 (스택 덤프 저장)
perf record --call-graph dwarf,32768 -F 99 ./program
# 32768 = 스택 덤프 크기 (기본 8192, 깊은 스택이면 증가)

# LBR: Intel CPU에서 하드웨어 지원, 낮은 오버헤드 + 높은 정확도
perf record --call-graph lbr -F 99 ./program
💡

콜 그래프 방법 선택 가이드: 커널 프로파일링은 fp가 기본(커널은 frame pointer 사용). 사용자 공간은 dwarf가 가장 정확하며, Intel 환경에서 오버헤드가 문제면 lbr을 선택하세요. -g만 쓰면 기본값은 fp입니다.

perf annotate: 명령어 수준 분석

perf annotate는 핫스팟 함수의 어셈블리/소스 코드에 샘플 분포를 매핑하여, 어떤 명령어가 가장 많은 사이클을 소모하는지 보여줍니다.

# 기본 사용법 (perf record 후)
perf record -g -F 99 ./program
perf annotate                         # TUI: 심볼 선택 → 명령어별 비율
perf annotate --symbol=hot_function   # 특정 함수 직접 지정

# 소스 코드 인터리빙 (CONFIG_DEBUG_INFO 필요)
perf annotate --source               # 소스 + 어셈블리 동시 표시
perf annotate --no-source            # 어셈블리만 표시

# stdio 출력 (스크립트/분석용)
perf annotate --stdio --symbol=copy_page

annotate 출력 예시 — 각 어셈블리 명령어 앞에 해당 명령어의 샘플 비율(%)이 표시됩니다:

       │   copy_page():
  0.50 │   mov    (%rsi),%rax
 45.20 │   mov    %rax,(%rdi)      ← 캐시 미스로 스톨 (핫 명령어)
  0.30 │   mov    0x8(%rsi),%rax
  2.10 │   mov    %rax,0x8(%rdi)
         │   ...
ℹ️

핫 명령어 해석: mov 명령어에 높은 비율이 나타나면 대개 캐시 미스(메모리 스톨)가 원인입니다. 이때 perf stat -e L1-dcache-load-misses와 함께 확인하세요. div/idiv에 높은 비율이면 정수 나눗셈 비용, 분기 명령에 높으면 분기 예측 실패를 의심합니다.

perf top: 실시간 모니터링

perf toptop 명령어처럼 실시간으로 CPU를 가장 많이 소모하는 함수를 보여줍니다.

# 시스템 전체 실시간 프로파일링
perf top                              # 기본: cycles 이벤트
perf top -g                           # 콜 그래프 포함

# 특정 프로세스 / 이벤트 필터링
perf top -p $(pidof myapp)            # PID 기반
perf top -e cache-misses              # 캐시 미스 기준 정렬

# TUI에서 심볼 선택 후 Enter → annotate 뷰 (명령어별 분석)
# 's' 키: 심볼 필터, 'K': 커널 심볼 토글

perf trace: 시스템 콜 추적

perf tracestrace와 유사하지만, 커널 tracepoint 기반으로 동작하여 오버헤드가 훨씬 낮습니다.

특성straceperf trace
메커니즘ptrace (프로세스 중단)perf_event tracepoint
오버헤드높음 (2~10x 느려짐)낮음 (<5%)
시스템 전체PID별만 가능-a로 시스템 전체
통계 모드-c-s (더 상세)
이벤트 확장시스콜만시스콜 + page-fault + 기타 이벤트
# 기본 사용법
perf trace ./program                  # 새 프로세스 추적
perf trace -p $(pidof myapp)          # 실행 중인 프로세스

# 필터링
perf trace -e open,read,write ./program      # 특정 시스콜만
perf trace --duration 10 -p 1234             # 10ms 이상 소요된 콜만

# 요약 모드: 시스콜별 횟수/시간 통계
perf trace -s ./program
# 출력: syscall  calls  errors  total(ms)  min(ms)  avg(ms)  max(ms)

# 시스템 전체 + page fault 포함
perf trace -a -e 'major-faults,minor-faults' -- sleep 5

perf c2c: False Sharing 탐지

perf c2c(cache-to-cache)는 HITM(Hit in Modified) 이벤트를 분석하여, 여러 CPU 코어가 동일 캐시라인을 경쟁적으로 수정하는 false sharing을 탐지합니다.

# 1단계: 메모리 접근 샘플 기록
perf c2c record -a -- sleep 10
# 또는 특정 프로세스
perf c2c record -p $(pidof myapp) -- sleep 10

# 2단계: HITM 분석 리포트
perf c2c report --stdio
# Shared Data Cache Line Table에서:
# - Rmt HITM: 원격 소켓에서 수정된 라인 접근 (가장 비쌈)
# - Lcl HITM: 같은 소켓 내 다른 코어에서 수정된 라인 접근

# 3단계: 핫 캐시라인의 오프셋 + 데이터 소스 확인
# → struct 멤버 분리 또는 ____cacheline_aligned 적용
ℹ️

캐시 코히런시 프로토콜(MESI/MOESI)과 false sharing의 원리, ____cacheline_aligned 해결 기법 등 상세 내용은 CPU 캐시 — False Sharing 페이지를 참고하세요.

perf diff: 프로파일 비교

perf diff는 두 개의 perf.data 파일을 비교하여 최적화 전후의 성능 변화를 정량적으로 분석합니다.

# A/B 테스트 워크플로

# 1. 최적화 전 프로파일 기록
perf record -g -F 99 -o baseline.data -- ./program_v1

# 2. 최적화 후 프로파일 기록
perf record -g -F 99 -o optimized.data -- ./program_v2

# 3. 비교 분석
perf diff baseline.data optimized.data
# 출력 컬럼:
#   Baseline  Delta    Symbol
#   --------  -----    ------
#   15.30%    -8.20%   hot_function    ← 8.2% 감소 (개선)
#    3.10%    +2.50%   new_function    ← 2.5% 증가 (회귀)

# delta 기준 정렬
perf diff --sort=delta baseline.data optimized.data

perf bench: 내장 벤치마크

perf bench는 커널/하드웨어 서브시스템의 기본 성능을 측정하는 마이크로벤치마크 모음입니다. 시스템 설정 변경이나 커널 업그레이드 전후 비교에 유용합니다.

카테고리벤치마크측정 대상
schedmessaging, pipe스케줄러 컨텍스트 스위치, IPC 성능
memmemcpy, memset메모리 대역폭
numamemNUMA 노드 간 메모리 접근 지연
futexhash, wake, requeue, lock-pifutex 서브시스템 성능
epollwait, ctlepoll 이벤트 처리 성능
# 스케줄러: 메시지 패싱 성능 (프로세스/스레드 간)
perf bench sched messaging -g 20 -t -l 1000
# -g 20: 20개 그룹, -t: 스레드 사용, -l 1000: 1000회 반복

# 메모리: memcpy 대역폭
perf bench mem memcpy -s 4GB -l 5

# futex: 잠금 성능 (nthread별)
perf bench futex lock-pi -t 8

# NUMA: 노드 간 메모리 접근
perf bench numa mem -p 4 -t 4 -P 1024 -T 0

# 전체 벤치마크 실행
perf bench all

Intel/AMD 플랫폼별 PMU

범용 이벤트(cycles, instructions) 외에, 플랫폼별 PMU 확장 기능을 활용하면 더 정밀한 분석이 가능합니다.

Intel PEBS (Precise Event-Based Sampling)

PEBS는 이벤트 발생 시점의 정확한 IP(Instruction Pointer)를 하드웨어가 기록합니다. 일반 샘플링의 "skid"(인터럽트 지연으로 인한 IP 부정확) 문제를 해결합니다.

# PEBS 이벤트: 이벤트명 뒤에 :p 또는 :pp 수정자
# :p = precise level 1, :pp = precise level 2 (최대 정밀도)
perf record -e cycles:pp -F 99 ./program
perf record -e mem-loads:pp -F 97 ./program   # 메모리 로드 정밀 샘플링

# PEBS + annotate로 정확한 명령어별 분석
perf annotate --symbol=hot_function

AMD IBS (Instruction-Based Sampling)

IBS는 명령어 파이프라인에서 직접 샘플링하여 PEBS와 유사한 정밀도를 제공합니다. Fetch와 Op 두 가지 모드가 있습니다.

# AMD IBS Fetch 샘플링
perf record -e ibs_fetch// -c 100000 ./program

# AMD IBS Op 샘플링 (실행된 마이크로-op 기반)
perf record -e ibs_op// -c 100000 ./program

# IBS + 메모리 접근 분석
perf mem record -- ./program    # AMD에서 자동으로 IBS 사용

플랫폼별 유용 이벤트

이벤트플랫폼용도
mem-loads:pp, mem-stores:ppIntel (PEBS)메모리 접근 지연/소스 분석
ibs_op//, ibs_fetch//AMD (IBS)명령어/페치 수준 정밀 분석
topdown-*Intel (Skylake+)Top-Down 마이크로아키텍처 분석
offcore_responseIntelL3 미스 후 메모리 응답 소스 분석
cpu/event=0x...,umask=0x.../공통벤더 문서의 raw 이벤트 직접 지정
ℹ️

MSR 레지스터를 통한 PMU 제어, HWP/RAPL 등 하드웨어 성능 모니터링의 저수준 세부사항은 MSR 레지스터 페이지를 참고하세요.

Flame Graph 분석

Flame Graph는 CPU 프로파일 데이터를 스택 깊이별로 시각화하여 핫 코드 경로를 직관적으로 식별합니다:

# On-CPU Flame Graph
perf record -F 99 -ag -- sleep 30
perf script > out.perf
stackcollapse-perf.pl out.perf > out.folded
flamegraph.pl out.folded > cpu_flame.svg

# Off-CPU Flame Graph (블로킹 시간 분석)
# BPF 기반 (bcc tools)
offcputime-bpfcc -df -p $(pidof myapp) 30 > offcpu.folded
flamegraph.pl --color=io offcpu.folded > offcpu_flame.svg

# Memory allocation Flame Graph
perf record -e kmem:kmalloc -ag -- sleep 10
perf script | stackcollapse-perf.pl | flamegraph.pl > mem_flame.svg

Differential Flame Graph

perf diff의 결과를 시각화하여 최적화 전후 변화를 색상으로 표현합니다:

# 최적화 전/후 프로파일 수집
perf record -F 99 -ag -o before.data -- ./workload_v1
perf record -F 99 -ag -o after.data -- ./workload_v2

# Differential Flame Graph 생성
perf script -i before.data | stackcollapse-perf.pl > before.folded
perf script -i after.data | stackcollapse-perf.pl > after.folded
difffolded.pl before.folded after.folded | flamegraph.pl > diff_flame.svg
# 빨간색: 증가 (회귀), 파란색: 감소 (개선)

Intel® PCM (Performance Counter Monitor)

ℹ️

Intel PCM (Performance Counter Monitor)은 별도 페이지로 분리되었습니다. → Intel PCM 완전 가이드: pcm-memory, pcm-tpmi, CXL 모니터링, Grafana 대시보드, TMA 분석, Emerald/Granite Rapids, 실전 진단 시나리오 6개

Intel PCM은 Intel이 개발한 오픈소스 성능 모니터링 도구 모음으로, CPU 코어 성능 카운터, 메모리 대역폭, QPI/UPI 링크 사용률, PCIe 대역폭, 전력 소비량 등을 실시간으로 측정합니다. perf가 커널에 내장된 범용 프로파일링 프레임워크라면, PCM은 Intel 플랫폼에 특화된 시스템 수준 하드웨어 모니터링 도구입니다.

ℹ️

PCM vs perf: perf는 커널 perf_event 서브시스템을 통해 PMU에 접근하며 프로세스/함수 수준 프로파일링에 강합니다. 반면 PCM은 MSR/PCI config 레지스터에 직접 접근하여 언코어(Uncore) 카운터 — 메모리 컨트롤러, QPI/UPI 링크, PCIe, 전력 등 코어 외부 리소스 — 를 포함한 시스템 전체 관점의 모니터링에 특화되어 있습니다.

PCM 아키텍처 개요

PCM은 세 가지 경로로 하드웨어 카운터에 접근합니다:

User Space pcm (Core 카운터) pcm-memory (메모리 BW) pcm-pcie (PCIe BW) pcm-power (전력/온도) pcm-latency (메모리 레이턴시) pcm-sensor-server (REST API/Grafana) Kernel Space /dev/cpu/*/msr (MSR) perf_event_open() (PMU) sysfs / MMIO (TPMI) Hardware (CPU / Uncore) Core PMU PMC, Fixed CTR Uncore PMU CBox, iMC, UPI 메모리 컨트롤러 iMC 카운터 RAPL PKG/DRAM 에너지 PCIe / CXL IIO 카운터
접근 경로대상 카운터커널 요구사항용도
MSR 드라이버Core PMU, Fixed CTR, RAPLmsr 커널 모듈코어별 성능 카운터, 전력 측정
perf_eventCore/Uncore PMUCONFIG_PERF_EVENTSperf 서브시스템 경유 (권한 제어 용이)
sysfs/MMIOTPMI, PCI config spaceTPMI 드라이버 (6.3+)언코어 카운터, PCIe/CXL 모니터링

설치 및 빌드

# 소스에서 빌드 (GitHub)
git clone --recursive https://github.com/intel/pcm.git
cd pcm
mkdir build && cd build
cmake ..
cmake --build . --parallel

# 패키지 매니저로 설치 (배포판별)
sudo apt install pcm              # Ubuntu 22.04+ / Debian
sudo dnf install pcm              # Fedora / RHEL 9+

# Docker로 실행 (권한 필요)
docker run --privileged -d \
  --name pcm --ipc=host --pid=host \
  -v /sys:/sys:rw \
  ghcr.io/intel/pcm

커널 모듈 및 접근 권한 설정

PCM은 MSR 레지스터에 직접 접근하므로 msr 커널 모듈과 root 권한(또는 CAP_SYS_RAWIO)이 필요합니다:

# msr 커널 모듈 로드
sudo modprobe msr

# NMI watchdog 비활성화 (PMU 카운터 점유 방지)
# NMI watchdog가 고정 카운터를 사용하면 PCM과 충돌
sudo sysctl kernel.nmi_watchdog=0

# Secure Boot 환경에서 MSR 접근 허용
# /dev/cpu/*/msr 접근이 차단될 경우:
sudo modprobe msr allow_writes=on

# perf_event 기반 접근 사용 시 (MSR 직접 접근 대신)
# PCM은 perf_event 모드도 지원 (-e 옵션 또는 환경변수)
export PCM_USE_PERF=1
sudo sysctl kernel.perf_event_paranoid=-1
⚠️

NMI watchdog 충돌: NMI watchdog는 고정 카운터(INST_RETIRED.ANY)를 점유합니다. PCM 실행 전 nmi_watchdog=0으로 비활성화하지 않으면 카운터 프로그래밍이 실패하거나 부정확한 결과가 나올 수 있습니다. PCM은 시작 시 이 충돌을 자동 감지하고 경고합니다.

핵심 도구 모음

PCM은 목적별로 분리된 여러 도구를 제공합니다. 각 도구는 Intel CPU의 특정 하드웨어 카운터에 최적화되어 있습니다.

pcm: 코어 성능 카운터

기본 도구로, 코어별 IPC(Instructions Per Cycle), L2/L3 캐시 히트율, 분기 예측률 등 핵심 지표를 실시간 표시합니다.

# 기본 실행: 1초 간격으로 코어별 성능 카운터 표시
sudo pcm

# 측정 간격 및 반복 횟수 지정
sudo pcm 0.5 -i=20   # 0.5초 간격, 20회 측정

# CSV 출력 (후처리/시각화용)
sudo pcm 1 -csv=result.csv

# 특정 프로그램 실행 동안 측정
sudo pcm -- ./benchmark

# 코어 이벤트 커스텀 지정 (최대 4개 범용 카운터)
sudo pcm -e core/config=0x2e,config1=0x41,name=LLC_MISSES/

pcm 출력의 주요 지표:

지표설명정상 범위이상 징후
IPCInstructions Per Cycle1.0~4.0< 0.5이면 심각한 메모리 스톨
L2 Hit RatioL2 캐시 히트율> 95%< 80%이면 작업 셋 > L2
L3 Hit RatioL3 (LLC) 캐시 히트율> 80%< 50%이면 메모리 대역폭 확인 필요
FREQ실제 동작 주파수 (GHz)부스트 근처기본 클럭 이하이면 전력/열 제한
TEMP코어 온도 (°C)< TjMax-10TjMax 도달 시 스로틀링
INST은퇴된 명령어 수워크로드 의존코어 간 불균형 → 부하 분산 문제
AFREQActive Frequency (활성 시 주파수)≈ Max Turbo기본 클럭 근처이면 전력 제한
L3MISSL3 캐시 미스 횟수워크로드 의존높으면 메모리 BW 병목

pcm-memory: 메모리 대역폭 모니터링

소켓/채널별 메모리 대역폭(Read/Write/Total)을 iMC(Integrated Memory Controller) 카운터로 실시간 측정합니다. NUMA 환경에서 메모리 병목 진단에 매우 중요한 지표입니다.

# 소켓/채널별 메모리 대역폭 측정
sudo pcm-memory

# 0.5초 간격, 파일 출력
sudo pcm-memory 0.5 -csv=mem_bw.csv

# 특정 소켓만 모니터링
sudo pcm-memory -s 0  # 소켓 0만

출력 예시:

SocketRead (GB/s)Write (GB/s)PMM ReadPMM WriteTotal (GB/s)
045.212.8N/AN/A58.0
143.811.5N/AN/A55.3
System89.024.3N/AN/A113.3
💡

메모리 대역폭 포화 판단: 이론적 최대 대역폭은 채널 수 × DDR 전송률 × 8바이트로 계산됩니다. 예: 6채널 DDR5-4800 = 6 × 4800MT/s × 8B ≈ 230 GB/s. 측정값이 이론값의 70% 이상이면 메모리 BW 포화 상태입니다. perf stat -e LLC-load-misses와 함께 확인하세요.

pcm-pcie: PCIe 대역폭 모니터링

IIO(I/O 유닛) 언코어 카운터를 통해 PCIe 포트별 인바운드/아웃바운드 대역폭을 측정합니다. NVMe SSD, GPU, 네트워크 카드 등의 I/O 병목 진단에 사용합니다.

# PCIe 포트별 대역폭 (GB/s)
sudo pcm-pcie

# 이벤트 모드: Part0~Part3 상세 분류
sudo pcm-pcie -e

# 특정 BDF (Bus:Device.Function) 필터링
sudo pcm-pcie -B 0x3a:0x00.0

# CSV 출력
sudo pcm-pcie 1 -csv=pcie_bw.csv

# CXL 메모리 대역폭 모니터링 (지원 플랫폼)
sudo pcm-pcie -cxl

pcm-power: 전력 및 열 모니터링

RAPL(Running Average Power Limit) MSR과 온도 센서를 통해 패키지/DRAM/PP0/PP1별 전력 소비와 코어 온도, C-State 상주 비율을 실시간으로 표시합니다.

# 전력/온도/C-State 모니터링
sudo pcm-power

# 출력 예시:
# Package 0: Consumed energy (Joules): 85.2 | Thermal headroom: 23°C
# Package 0: C2 residency: 45.2% | C6 residency: 32.1%
# DRAM energy: 12.8 J

# 전력 변화 추세 관찰 (CSV)
sudo pcm-power 1 -csv=power.csv

pcm-latency: 메모리 레이턴시 측정

메모리 접근 레이턴시를 측정합니다. NUMA 환경에서 로컬/원격 메모리 접근 지연 차이를 정량적으로 파악할 수 있습니다.

# 메모리 레이턴시 측정
sudo pcm-latency

# 특정 소켓 대상
sudo pcm-latency -s 0

pcm-numa: NUMA 트래픽 분석

NUMA 환경에서 로컬/원격 메모리 접근 비율을 측정하여 메모리 배치 최적화 기회를 식별합니다.

# NUMA 로컬/원격 메모리 접근 비율 측정
sudo pcm-numa

# 출력 예시:
# Socket 0: Local Memory Accesses: 95.2% | Remote: 4.8%
# Socket 1: Local Memory Accesses: 87.3% | Remote: 12.7%  ← 최적화 필요

# NUMA 최적화 확인: 원격 접근이 10% 이상이면
# numactl --membind 또는 cpuset으로 바인딩 조정 필요

pcm-iio: I/O 유닛 통계

IIO(I/O 유닛) 스택별 인바운드/아웃바운드 트래픽을 상세 분석합니다. PCIe 디바이스와 CPU 소켓 간 데이터 흐름을 파악합니다.

# I/O 유닛(IIO) 스택별 트래픽
sudo pcm-iio

# CSV 출력
sudo pcm-iio 1 -csv=iio.csv

pcm-raw: Raw 카운터 프로그래밍

사전 정의 도구로 측정할 수 없는 커스텀 이벤트를 JSON 기반 이벤트 정의 파일로 프로그래밍합니다. Intel SDM의 이벤트 코드를 직접 지정할 수 있습니다.

# JSON 이벤트 정의 파일 사용
sudo pcm-raw -e events.json

# 이벤트 정의 예시 (events.json):
# {
#   "core": {
#     "programmable": [
#       { "name": "L2_MISS", "event": "0x2e", "umask": "0x41" },
#       { "name": "DTLB_MISS", "event": "0x08", "umask": "0x20" }
#     ]
#   }
# }

# PCM 배포에 포함된 사전 정의 이벤트
ls /usr/share/pcm/PMURegisterDeclarations/
# → SPR/ ICX/ SKX/ ... (마이크로아키텍처별 이벤트 파일)

pcm-sensor-server: 원격 모니터링 (REST API / Grafana)

pcm-sensor-server는 PCM 데이터를 HTTP/HTTPS REST API로 노출하여, Grafana, Prometheus, Telegraf 등 외부 모니터링 시스템과 통합합니다.

# REST API 서버 시작 (기본 포트: 9738)
sudo pcm-sensor-server

# HTTPS + 포트 지정
sudo pcm-sensor-server -p 19738 --https 443 \
  --key server.key --cert server.crt

# JSON 데이터 조회
curl -s http://localhost:9738/metrics | head -20
# Prometheus 형식 메트릭 노출 → Grafana 대시보드 연동

# Grafana 대시보드 활용
# PCM 소스의 grafana/ 디렉터리에 사전 정의 대시보드 JSON 포함
# → Grafana에서 Import하면 코어/메모리/PCIe/전력 대시보드 즉시 사용

pcm-tpmi: Topology Aware Register & PM Interface (6.3+)

Sapphire Rapids 이후 서버 플랫폼에서 TPMI(Topology Aware Register and PM Interface)를 통해 언코어 카운터에 접근합니다. MSR 대신 MMIO 기반으로 동작하여 Secure Boot 환경에서도 사용 가능합니다.

# TPMI 기반 언코어 카운터 접근
sudo pcm-tpmi

# TPMI 지원 확인
ls /sys/bus/auxiliary/devices/ | grep intel_vsec
# intel_vsec 드라이버가 로드되어 있으면 TPMI 지원

실전 활용 시나리오

시나리오 1: 메모리 대역폭 포화 진단

애플리케이션이 CPU 사용률은 높지만 IPC가 낮은 경우, 메모리 대역폭 포화가 원인일 수 있습니다:

# 1단계: IPC 확인 → 메모리 바운드 여부 판단
sudo pcm 1 -i=5
# → IPC < 1.0 + L3 Hit Ratio < 50% → 메모리 바운드 의심

# 2단계: 메모리 대역폭 측정
sudo pcm-memory 0.5
# → Read BW가 이론값의 70% 이상이면 BW 포화
# → 채널 간 불균형이 크면 DIMM 구성 확인

# 3단계: NUMA 배치 확인
sudo pcm-numa
# → Remote Access > 10%이면 메모리 배치 최적화
# → numactl --membind=0 ./app 으로 로컬 바인딩

# 4단계: perf로 핫스팟 함수의 LLC 미스 확인
perf record -e LLC-load-misses:pp -c 10000 -- ./app
perf report

시나리오 2: PCIe I/O 병목 진단

# NVMe/GPU가 예상 성능에 미달할 때

# 1단계: PCIe 대역폭 측정
sudo pcm-pcie 1
# → 디바이스별 인바운드/아웃바운드 BW 확인
# → Gen4 x16: 이론 31.5 GB/s, 실측이 50% 미만이면 문제

# 2단계: IIO 스택별 상세 분석
sudo pcm-iio 1
# → 어느 IIO 스택에서 병목이 발생하는지 파악

# 3단계: 전력 제한 확인 (전력 제한으로 I/O 성능 저하 가능)
sudo pcm-power 1
# → PKG 전력이 TDP 근처이면 전력 스로틀링

시나리오 3: 코어 주파수 스로틀링 진단

# 성능이 간헐적으로 저하될 때 (열/전력 제한 의심)
sudo pcm 0.5 -csv=freq_trace.csv -i=60
# → FREQ/AFREQ 컬럼에서 주파수 변동 추적
# → TEMP가 TjMax에 근접하면 열 스로틀링
# → C6 residency가 높으면 idle 상태 비율 과다

# turbostat 병행 (PCM과 보완적)
sudo turbostat --interval 1 --show Core,CPU,Avg_MHz,Busy%,Bzy_MHz,PkgWatt
# Busy% 100%인데 Bzy_MHz < Max Turbo이면 전력/열 제한

PCM과 대안 도구 비교

도구제공자카운터 접근강점제한
Intel PCMIntel (오픈소스)MSR, perf_event, MMIO언코어 카운터 (iMC, UPI, IIO) 통합, REST APIIntel CPU 전용
perf커널 내장perf_event범용 프로파일링, 프로세스/함수 수준 분석언코어 카운터 설정 복잡
turbostat커널 소스 포함MSR주파수/전력/C-State 특화, 커널과 함께 배포성능 카운터 제한적
pmu-tools (toplev)Andi Kleenperf 위에 구축Top-Down 마이크로아키텍처 분석 (TMA) 자동화설정 복잡, Intel 전용
likwidRRZE (독일)MSR, perf_eventIntel + AMD 지원, 그룹 기반 측정언코어 카운터 PCM보다 제한적

Top-Down 마이크로아키텍처 분석 (TMA) 연계

Intel의 Top-Down Microarchitecture Analysis (TMA) 방법론은 CPU 파이프라인의 병목을 4개 대분류(Frontend Bound, Backend Bound, Bad Speculation, Retiring)로 분류합니다. PCM과 pmu-tools/toplev을 결합하면 체계적인 병목 분석이 가능합니다.

Pipeline Slots (100%) Frontend Bound Bad Speculation Backend Bound Retiring Fetch Latency Fetch BW Branch Mispr. Machine Clears Memory Bound Core Bound Base Micro Seq. L1/L2/L3 Bound DRAM Bound Store Bound pcm → IPC/L3 히트율 | pcm-memory → DRAM BW | toplev → TMA Level 1~6 자동 분류
# pmu-tools 설치
git clone https://github.com/andikleen/pmu-tools.git
cd pmu-tools

# toplev: TMA Level 1 (대분류) 분석
sudo python3 toplev.py --core S0-C0 -l1 -v -- ./benchmark
# 출력: FE(Frontend) 30%, BE(Backend) 45%, BadSpec 5%, Retiring 20%
# → Backend Bound가 지배적 → 메모리/코어 바운드 분석 필요

# Level 2: Backend Bound 상세 분해
sudo python3 toplev.py --core S0-C0 -l2 -v -- ./benchmark
# → Memory Bound 35%, Core Bound 10%

# Level 3+: Memory Bound 세부 원인
sudo python3 toplev.py --core S0-C0 -l3 --nodes '!+Memory*' -v -- ./benchmark
# → L3 Bound 8%, DRAM Bound 22%, Store Bound 5%
# → pcm-memory로 DRAM BW 포화 확인

# PCM + toplev 병행 워크플로
# 1. pcm: 전체 시스템 IPC/캐시/주파수 개요 → 이상 징후 파악
# 2. toplev -l1~l3: 병목 카테고리 좁히기
# 3. pcm-memory/pcm-pcie: 구체적 하드웨어 병목 수치 확인
# 4. perf record + annotate: 함수/명령어 수준 최적화

커널 드라이버 및 관련 소스

PCM이 내부적으로 사용하는 커널 인프라:

커널 드라이버/인터페이스경로역할
msr 모듈arch/x86/kernel/msr.c/dev/cpu/N/msr 문자 장치 제공
intel_uncore PMUarch/x86/events/intel/uncore*.cperf 서브시스템에 언코어 PMU 등록
intel_rapldrivers/powercap/intel_rapl_*.cRAPL 전력 측정 (powercap 프레임워크)
intel_vsec/tpmidrivers/platform/x86/intel/tpmi.cTPMI 레지스터 MMIO 접근 (6.3+)
intel-cstate PMUarch/x86/events/intel/cstate.cC-State 상주 시간 카운터
PCI config space/sys/bus/pci/devices/*/config언코어 PCI 레지스터 접근 (iMC, UPI)
/* PCM이 MSR을 읽는 커널 경로 (arch/x86/kernel/msr.c) */
static ssize_t msr_read(struct file *file, char __user *buf,
                       size_t count, loff_t *ppos)
{
    u32 __user *tmp = (u32 __user *)buf;
    u32 data[2];
    u32 reg = *ppos;       /* MSR 주소 = file offset */
    int cpu = iminor(file->f_path.dentry->d_inode);
    int err;

    /* 해당 CPU에서 RDMSR 실행 */
    err = rdmsr_safe_on_cpu(cpu, reg, &data[0], &data[1]);
    if (err)
        return -EIO;

    if (copy_to_user(tmp, &data, 8))
        return -EFAULT;
    return 8;
}

/* PCM의 실제 MSR 읽기 (user space → /dev/cpu/N/msr) */
/* pread(fd, &data, sizeof(data), MSR_ADDR) */
/* 예: pread(fd, &val, 8, 0x38F) → IA32_PERF_GLOBAL_CTRL */
# PCM에 필요한 커널 설정
CONFIG_X86_MSR=m                   # /dev/cpu/*/msr (필수)
CONFIG_PERF_EVENTS=y               # perf_event 모드 사용 시
CONFIG_INTEL_UNCORE=m              # 언코어 PMU (perf_event 경유 시)
CONFIG_INTEL_RAPL=m                # RAPL 전력 측정
CONFIG_INTEL_VSEC=m                # TPMI 지원 (SPR+ 서버)
CONFIG_INTEL_PMT_TELEMETRY=m       # PMT 텔레메트리

# msr 모듈 자동 로드 설정
echo "msr" | sudo tee /etc/modules-load.d/msr.conf
💡

perf_event 모드 vs MSR 직접 접근: PCM_USE_PERF=1 환경 변수를 설정하면 PCM이 MSR 직접 접근 대신 perf_event_open()을 통해 카운터에 접근합니다. 이 모드는 msr 커널 모듈이 불필요하고, perf_event_paranoid로 권한을 세밀하게 제어할 수 있습니다. 다만 일부 언코어 카운터는 perf_event로 접근이 불가하므로 MSR 모드가 더 완전한 데이터를 제공합니다.

ftrace 성능 분석 활용

ftrace는 커널 내장 추적 프레임워크입니다. 핵심 원리는 동적 계측(dynamic instrumentation)으로, 컴파일 시 함수 엔트리 계측 지점(mcount/fentry)이 준비되고, 런타임에 동적 패치를 통해 NOP/호출 경로를 전환합니다. 비활성 시 오버헤드는 거의 0입니다. 상세 원리는 ftrace 전용 페이지를 참조하세요. 여기서는 성능 분석에 특화된 활용법에 집중합니다.

# ftrace 디렉터리
cd /sys/kernel/tracing  # 또는 /sys/kernel/debug/tracing

# ── 함수 지연 시간 측정 (function_graph) ──
echo function_graph > current_tracer
echo vfs_read > set_graph_function
echo 1 > tracing_on
cat trace
#  1)               |  vfs_read() {
#  1)   0.523 us    |    rw_verify_area();
#  1)               |    __vfs_read() {
#  1)   0.891 us    |      new_sync_read();
#  1)   1.234 us    |    }
#  1)   2.345 us    |  }  ← 전체 실행 시간
echo 0 > tracing_on

# ── trace-cmd: ftrace의 사용자 친화적 래퍼 ──
# 설치: apt install trace-cmd
trace-cmd record -p function_graph -g vfs_read -P $(pidof myapp)
trace-cmd report | head -50

# ── 히스토그램 트리거 (6.1+): ftrace 기반 지연 분포 ──
# sched_switch 이벤트의 지연 히스토그램
echo 'hist:keys=next_pid:vals=hitcount:sort=hitcount.descending:size=32' \
  > events/sched/sched_switch/trigger
cat events/sched/sched_switch/hist

# ── 함수 추적 + 필터 ──
echo function > current_tracer
echo do_sys_openat2 > set_ftrace_filter  # 특정 함수만
echo '*lock*' > set_ftrace_filter         # 와일드카드 패턴
echo '!*debug*' >> set_ftrace_filter       # 패턴 제외

# ── 이벤트 추적 (tracepoint) ──
echo 1 > events/sched/sched_switch/enable
echo 1 > events/block/block_rq_issue/enable
echo 1 > tracing_on
cat trace_pipe                             # 실시간 스트리밍

# ── 지연 추적기: 최대 지연 원인 찾기 ──
echo irqsoff > current_tracer      # IRQ 비활성화 최대 시간
echo preemptoff > current_tracer   # 선점 비활성화 최대 시간
echo wakeup > current_tracer       # 최고 우선순위 태스크 웨이크업→실행 지연
cat tracing_max_latency             # 최대 지연 (us)

# ── 함수 프로파일러: 함수별 호출 횟수/시간 ──
echo nop > current_tracer
echo 1 > function_profile_enabled
# 워크로드 실행...
echo 0 > function_profile_enabled
cat trace_stat/function*
#   Function              Hit    Time            Avg
#   --------              ---    ----            ---
#   vfs_read              1234   456.789 us      0.370 us
💡

ftrace vs perf vs BPF: ftrace는 커널 함수 호출 흐름과 지연 시간 분석에 최적입니다. perf는 PMU 기반 통계/샘플링에, BPF는 커스텀 필터링과 집계에 강합니다. 일반적으로 "어디서 시간이 소모되나" → perf, "왜 느린가" → ftrace/BPF, "얼마나 느린가" → 모두 조합.

io_uring

io_uring은 Linux 5.1에서 도입된 비동기 I/O 인터페이스로, 공유 링 버퍼를 통해 시스템 콜 오버헤드를 줄입니다. SQPOLL+IOPOLL 조합은 특정 워크로드/디바이스 조건에서 시스템 콜 및 인터럽트 경로를 크게 줄일 수 있지만, 모든 I/O 경로에서 완전히 제거되는 것은 아닙니다.

💡

io_uring의 상세 아키텍처, 커널 내부 구현, liburing 예제, 보안 고려사항 등은 io_uring 전용 페이지를 참고하세요.

커널 튜닝 파라미터

커널 튜닝 파라미터(sysctl)는 런타임에 커널 동작을 조정합니다. 튜닝은 반드시 측정 기반으로 진행하고, 변경 전후 벤치마크를 비교해야 합니다.

네트워크 튜닝

파라미터기본값고성능 설정설명
net.ipv4.tcp_rmem4096 131072 62914564096 262144 16777216TCP 수신 버퍼 (min default max) 바이트
net.ipv4.tcp_wmem4096 16384 41943044096 262144 16777216TCP 송신 버퍼 (min default max) 바이트
net.core.rmem_max21299216777216소켓 수신 버퍼 최대값
net.core.wmem_max21299216777216소켓 송신 버퍼 최대값
net.core.somaxconn409665535listen() backlog 최대값
net.core.netdev_max_backlog10005000~30000NIC → 커널 큐 최대 길이
net.ipv4.tcp_max_syn_backlog204865535SYN_RECV 상태 최대 대기열
net.ipv4.tcp_tw_reuse21TIME_WAIT 소켓 재사용 (클라이언트)
net.ipv4.tcp_fastopen13TFO 활성화 (1: 클라이언트, 2: 서버, 3: 양쪽)
net.ipv4.tcp_congestion_controlcubicbbr혼잡 제어 알고리즘 (BBR: 대역폭 기반)
net.core.busy_poll050소켓 busy polling 시간(us) — 지연↓ CPU↑
# 고성능 네트워크 튜닝 (10G+ 환경)
sysctl -w net.core.rmem_max=16777216
sysctl -w net.core.wmem_max=16777216
sysctl -w net.ipv4.tcp_rmem="4096 262144 16777216"
sysctl -w net.ipv4.tcp_wmem="4096 262144 16777216"
sysctl -w net.core.netdev_max_backlog=5000

# BBR 혼잡 제어 활성화
modprobe tcp_bbr
sysctl -w net.ipv4.tcp_congestion_control=bbr
sysctl -w net.core.default_qdisc=fq

# RSS (Receive Side Scaling): NIC 큐 → CPU 분산
ethtool -l eth0                    # 현재 큐 수 확인
ethtool -L eth0 combined 8        # 8개 큐 설정

# RPS/RFS (소프트웨어 기반 패킷 분산)
echo f > /sys/class/net/eth0/queues/rx-0/rps_cpus      # CPU 0~3
echo 32768 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt  # 플로우 테이블
echo 32768 > /proc/sys/net/core/rps_sock_flow_entries

# 인터럽트 코얼레싱 (NIC별 최적화)
ethtool -c eth0                           # 현재 설정
ethtool -C eth0 rx-usecs 50 tx-usecs 50  # 50us 주기로 인터럽트 통합
ethtool -C eth0 adaptive-rx on            # 적응형 코얼레싱

I/O 튜닝

파라미터경로설명
I/O 스케줄러/sys/block/sdX/queue/schedulernone(NVMe), mq-deadline(HDD), bfq(데스크톱), kyber(서버)
readahead/sys/block/sdX/queue/read_ahead_kb선독 크기 (기본 128KB, 순차 I/O: 2048~4096)
nr_requests/sys/block/sdX/queue/nr_requestsI/O 큐 깊이 (높이면 처리량↑ 지연↑)
max_sectors_kb/sys/block/sdX/queue/max_sectors_kb단일 I/O 요청 최대 크기
rotational/sys/block/sdX/queue/rotational0: SSD/NVMe, 1: HDD (스케줄러 힌트)
# I/O 스케줄러 설정
cat /sys/block/nvme0n1/queue/scheduler   # 현재 스케줄러 확인
echo none > /sys/block/nvme0n1/queue/scheduler  # NVMe: none 권장
echo mq-deadline > /sys/block/sda/queue/scheduler  # HDD: mq-deadline

# readahead 튜닝 (순차 읽기 워크로드)
blockdev --getra /dev/sda              # 현재값 (512바이트 단위)
blockdev --setra 4096 /dev/sda       # 2MB readahead (4096 × 512B)

# NVMe 최적화
echo 2 > /sys/block/nvme0n1/queue/nomerges      # 병합 비활성화 (NVMe는 불필요)
echo 1024 > /sys/block/nvme0n1/queue/nr_requests  # 큐 깊이 증가

# Dirty 페이지 writeback 튜닝
sysctl -w vm.dirty_ratio=40              # 동기 writeback 임계값 (%)
sysctl -w vm.dirty_background_ratio=10  # 비동기 writeback 시작 (%)
sysctl -w vm.dirty_expire_centisecs=3000  # dirty 페이지 만료 (30초)
sysctl -w vm.dirty_writeback_centisecs=500  # writeback 데몬 주기 (5초)

# filesystem mount 옵션 최적화
# ext4: noatime,nobarrier(배터리 백업 시),commit=60
# xfs: noatime,inode64,logbufs=8
# 주의: nobarrier는 전원 장애 시 데이터 손실 위험

메모리/VM 튜닝

파라미터기본값설명
vm.swappiness60스왑 적극성 (0: 최소 스왑, 200: 매우 적극적)
vm.dirty_ratio20프로세스가 writeback 대기하는 dirty 비율(%)
vm.dirty_background_ratio10백그라운드 writeback 시작 임계값(%)
vm.nr_hugepages0정적 Huge page 수 (2MB 페이지)
vm.overcommit_memory0메모리 오버커밋 (0: 휴리스틱, 1: 항상, 2: 제한)
vm.min_free_kbytes자동비상 메모리 예약 (높이면 OOM↓ 가용 메모리↓)
kernel.sched_latency_ns커널별 상이CFS 지연 관련 (디버그 인터페이스)

컴파일러 최적화

커널 코드에서 사용하는 컴파일러 힌트, 최적화 속성, 빌드 옵션은 런타임 성능에 직접적인 영향을 줍니다. GCC/Clang은 커널 전용 확장과 함께 다양한 최적화 기법을 제공합니다.

커널 빌드 최적화 파이프라인 C 소스 likely/unlikely __hot/__cold GCC/Clang -O2 인라인, 루프 최적화 상수 전파, DCE LTO (선택) 모듈 간 인라인 전역 DCE PGO/AutoFDO 프로파일 기반 핫/콜드 분리 vmlinux / *.ko .text.hot / .text.unlikely 최적화된 기계어 커널 빌드 CONFIG 옵션 CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y (-O2) | CONFIG_CC_OPTIMIZE_FOR_SIZE=y (-Os) CONFIG_LTO_CLANG_FULL=y | CONFIG_CFI_CLANG=y | CONFIG_FRAME_POINTER=y (-fno-omit-frame-pointer)

분기 예측 및 코드 배치 힌트

/* ── 분기 예측 힌트 ── */
if (likely(condition))    /* __builtin_expect(!!(x), 1) → 대부분 true */
    fast_path();
if (unlikely(error))     /* __builtin_expect(!!(x), 0) → 대부분 false */
    handle_error();

/* static_branch: 런타임 패칭 (0 오버헤드 분기) */
DEFINE_STATIC_KEY_FALSE(my_feature);

if (static_branch_unlikely(&my_feature))
    do_feature();   /* 비활성 시: NOP (분기 비용 0) */

/* 활성화 시 코드 패칭으로 분기 전환 */
static_branch_enable(&my_feature);

/* ── 코드 섹션 배치 ── */
void __hot fast_func(void);     /* .text.hot 섹션 (I-캐시 친화적) */
void __cold error_func(void);   /* .text.unlikely 섹션 (핫 경로에서 분리) */
void __init setup_func(void);   /* .init.text → 부팅 후 메모리 해제 */

/* noinline: 인라인 방지 (콜스택 보존, 코드 크기 제어) */
noinline void debug_dump(void);
/* __always_inline: 최적화 수준과 관계없이 강제 인라인 */
static __always_inline void critical_path(void);

메모리 접근 최적화

/* ── 캐시 프리페치 ── */
prefetch(ptr);            /* L1 캐시로 미리 로드 (읽기 의도) */
prefetchw(ptr);           /* L1 캐시로 미리 로드 (쓰기 의도, MESI E 상태) */

/* 리스트 순회 시 다음 노드 프리페치 */
list_for_each_entry(entry, &head, list) {
    prefetch(entry->list.next);
    process(entry);
}

/* ── 캐시라인 정렬 ── */
struct hot_data {
    atomic_t counter;         /* 자주 접근하는 필드를 앞에 배치 */
    unsigned long flags;
} ____cacheline_aligned;     /* 캐시라인 경계에 정렬 */

/* false sharing 방지: 별도 캐시라인에 배치 */
struct per_cpu_data {
    unsigned long count;
} ____cacheline_aligned_in_smp;  /* SMP에서만 정렬 */

/* ── READ_ONCE / WRITE_ONCE: 컴파일러 최적화 방지 ── */
/* 공유 변수 접근 시 컴파일러의 부적절한 최적화 방지 */
int val = READ_ONCE(shared_var);   /* 반드시 메모리에서 읽기 */
WRITE_ONCE(shared_var, new_val);   /* 반드시 메모리에 쓰기 */

/* ── 배리어 ── */
barrier();       /* 컴파일러 배리어 (재배치 방지, CPU 배리어 아님) */
smp_rmb();       /* SMP 읽기 메모리 배리어 */
smp_wmb();       /* SMP 쓰기 메모리 배리어 */
smp_mb();        /* SMP 전체 메모리 배리어 */

LTO, PGO, AutoFDO

기법원리커널 지원성능 영향
LTO (Link-Time Optimization)링크 시점에 모든 오브젝트를 통합 최적화CONFIG_LTO_CLANG_FULL (Clang 전용, 5.12+)코드 크기 5~10% 감소, IPC 1~3% 향상
ThinLTOLTO의 경량 버전 (병렬 빌드 가능)CONFIG_LTO_CLANG_THINFull LTO의 90% 효과, 빌드 시간 1/3
PGO (Profile-Guided Optimization)프로파일 데이터로 핫 경로 최적화CONFIG_PGO_CLANG (5.12+, Clang)커널 전체 1~5% 처리량 향상
AutoFDOperf 샘플링 데이터를 피드백실험적 (GCC + perf)PGO 유사하나 수집이 쉬움
CFI (Control-Flow Integrity)간접 호출 무결성 검증CONFIG_CFI_CLANG (LTO 필요)보안 강화, 약간의 성능 비용
# LTO 빌드 (Clang 필수)
make LLVM=1 defconfig
scripts/config -e LTO_CLANG_THIN
make LLVM=1 -j$(nproc)

# PGO 워크플로 (Clang)
# 1. 계측 빌드
scripts/config -e PGO_CLANG
make LLVM=1 -j$(nproc)

# 2. 대표 워크로드 실행 후 프로파일 수집
# (커널 부팅 → 워크로드 실행 → 프로파일 추출)
cp /sys/kernel/debug/pgo/profraw vmlinux.profraw
llvm-profdata merge -o vmlinux.profdata vmlinux.profraw

# 3. PGO 최적화 빌드
make LLVM=1 KCFLAGS="-fprofile-use=vmlinux.profdata" -j$(nproc)

# AutoFDO 워크플로 (GCC)
# 1. 표준 빌드 후 perf 프로파일 수집
perf record -b -e cycles:pp -ag -- sleep 60
# 2. create_gcov로 프로파일 변환
create_gcov --binary=vmlinux --profile=perf.data \
  --gcov=vmlinux.gcov -gcov_version=2
# 3. 프로파일 기반 재빌드
make KCFLAGS="-fauto-profile=vmlinux.gcov" -j$(nproc)

성능 관련 커널 CONFIG 옵션

CONFIG 옵션효과권장
CC_OPTIMIZE_FOR_PERFORMANCE-O2 (기본, 균형)일반 워크로드
CC_OPTIMIZE_FOR_SIZE-Os (코드 크기 최소화)임베디드, I-캐시 압박
FRAME_POINTER-fno-omit-frame-pointer프로파일링 시 (perf 콜스택)
DEBUG_INFO_REDUCED축소된 디버그 정보프로덕션 (디버그+성능 절충)
RETPOLINESpectre v2 완화 (간접 분기 보호)보안 필수, 성능 1~5% 비용
PAGE_TABLE_ISOLATIONKPTI (Meltdown 완화)Intel CPU 필수, AMD는 불필요
PREEMPT_NONE선점 비활성화서버 처리량 최대화
PREEMPT완전 선점 허용데스크톱/RT 반응성
HZ_1000틱 1000Hz (1ms 해상도)저지연 워크로드
HZ_250틱 250Hz (4ms 해상도)서버 (오버헤드↓)
NO_HZ_FULL틱리스 (유휴+활성)지연 민감 RT + isolcpus
💡

perf stat -d로 L1/LLC 캐시 미스율을 확인하세요. 캐시 미스가 많으면 자료구조의 캐시라인 정렬과 메모리 접근 패턴 최적화가 필요합니다. 보안 완화(KPTI, Retpoline)의 성능 비용은 perf stat -d -- before_mitigation과 비교 측정으로 정량화하세요.

BPF 기반 프로파일링

BPF(eBPF)는 커널 내부에서 안전하게 실행되는 프로그램으로, perf/ftrace보다 유연한 커스텀 프로파일링을 가능하게 합니다. bcc(BPF Compiler Collection)는 사전 작성된 도구 모음이고, bpftrace는 AWK 스타일의 간결한 프로파일링 스크립트 언어입니다.

BPF 프로파일링 아키텍처 User Space bcc tools bpftrace libbpf/CO-RE perf (BPF 필터) custom BPF program bpf() syscall Kernel Space BPF Verifier 안전성 검증 JIT 컴파일 네이티브 코드 kprobe/kretprobe 함수 진입/종료 tracepoint 정적 계측점 fentry/fexit BPF 트램폴린 perf_event PMU/SW 이벤트 uprobe/USDT 사용자 공간 계측 출력 BPF Maps (hash/array/ringbuf) → histogram, count, stack trace → perf_buffer → user space

bcc 도구 모음

카테고리도구용도주요 옵션
CPUprofileCPU 프로파일 (스택 샘플링)-f (Flame Graph 형식)
runqlat런큐 대기 시간 히스토그램-m (ms), -P (PID별)
cpudistOn-CPU 시간 분포-O (Off-CPU)
메모리cachestat페이지 캐시 히트/미스율인터벌(초) 지정
memleak메모리 누수 탐지 (malloc/free)-p PID, -a (할당 추적)
oomkillOOM kill 이벤트 추적실시간 알림
디스크 I/Obiolatency블록 I/O 지연 히스토그램-mD (ms, 디스크별)
biosnoop개별 블록 I/O 요청 추적-Q (큐 시간 포함)
biotop프로세스별 블록 I/O (top 형식)인터벌 지정
네트워크tcpretransTCP 재전송 추적-l (lossprobe 포함)
tcpconnectTCP 연결 시도 추적-t (타임스탬프)
tcplifeTCP 세션 수명/전송량연결 시작~종료 추적
커널funclatency함수 실행 시간 히스토그램-u (us), -m (ms)
hardirqs/softirqs인터럽트 처리 시간-d (분포), -T (타임스탬프)
# 설치 (배포판별)
sudo apt install bcc-tools bpftrace       # Debian/Ubuntu
sudo dnf install bcc-tools bpftrace       # Fedora/RHEL

# CPU 프로파일링 → Flame Graph
profile-bpfcc -df -p $(pidof myapp) 30 > out.folded
flamegraph.pl out.folded > cpu_flame.svg

# 런큐 지연 (스케줄링 대기 시간) 히스토그램
runqlat-bpfcc -m 10    # 10초 동안 ms 단위

# 블록 I/O 지연 시간 분석
biolatency-bpfcc -mD 10    # 밀리초 단위, 디스크별, 10초

# 페이지 캐시 효율성
cachestat-bpfcc 1
#     HITS    MISSES  DIRTIES  HITRATIO   BUFFERS_MB  CACHED_MB
#    12543      234       89    98.17%         128       4096

# 함수 실행 시간 분포
funclatency-bpfcc vfs_read -u 10
# → vfs_read() 실행 시간 히스토그램 (us 단위)

# Off-CPU 분석 (블로킹/슬립 시간)
offcputime-bpfcc -df -p $(pidof myapp) 30 > offcpu.folded
flamegraph.pl --color=io offcpu.folded > offcpu_flame.svg

bpftrace 원라이너

# 함수별 실행 시간 히스토그램
bpftrace -e 'kprobe:vfs_read { @start[tid] = nsecs; }
  kretprobe:vfs_read /@start[tid]/ {
    @us = hist((nsecs - @start[tid]) / 1000);
    delete(@start[tid]);
  }'

# 시스콜별 횟수 (프로세스별)
bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm, args->id] = count(); }'

# 프로세스별 페이지 폴트
bpftrace -e 'software:page-faults:1 { @[comm, pid] = count(); }'

# TCP 재전송 추적 (소스/목적지 포함)
bpftrace -e 'kprobe:tcp_retransmit_skb {
    $sk = (struct sock *)arg0;
    @[ntop($sk->__sk_common.skc_daddr)] = count();
}'

# 블록 I/O 크기 분포
bpftrace -e 'tracepoint:block:block_rq_issue {
    @bytes = hist(args->bytes);
}'

# context switch 빈도 (프로세스별)
bpftrace -e 'tracepoint:sched:sched_switch {
    @[args->prev_comm] = count();
}'

# 메모리 할당 핫스팟 (kmalloc 콜스택)
bpftrace -e 'tracepoint:kmem:kmalloc {
    @bytes[kstack] = sum(args->bytes_alloc);
}'

# CPU 주파수 변경 추적
bpftrace -e 'tracepoint:power:cpu_frequency {
    printf("CPU %d: %d MHz\n", args->cpu_id, args->state / 1000);
}'
💡

BPF 프로파일링 선택 기준: (1) 사전 정의된 분석 → bcc tools (빠르게 사용). (2) 커스텀 일회성 분석 → bpftrace (간결한 스크립트). (3) 프로덕션 상시 모니터링 → libbpf + CO-RE (커널 버전 독립적, 컴파일 의존성 없음). BPF의 오버헤드는 일반적으로 매우 낮지만(~1-5%), 고빈도 이벤트(예: 모든 malloc 추적)에서는 측정 대상 자체의 성능에 영향을 줄 수 있습니다.

잠금 경쟁 분석 (lockstat)

다중 CPU 환경에서 잠금(lock) 경쟁은 확장성의 가장 흔한 병목입니다. 커널은 목적별로 다양한 잠금 메커니즘을 제공하며, 각 잠금의 경쟁 패턴을 식별하는 것이 최적화의 첫 단계입니다.

커널 잠금 메커니즘 비교 잠금 유형 경쟁 시 동작 슬립 여부 IRQ 안전 사용 시나리오 spinlock 바쁜 대기 (spin) No spin_lock_irq* 짧은 임계영역, IRQ 핸들러 rwlock 바쁜 대기 No read_lock_irq* 읽기 위주 (동시 읽기 허용) mutex 슬립 (optimistic spin) Yes No 긴 임계영역, 프로세스 컨텍스트 rw_semaphore 슬립 Yes No mmap_lock, 파일시스템 (읽기 동시성) RCU 대기 없음 (읽기) No (읽기) Yes 읽기 극다수, 쓰기 드묾 (라우팅 등) 경쟁 심한 spinlock → mutex 전환 | 읽기 위주 → rwsem/RCU 전환 | per-CPU 변수로 잠금 제거

lockstat 사용법

# lockstat 활성화 (CONFIG_LOCK_STAT 필요)
echo 0 > /proc/lock_stat      # 카운터 초기화
echo 1 > /proc/sys/kernel/lock_stat  # 활성화 (없을 수 있음, 커널 버전에 따라)

# 워크로드 실행 후 통계 수집
cat /proc/lock_stat | head -60
# 출력 컬럼:
# class name     con-bounces  contestations  waittime-min  waittime-max  waittime-total
#                acq-bounces  acquisitions   holdtime-min  holdtime-max  holdtime-total
# → contestations(경쟁 횟수)와 waittime-total이 큰 잠금이 병목
# → con-bounces: 캐시라인 바운스 (다른 CPU로 전이된 횟수)

# 경쟁이 심한 잠금 Top 10 추출
awk '/^ / && NR>4 {print}' /proc/lock_stat | sort -k 4 -rn | head -10

perf lock: 상세 잠금 분석

# perf lock: 잠금 이벤트 기록 및 분석
perf lock record -- sleep 10     # 시스템 전체 잠금 이벤트 기록
perf lock record -p $(pidof myapp) -- sleep 10  # 특정 프로세스

# 잠금별 경쟁 통계
perf lock report
#                     Name  acquired  contended  avg wait  total wait
#     rcu_node_0:lock    234567       1234      5.2us     6412us
#     &mm->mmap_lock     89012        890       12.3us    10947us  ← 핫

# 경쟁이 심한 잠금의 콜스택 확인
perf lock contention -b -s 10  # BPF 기반 실시간 (6.2+)
# 가장 경쟁이 심한 10개 잠금의 콜스택 표시

# lock contention 추적 (BPF)
bpftrace -e 'tracepoint:lock:contention_begin {
    @[kstack] = count();
}'

# mutex 경쟁 지연 히스토그램
bpftrace -e 'tracepoint:lock:contention_begin {
    @start[tid] = nsecs;
}
tracepoint:lock:contention_end /@start[tid]/ {
    @us = hist((nsecs - @start[tid]) / 1000);
    delete(@start[tid]);
}'
💡

잠금 최적화 전략: (1) 잠금 세분화 — 하나의 큰 잠금을 여러 작은 잠금으로 분할 (예: 글로벌 → per-CPU/per-bucket). (2) 잠금 유형 변경 — 읽기 위주면 spinlock → rwlock 또는 RCU. (3) 잠금 제거 — per-CPU 변수, lockless 자료구조(RCU 리스트), atomic 연산. (4) 임계영역 축소 — 잠금 보유 중 I/O나 메모리 할당 금지. RCU 기법에 대한 상세 내용은 RCU 페이지를 참고하세요.

메모리 프로파일링

메모리 서브시스템은 CPU와 함께 성능의 양대 축입니다. 물리 메모리 할당, 가상 메모리 매핑, 페이지 캐시, slab 할당자, NUMA 배치 등 여러 계층에서 병목이 발생할 수 있으며, 각 계층에 맞는 도구로 분석해야 합니다.

메모리 프로파일링 계층 및 도구 Application (malloc/mmap) memleak-bpfcc, valgrind, heaptrack 사용자 공간 메모리 할당/해제 Page Cache cachestat, vmtouch, fincore 파일 데이터 캐싱 (LRU active/inactive) Slab Allocator (SLUB) slabtop, /proc/slabinfo, slabinfo -T 커널 객체 캐시 (dentry, inode, task_struct 등) Buddy Allocator (Page) page_owner, /proc/buddyinfo, DAMON 물리 페이지 할당/해제 (order 0~10, 2^N 페이지) Physical Memory (DRAM / NUMA) numastat, pcm-memory, pcm-numa NUMA 노드별 메모리 대역폭/레이턴시, 채널별 사용률 vmstat · /proc/meminfo · /proc/vmstat · PSI(memory) · perf mem · kmemleak

vmstat / /proc/meminfo 분석

# vmstat: 1초 간격 시스템 메모리/CPU 통계
vmstat 1
# procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
#  r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
#  2  0      0 823456  12340 456789    0    0    50   120  850  420  5  2 92  1  0
# r: 실행 가능 프로세스 (> CPU 수이면 포화)
# b: 블로킹된 프로세스 (I/O 대기)
# si/so: swap in/out (>0이면 메모리 부족)
# bi/bo: 블록 I/O (read/write)

# /proc/meminfo 핵심 항목
grep -E "^(MemTotal|MemFree|MemAvailable|Buffers|Cached|SwapTotal|SwapFree|Dirty|Writeback|Slab|SReclaimable|AnonPages|Mapped|Shmem|HugePages)" /proc/meminfo

# /proc/vmstat: 상세 페이지 이벤트 카운터
grep -E "pgfault|pgmajfault|pswpin|pswpout|pgalloc|pgfree|compact_|oom_kill" /proc/vmstat
# pgfault: 마이너 페이지 폴트 (정상적 요구 페이징)
# pgmajfault: 메이저 페이지 폴트 (디스크 I/O 필요 → 성능 저하)
# compact_*: 메모리 컴팩션 활동 (높으면 단편화)

# PSI(Pressure Stall Information) — 메모리 압박 지표
cat /proc/pressure/memory
# some avg10=5.20 avg60=2.10 avg300=0.80 total=345678
# full avg10=1.50 avg60=0.60 avg300=0.20 total=112233
# some > 10%: 메모리 경쟁 중, full > 5%: 심각한 메모리 부족

Slab 분석

# slabtop: 실시간 slab 캐시 사용량
slabtop -o -s c    # 캐시 크기순 정렬 (1회 출력)
slabtop -s a       # 활성 객체수순 정렬 (실시간)

# /proc/slabinfo: 상세 slab 통계
head -2 /proc/slabinfo && sort -k 3 -rn /proc/slabinfo | head -10
# name      active_objs  num_objs  objsize  ...
# dentry         45123     48000      192
# → dentry 캐시가 가장 많은 메모리 사용 중

# SLUB 디버그 통계 (CONFIG_SLUB_STATS 필요)
cat /sys/kernel/slab/kmalloc-256/alloc_calls | head
# → 어떤 콜사이트에서 kmalloc-256 객체를 많이 할당하는지

# slab 메모리 회수 압력 조절
# vfs_cache_pressure: 100이 기본, 높이면 dentry/inode 캐시 공격적 회수
sysctl vm.vfs_cache_pressure=150

페이지 할당 추적

# 페이지 할당 tracepoint 활성화
echo 1 > /sys/kernel/tracing/events/kmem/mm_page_alloc/enable
cat /sys/kernel/tracing/trace_pipe
# → 어떤 함수에서 페이지를 할당하는지 콜스택 포함

# page_owner: 모든 페이지의 할당자 추적 (부팅 시 활성화 필요)
# 커널 부트 파라미터: page_owner=on
cat /sys/kernel/debug/page_owner | head -50
# → 각 페이지의 할당 콜스택, 할당 시점, order 확인
# → 페이지 "어디서 할당되었는데 해제되지 않았나" 추적에 유용

# /proc/buddyinfo: 버디 할당자 단편화 상태
cat /proc/buddyinfo
# Node 0, zone  Normal   3245  1567  823  412  205  102  51  25  12  6  3
# → 각 열: order 0~10 (4KB~4MB) 프리 블록 수
# → 큰 order(4+)에 프리 블록이 없으면 단편화 심각

# 메모리 컴팩션 수동 트리거
echo 1 > /proc/sys/vm/compact_memory

# kmemleak: 커널 메모리 누수 탐지 (CONFIG_DEBUG_KMEMLEAK)
echo scan > /sys/kernel/debug/kmemleak
cat /sys/kernel/debug/kmemleak
# unreferenced object 0xffff888012345678 (size 128):
#   comm "kworker/0:1", pid 123, jiffies 4294967295
#   backtrace: ... → 할당 콜스택 표시

DAMON (Data Access MONitor)

DAMON(Linux 5.15+)은 커널 레벨에서 데이터 접근 패턴을 낮은 오버헤드로 모니터링하고, 결과에 기반한 자동 최적화(proactive reclaim, LRU 정렬)를 수행합니다.

# DAMON 커널 설정
CONFIG_DAMON=y
CONFIG_DAMON_VADDR=y        # 가상 주소 공간 모니터링
CONFIG_DAMON_PADDR=y        # 물리 주소 공간 모니터링
CONFIG_DAMON_SYSFS=y        # sysfs 인터페이스
CONFIG_DAMON_RECLAIM=y      # 선제적 메모리 회수
CONFIG_DAMON_LRU_SORT=y     # LRU 리스트 자동 정렬

# damo 사용자 도구 설치
pip3 install damo

# 프로세스의 메모리 접근 패턴 모니터링
sudo damo record $(pidof myapp) -o damon.data
# → 5초 간격으로 메모리 영역별 접근 빈도를 기록

# 접근 패턴 시각화
sudo damo report heats -i damon.data
# → 핫/콜드 메모리 영역 히트맵

# DAMON 기반 선제적 회수 (proactive reclaim)
# 접근 빈도가 낮은 페이지를 능동적으로 회수
echo Y > /sys/module/damon_reclaim/parameters/enabled
echo 5000000 > /sys/module/damon_reclaim/parameters/min_age
# → 5초 이상 접근되지 않은 페이지를 선제 회수

NUMA 메모리 분석

# NUMA 토폴로지 확인
numactl --hardware
# available: 2 nodes (0-1)
# node 0 cpus: 0-15
# node 1 cpus: 16-31
# node distances:
# node   0   1
#   0:  10  21    ← 원격 접근이 2.1배 느림
#   1:  21  10

# 프로세스별 NUMA 메모리 배치 확인
numastat -p $(pidof myapp)
# → 노드별 할당량, 히트/미스 비율
# → other_node > 10%이면 NUMA 배치 최적화 필요

# NUMA 바인딩으로 실행
numactl --membind=0 --cpunodebind=0 ./app  # 노드 0에 메모리+CPU 고정
numactl --interleave=all ./app              # 메모리를 노드 간 인터리브

# 자동 NUMA 밸런싱 (커널 기능)
cat /proc/sys/kernel/numa_balancing
# 1: 활성화 (기본). 커널이 접근 패턴 분석 후 페이지 자동 이동
# 0: 비활성화 (수동 NUMA 바인딩 시)

# NUMA 밸런싱 통계
grep -E "numa_" /proc/vmstat
# numa_hit: 로컬 노드 할당 성공
# numa_miss: 원격 노드에서 할당 (성능 저하)
# numa_pages_migrated: 자동 마이그레이션된 페이지 수

# perf로 NUMA 미스 프로파일링
perf stat -e node-loads,node-load-misses,node-stores,node-store-misses -a -- sleep 10

Huge Pages 튜닝

# Huge Pages: TLB 미스 감소를 통한 성능 향상

# 정적 Huge Pages (2MB) 할당
echo 1024 > /proc/sys/vm/nr_hugepages    # 1024 × 2MB = 2GB
cat /proc/meminfo | grep HugePages
# HugePages_Total:  1024
# HugePages_Free:   1024
# Hugepagesize:     2048 kB

# NUMA 노드별 할당
echo 512 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages
echo 512 > /sys/devices/system/node/node1/hugepages/hugepages-2048kB/nr_hugepages

# 1GB Huge Pages (커널 부트 파라미터 필요)
# hugepagesz=1G hugepages=16 default_hugepagesz=1G

# THP (Transparent Huge Pages) 설정
cat /sys/kernel/mm/transparent_hugepage/enabled
# [always] madvise never
# always: 모든 할당에 THP 시도
# madvise: MADV_HUGEPAGE가 설정된 영역만
# never: THP 비활성화

# DB 워크로드: madvise 권장 (예측 불가 지연 방지)
echo madvise > /sys/kernel/mm/transparent_hugepage/enabled
echo madvise > /sys/kernel/mm/transparent_hugepage/defrag

# THP 컴팩션으로 인한 지연 확인
grep -E "thp_fault_alloc|thp_fault_fallback|thp_collapse_alloc" /proc/vmstat
# thp_fault_fallback이 높으면 → 단편화로 THP 실패 빈번

메모리 튜닝 파라미터 종합

파라미터경로기본값설명
vm.swappiness/proc/sys/vm/60스왑 적극성 (0: 거의 안 함, 200: 매우 적극적)
vm.dirty_ratio/proc/sys/vm/20프로세스가 writeback을 기다리기 시작하는 dirty 비율(%)
vm.dirty_background_ratio/proc/sys/vm/10백그라운드 writeback 시작 임계값(%)
vm.min_free_kbytes/proc/sys/vm/자동 계산비상 메모리 예약 (워터마크 조정, OOM 방지)
vm.zone_reclaim_mode/proc/sys/vm/0NUMA 존 회수 정책 (1: 로컬 존 우선 회수)
vm.vfs_cache_pressure/proc/sys/vm/100dentry/inode 캐시 회수 압력 (높을수록 공격적)
vm.overcommit_memory/proc/sys/vm/0메모리 오버커밋 (0: 휴리스틱, 1: 항상 허용, 2: 제한)
vm.watermark_boost_factor/proc/sys/vm/15000단편화 방지를 위한 워터마크 부스트 (0: 비활성화)

스케줄러 튜닝

스케줄러는 CPU 시간 할당을 결정하므로 워크로드 특성에 맞는 튜닝이 성능에 직접적 영향을 미칩니다. Linux는 CFS(Completely Fair Scheduler), RT(Real-Time), Deadline 세 가지 스케줄링 클래스를 제공하며, 6.6+ 커널에서는 CFS에 EEVDF 알고리즘이 반영되었습니다.

Linux 스케줄링 클래스 우선순위 계층 SCHED_DEADLINE (최고 우선순위) CBS + EDF 알고리즘 | runtime/deadline/period 파라미터 | 주기적 실시간 태스크 SCHED_FIFO / SCHED_RR (RT 클래스) 고정 우선순위 1~99 | FIFO: 양보 없음 | RR: 타임슬라이스 기반 라운드로빈 SCHED_NORMAL / SCHED_BATCH / SCHED_IDLE (CFS 클래스) 가상 런타임(vruntime) 기반 공정 스케줄링 | nice -20~19 | 6.6+: EEVDF 반영 BATCH: 인터랙티브 보너스 없음 (배치 작업) | IDLE: 다른 태스크 없을 때만 실행 우선순위 낮음 → chrt: 정책/우선순위 변경 | taskset/cpuset: CPU 친화도 | cgroup cpu.max: 대역폭 제한 | nice/renice: CFS 가중치

CFS 튜너블 파라미터

파라미터기본값설명튜닝 방향
base_slice_ns커널/CONFIG별 상이CFS 슬라이스 기준값 (/sys/kernel/debug/sched/)줄이면 반응성↑ 처리량↓
sched_min_granularity_ns750,000 (0.75ms)태스크 최소 실행 시간줄이면 지연↓ 컨텍스트 스위치↑
sched_wakeup_granularity_ns1,000,000 (1ms)선점 결정 임계값줄이면 선점 빈도↑ (대화형 향상)
sched_migration_cost_ns500,000 (0.5ms)CPU 마이그레이션 비용 추정치높이면 마이그레이션↓ 캐시 친화도↑
sched_nr_migrate32로드밸런싱 시 이동할 최대 태스크 수줄이면 밸런싱 안정적, RT 지연↓
sched_autogroup_enabled1세션 기반 자동 그룹화 (데스크톱 반응성)서버에서는 0으로 비활성화 권장
sched_child_runs_first0fork 후 자식 프로세스 우선 실행exec 빈번 시 1로 설정 (CoW 활용)
# CFS 튜너블 확인 (커널 디버그 인터페이스)
cat /sys/kernel/debug/sched/base_slice_ns
cat /proc/sys/kernel/sched_min_granularity_ns
cat /proc/sys/kernel/sched_migration_cost_ns

# 서버 워크로드: 처리량 최적화 (컨텍스트 스위치 최소화)
sysctl kernel.sched_min_granularity_ns=10000000    # 10ms
sysctl kernel.sched_wakeup_granularity_ns=15000000  # 15ms
sysctl kernel.sched_migration_cost_ns=5000000      # 5ms (마이그레이션 억제)

# 대화형 워크로드: 반응성 최적화
sysctl kernel.sched_min_granularity_ns=300000      # 0.3ms
sysctl kernel.sched_wakeup_granularity_ns=500000   # 0.5ms

실시간(RT) 스케줄링 튜닝

RT 태스크는 CFS 태스크보다 항상 우선 실행되므로, 잘못된 설정은 시스템 전체를 멈출 수 있습니다. RT 스로틀링은 이를 방지하는 안전장치입니다.

# RT 스로틀링: RT 태스크의 CPU 독점 방지
cat /proc/sys/kernel/sched_rt_runtime_us    # 기본 950000 (95%)
cat /proc/sys/kernel/sched_rt_period_us     # 기본 1000000 (1초)
# → RT 태스크는 1초 중 최대 950ms만 실행, 나머지 50ms는 CFS에 양보

# RT 스로틀링 비활성화 (주의: RT 태스크가 CPU 100% 점유 가능)
echo -1 > /proc/sys/kernel/sched_rt_runtime_us

# SCHED_FIFO로 실행 (고정 우선순위, 양보 없음)
chrt -f 80 ./realtime_app

# SCHED_RR로 실행 (같은 우선순위 내 라운드로빈)
chrt -r 80 ./realtime_app

# SCHED_DEADLINE으로 실행 (주기적 실시간 태스크)
# runtime=10ms, deadline=30ms, period=30ms
chrt -d --sched-runtime 10000000 \
        --sched-deadline 30000000 \
        --sched-period 30000000 0 ./periodic_app

# 현재 프로세스의 스케줄링 정책 확인
chrt -p $(pidof myapp)
# pid 1234's current scheduling policy: SCHED_OTHER
# pid 1234's current scheduling priority: 0

# RT 우선순위 범위 확인
chrt -m
# SCHED_FIFO min/max priority : 1/99
# SCHED_RR   min/max priority : 1/99

CPU 친화도 및 격리

# CPU 고정 (CPU affinity)
taskset -c 2,3 ./app               # CPU 2,3에 고정 (실행 시)
taskset -cp 2,3 $(pidof myapp)    # 실행 중인 프로세스 변경

# CPU 격리: 커널 부트 파라미터 (스케줄러/인터럽트에서 제외)
# isolcpus=2,3          — 스케줄러에서 제외
# nohz_full=2,3         — 틱리스 모드 (tick interrupt 제거)
# rcu_nocbs=2,3         — RCU 콜백을 다른 CPU로 오프로드
# irqaffinity=0,1       — IRQ를 CPU 0,1로 제한

# 인터럽트 친화도 설정 (특정 IRQ를 특정 CPU로)
echo 3 > /proc/irq/42/smp_affinity  # IRQ 42를 CPU 0,1로 (비트마스크)

# 현재 인터럽트 분배 확인
cat /proc/interrupts | head -20

# cpuset(cgroup v2)으로 CPU 파티셔닝
mkdir /sys/fs/cgroup/latency-sensitive
echo "2-3" > /sys/fs/cgroup/latency-sensitive/cpuset.cpus
echo "0" > /sys/fs/cgroup/latency-sensitive/cpuset.mems
echo $$ > /sys/fs/cgroup/latency-sensitive/cgroup.procs

cgroup v2 CPU 컨트롤러

# cgroup v2: CPU 대역폭 제한
# cpu.max: "quota period" (마이크로초)
echo "100000 1000000" > /sys/fs/cgroup/mygroup/cpu.max
# → 1초(period) 중 100ms(quota) = 10% CPU

# 제한 없음 (기본값)
echo "max 100000" > /sys/fs/cgroup/mygroup/cpu.max

# CPU 가중치 (nice와 유사, 1~10000, 기본 100)
echo 200 > /sys/fs/cgroup/mygroup/cpu.weight
# → 기본(100) 대비 2배 CPU 시간 할당

# CPU 사용량 통계
cat /sys/fs/cgroup/mygroup/cpu.stat
# usage_usec: 총 CPU 사용 시간
# user_usec: 사용자 공간
# system_usec: 커널 공간
# nr_periods: 주기 수
# nr_throttled: 스로틀링 횟수
# throttled_usec: 스로틀링된 총 시간

# CPU pressure (PSI) 모니터링
cat /sys/fs/cgroup/mygroup/cpu.pressure
# some avg10=5.00 avg60=3.20 avg300=1.80 total=182345
# → some avg10 > 10이면 CPU 경쟁 심각

EEVDF 스케줄러 (6.6+)

Linux 6.6부터 CFS의 내부 알고리즘에 EEVDF(Earliest Eligible Virtual Deadline First)가 반영되었습니다. 기존 CFS의 vruntime 기반 공정 스케줄링을 유지하면서, "가상 데드라인" 개념을 추가하여 짧은 작업의 반응성을 개선합니다.

특성기존 CFSEEVDF (6.6+)
스케줄링 기준최소 vruntime최소 vruntime + 가상 데드라인
짧은 태스크vruntime만으로 공정 분배데드라인이 빨라 더 빨리 선점 가능
슬라이스 결정sched_latency / nr_running요청 기반 슬라이스 + base_slice_ns
선점 조건vruntime 차이 > granularityeligible + 데드라인 비교
성능 영향대화형 지연↓, 처리량 동등
ℹ️

EEVDF 실전 영향: 대부분의 워크로드에서 EEVDF는 기존 CFS와 동일하게 동작합니다. 차이가 두드러지는 경우: (1) 짧은 요청-응답 태스크(웹 서버, DB)가 긴 배치 작업과 경쟁할 때 반응성 향상, (2) sched_latency_ns 등 일부 튜너블이 EEVDF에서는 무시되거나 의미가 변경됨. 6.6+ 커널에서 기존 CFS 튜닝 스크립트를 검증 없이 적용하지 마세요.

스케줄링 성능 모니터링

# 런큐 지연 측정 (BPF): 태스크가 CPU를 기다린 시간
runqlat-bpfcc -m 10
# 출력: 대기 시간 히스토그램 (ms)
#   usecs   : count    distribution
#   0 -> 1  : 1523    |********                |
#   2 -> 3  : 4820    |*************************|
#   4 -> 7  : 890     |****                     |

# 컨텍스트 스위치 분석
perf stat -e context-switches,cpu-migrations -a -- sleep 10

# 스케줄러 이벤트 추적
perf sched record -- sleep 5
perf sched latency --sort max    # 태스크별 최대 지연
perf sched timehist              # 시간순 스케줄링 이벤트
perf sched map                   # CPU별 태스크 매핑 시각화

# PSI (Pressure Stall Information) — 리소스 부족 지표
cat /proc/pressure/cpu
# some avg10=2.50 avg60=1.80 avg300=0.90 total=823456
# → some: 하나 이상의 태스크가 CPU 대기 중인 시간 비율(%)
# → avg10 > 25%이면 CPU 부족, 스케줄러 또는 자원 확대 필요

벤치마킹 방법론

성능 측정의 신뢰도를 확보하지 않으면, 최적화의 효과를 정확히 판단할 수 없습니다. 벤치마킹은 통계적 엄밀함과 환경 제어가 핵심입니다.

신뢰할 수 있는 벤치마크 워크플로 환경 제어 노이즈 제거 터보/cstate 고정 워밍업 캐시/JIT 안정화 사전 실행 3~5회 반복 측정 N >= 10회 반복 평균 + 표준편차 통계 검증 CoV < 5% A/B 유의성 검정 회귀 검증 perf diff CI 자동 벤치마크 주요 노이즈 원인 및 제거 방법 CPU 주파수 변동 → cpupower frequency-set -g performance | 인터럽트 → irqbalance 중지, isolcpus ASLR → echo 0 > randomize_va_space | 투명 대규모 페이지 → THP=madvise | NTP/cron → 비활성화 NUMA 불균형 → numactl --membind | 열 스로틀링 → 냉각 확인, 전력 제한 해제

벤치마크 환경 제어

# ── 노이즈 제거 스크립트 ──

# CPU 주파수 고정 (터보 부스트/절전 방지)
cpupower frequency-set -g performance
# 또는
echo performance | tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor

# 터보 부스트 비활성화 (Intel)
echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo

# C-State 제한 (딥 슬립 방지)
# 커널 부트: processor.max_cstate=1 intel_idle.max_cstate=0

# SMT(하이퍼스레딩) 비활성화 (정밀 측정 시)
echo off > /sys/devices/system/cpu/smt/control

# ASLR 비활성화 (주소 공간 배치 변동 제거)
echo 0 > /proc/sys/kernel/randomize_va_space

# irqbalance 중지
systemctl stop irqbalance

# 백그라운드 서비스 최소화
systemctl isolate multi-user.target

# ── 반복 측정 도구 ──
# perf stat -r: 반복 실행 + 통계
perf stat -r 10 -e cycles,instructions,cache-misses ./benchmark
# → 평균 ± stddev (%, 5% 초과 시 노이즈 의심)

# hyperfine: 사용자 공간 벤치마크 도구
hyperfine --warmup 3 --min-runs 10 \
  './benchmark_v1' './benchmark_v2'
# → 통계적 비교 (평균, 중간값, 신뢰구간, p-value)

# 커널 벤치마크
perf bench sched messaging -g 20 -t -l 1000  # 스케줄러
perf bench mem memcpy -s 4GB -l 5              # 메모리 대역폭
perf bench futex lock-pi -t 8                  # futex 성능
⚠️

통계적 함정: (1) CoV(변동계수) > 5%이면 측정이 불안정 — 환경 노이즈를 먼저 제거하세요. (2) 이상치는 중간값(median)으로 걸러내세요. 평균만 보면 단일 스파이크에 속을 수 있습니다. (3) 작은 차이(< 3%)를 주장하려면 유의성 검정(Welch's t-test 또는 Mann-Whitney U)이 필요합니다. (4) perf stat -r의 stddev가 측정값의 1% 이내인지 확인하세요.

성능 안티패턴

커널/시스템 프로그래밍에서 자주 발생하는 성능 문제 패턴과 해결 방향입니다.

안티패턴증상진단 도구해결 방향
과도한 시스콜 높은 시스콜 빈도, 높은 sys% CPU perf trace -s, strace -c 배치 처리, io_uring, mmap, sendfile
False sharing 높은 HITM, 코어 수 증가에 비례 성능 저하 perf c2c ____cacheline_aligned, 구조체 패딩, per-CPU 변수
Lock contention 코어 증가 시 성능 정체/역전 perf lock, lockstat 잠금 세분화, RCU, lockless, per-CPU
NUMA 미배치 원격 메모리 접근 높음, IPC 저하 numastat, pcm-numa numactl --membind, NUMA-aware 할당
TLB thrashing 높은 dTLB-load-misses, 큰 작업 세트 perf stat -e dTLB-load-misses Huge pages (THP 또는 hugetlbfs)
캐시 오염 핫 데이터의 L1/L2 캐시 미스율 증가 perf stat -d, perf mem 데이터 구조 압축, hot/cold 분리, NT store
메모리 할당 폭주 높은 pgfault, slab 증가, kswapd CPU 소모 vmstat, slabtop, page_owner 오브젝트 풀, slab 캐시, 사전 할당
인터럽트 폭풍 높은 hi/si CPU, 인터럽트 편중 /proc/interrupts, hardirqs 인터럽트 코얼레싱, NAPI, RSS/RPS 분산
컨텍스트 스위치 폭주 cs > 100K/s, 높은 sys% CPU vmstat, perf sched 스레드 수 축소, 비동기 I/O, 스핀 대기
Writeback 스톰 간헐적 I/O 지연 스파이크 iostat, vmstat (bo열) dirty_ratio 낮춤, dirty_expire 줄임, I/O 스케줄러

안티패턴 진단 실전 예제

# ── 과도한 시스콜 진단 ──
perf trace -s ./app  # 시스콜별 횟수/시간 통계
# write: 1,234,567 calls → 배치 처리 또는 io_uring 고려
# futex: 890,123 calls → 락 경쟁 또는 조건 변수 남용

# ── TLB thrashing 확인 ──
perf stat -e dTLB-load-misses,dTLB-store-misses,iTLB-load-misses -a -- sleep 10
# dTLB-load-misses > 1% → Huge Pages 도입 검토

# ── 캐시 오염 패턴 확인 ──
perf stat -e L1-dcache-load-misses,L1-dcache-loads,LLC-load-misses,LLC-loads -a -- sleep 10
# L1 미스율 > 10% → 데이터 구조 캐시라인 최적화
# LLC 미스율 > 20% → 작업 세트 > LLC 크기, 메모리 BW 확인

# ── 스케줄러 병목 확인 ──
perf sched record -- sleep 10
perf sched latency --sort max
# 최대 지연 > 10ms이면 스케줄러 튜닝 또는 CPU 부족

# ── 종합 60초 진단 체크리스트 (Brendan Gregg 스타일) ──
uptime                   # 1. 로드 에버리지 추세
dmesg -T | tail          # 2. 커널 에러/OOM
vmstat 1 5               # 3. CPU/메모리/스왑/I/O 전체 그림
mpstat -P ALL 1 3        # 4. CPU별 사용률 불균형
iostat -xz 1 3           # 5. 디스크별 I/O 통계
free -h                  # 6. 메모리 사용 현황
sar -n DEV 1 3           # 7. 네트워크 인터페이스 통계
sar -n TCP,ETCP 1 3      # 8. TCP 연결/재전송 통계
top -bn1 | head -20      # 9. 상위 프로세스
cat /proc/pressure/*     # 10. PSI (CPU/메모리/I/O 압박)

네트워크 성능 최적화

네트워크 스택은 NIC 하드웨어부터 소켓 레이어까지 여러 계층으로 구성되며, 각 계층에서 최적화 포인트가 다릅니다.

네트워크 스택 최적화 포인트 Application (socket API) SO_REUSEPORT, TCP_NODELAY io_uring, sendfile, splice TCP / UDP tcp_rmem/wmem, BBR TFO, tcp_tw_reuse IP / Netfilter / Routing conntrack 최적화, nf_tables ip_forward_update_priority Qdisc / Traffic Control fq (BBR 필수), fq_codel noqueue (컨테이너 veth) Driver / NAPI / XDP RSS/RPS/RFS/XPS 코얼레싱, NAPI 가중치 NIC Hardware (DMA Ring Buffer) ethtool -G (링 버퍼 크기) XDP 바이패스

XDP (eXpress Data Path)

XDP는 NIC 드라이버 수준에서 패킷을 처리하여, 커널 네트워크 스택을 우회합니다. DDoS 필터링, 로드밸런싱, 패킷 포워딩에서 10~100배 성능 향상이 가능합니다.

# XDP 프로그램 로드 (ip 명령)
ip link set dev eth0 xdp obj xdp_prog.o sec xdp

# XDP 모드
# xdpdrv: NIC 드라이버 내장 (최고 성능, 드라이버 지원 필요)
# xdpgeneric: 범용 (모든 NIC, 성능 제한)
# xdpoffload: NIC 하드웨어 오프로드 (Netronome 등)
ip link set dev eth0 xdpdrv obj xdp_prog.o sec xdp

# XDP 통계 확인
bpftool prog show
bpftool map dump name xdp_stats

# AF_XDP: 커널 우회 사용자 공간 패킷 처리
# → DPDK 대안, 커널 관리 유지 + 고성능
# libbpf의 xsk (XDP socket) API 사용

인터럽트 및 패킷 분산 최적화

기술계층설명설정 방법
RSS하드웨어NIC이 패킷 해시로 큐 분산ethtool -L eth0 combined 8
RPS소프트웨어소프트 IRQ를 CPU에 분산 (RSS 미지원 NIC)echo ff > /sys/.../rps_cpus
RFS소프트웨어패킷을 소비 스레드의 CPU로 전달rps_flow_cnt + rps_sock_flow_entries
XPS송신 측송신 큐를 CPU에 매핑echo 1 > /sys/.../tx-0/xps_cpus
Busy polling소켓소켓 폴링으로 인터럽트 지연 제거sysctl net.core.busy_poll=50
GRO/GSO드라이버/스택패킷 집계로 프로토콜 처리 횟수 감소ethtool -K eth0 gro on gso on
# 네트워크 성능 진단
ethtool -S eth0 | grep -E "drop|error|miss"  # NIC 드롭/에러
cat /proc/net/softnet_stat                       # CPU별 패킷 처리 통계
# 컬럼: processed, dropped, time_squeeze, ...
# dropped > 0: 백로그 부족 → netdev_max_backlog 증가
# time_squeeze > 0: NAPI 처리 시간 부족 → netdev_budget 증가

# NAPI 가중치/예산 조정
sysctl -w net.core.netdev_budget=600         # 기본 300, 높이면 처리량↑ 지연↑
sysctl -w net.core.netdev_budget_usecs=8000  # NAPI 최대 처리 시간(us)

# NIC 링 버퍼 크기
ethtool -g eth0                   # 현재 설정
ethtool -G eth0 rx 4096 tx 4096  # 링 버퍼 확대 (드롭 방지)

# TCP 연결 추적 튜닝 (conntrack)
sysctl -w net.netfilter.nf_conntrack_max=1048576
sysctl -w net.netfilter.nf_conntrack_buckets=262144
💡

네트워크 최적화 우선순위: (1) NIC 드롭/에러 확인 (ethtool -S) → 링 버퍼/코얼레싱. (2) 인터럽트 분산 (/proc/interrupts) → RSS/RPS. (3) TCP 튜닝 (버퍼, BBR). (4) 커널 우회가 필요하면 XDP/AF_XDP. NAPI 상세 구조는 NAPI 페이지를 참고하세요.

I/O 성능 최적화

스토리지 I/O 성능은 I/O 스케줄러 선택, readahead 정책, 파일시스템 마운트 옵션, 그리고 블록 레이어 파라미터에 의해 결정됩니다.

I/O 스케줄러 선택

스케줄러알고리즘적합 워크로드디바이스
none (noop)큐에 넣고 바로 전달고성능 NVMe, 가상화 게스트NVMe, virtio-blk
mq-deadline데드라인 기반 + 배치DB, 지연 민감, 혼합 워크로드SSD, HDD 범용
bfqBudget Fair Queueing데스크톱 반응성, 공정 I/O회전 디스크, 느린 SSD
kyber토큰 기반 + 지연 목표서버 고부하, 지연 SLO고성능 SSD
# I/O 스케줄러 변경
cat /sys/block/sda/queue/scheduler      # 현재 스케줄러 확인
echo mq-deadline > /sys/block/sda/queue/scheduler

# 영구 설정: udev 규칙
# /etc/udev/rules.d/60-io-scheduler.rules
# ACTION=="add|change", KERNEL=="sd*", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="none"
# ACTION=="add|change", KERNEL=="sd*", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="mq-deadline"

# mq-deadline 파라미터 튜닝
echo 150 > /sys/block/sda/queue/iosched/read_expire   # 읽기 데드라인 (ms)
echo 5000 > /sys/block/sda/queue/iosched/write_expire  # 쓰기 데드라인 (ms)
echo 16 > /sys/block/sda/queue/iosched/fifo_batch       # 배치 크기

# I/O 성능 모니터링
iostat -xz 1
# 주요 지표:
# %util: 100%에 가까우면 포화 (HDD에서 중요, NVMe에서는 의미 제한적)
# await: 평균 I/O 완료 시간 (ms) — HDD: ~10ms, SSD: <1ms, NVMe: <0.1ms
# avgqu-sz: 평균 큐 깊이 — > 1이면 요청 대기 중
# r_await/w_await: 읽기/쓰기별 지연 (불균형 확인)

# Direct I/O vs Buffered I/O
# Direct I/O: 페이지 캐시 우회, DB에서 자체 버퍼 관리 시 사용
# O_DIRECT 플래그 또는 mount -o direct
# → 이중 캐싱 방지, DMA 직접 전송, 정렬 요구사항 주의

# fio: I/O 벤치마크 도구
fio --name=test --rw=randread --bs=4k --numjobs=4 \
    --iodepth=32 --size=1G --filename=/dev/nvme0n1 --direct=1
# → IOPS, 대역폭, 지연 시간 분포 (p50, p99, p99.9)

실전 사례 분석

일반적인 성능 문제를 발견하고 해결하는 전체 과정을 단계별로 제시합니다.

사례 1: CPU 바운드 — IPC 저하 원인 추적

# 증상: 애플리케이션이 CPU 100% 사용하지만 예상보다 느림

# 1단계: IPC 확인
perf stat -d -- ./slow_app
# → IPC: 0.45 (매우 낮음, 메모리 스톨 의심)
# → LLC-load-misses: 15.2% (높음)

# 2단계: 핫스팟 함수 식별
perf record -g -F 99 -- ./slow_app
perf report
# → process_data() 함수가 CPU의 45% 차지

# 3단계: TMA Level 2 분석
sudo python3 toplev.py -l2 -- ./slow_app
# → Backend Bound: 62% (Memory Bound 55%, Core Bound 7%)

# 4단계: 메모리 접근 패턴 분석
perf record -e mem-loads:pp -c 10000 -- ./slow_app
perf mem report
# → 대부분의 메모리 로드가 DRAM에서 서비스됨 (L3 미스)

# 5단계: 원인 - 연결 리스트 순회 시 캐시 미스
# 해결: 배열 기반 구조로 변경 + prefetch 적용
# 결과: IPC 0.45 → 1.8, 처리 시간 75% 감소

사례 2: 지연 스파이크 — 간헐적 느림 원인

# 증상: p99 지연이 p50 대비 100배 높음 (100ms vs 1ms)

# 1단계: 시스템 전체 상태 확인
vmstat 1 30
# → 간헐적으로 bo(block out) 폭증, wa(I/O wait) 급등

# 2단계: dirty writeback 확인
grep -E "Dirty|Writeback" /proc/meminfo
# → Dirty: 1,200,000 kB (1.2GB dirty 페이지 누적)

# 3단계: 원인 — dirty_ratio 도달 시 동기 writeback
# 해결:
sysctl -w vm.dirty_ratio=10               # 동기 writeback 임계값 낮춤
sysctl -w vm.dirty_background_ratio=5    # 비동기 writeback 더 빨리 시작
sysctl -w vm.dirty_expire_centisecs=1000 # dirty 만료 10초로 단축
# 결과: p99 지연 100ms → 5ms

사례 3: 확장성 문제 — 코어 추가해도 성능 정체

# 증상: 4코어 → 32코어로 증가해도 처리량 2배에서 정체

# 1단계: 잠금 경쟁 확인
perf lock record -a -- sleep 10
perf lock report
# → global_lock: contended 89,234회, avg wait 45us

# 2단계: 경쟁 콜스택 확인
perf lock contention -b -s 5
# → 모든 스레드가 하나의 전역 해시 테이블 잠금을 경쟁

# 3단계: false sharing 확인
perf c2c record -a -- sleep 10
perf c2c report --stdio
# → shared_counter와 unrelated_field가 같은 캐시라인

# 해결:
# (1) 글로벌 잠금 → per-bucket 잠금으로 세분화
# (2) 공유 카운터 → per-CPU 카운터 + 주기적 합산
# (3) ____cacheline_aligned로 false sharing 제거
# 결과: 32코어에서 처리량 28배 (거의 선형 확장)

최적화와 관련된 다른 주제를 더 깊이 이해하고 싶다면 다음 문서를 참고하세요.