성능 최적화 (Performance Optimization)

Linux 커널 성능 최적화를 위한 계측 중심 실전 가이드입니다. 이 문서는 개별 도구의 내부 구조보다 "어떤 순서로 병목(Bottleneck)을 좁히고, 어떤 지표를 조합해, 어떤 변경을 검증할 것인가"에 집중합니다. CPU·메모리·I/O 병목을 찾아내는 방법론과 회귀 방지형 튜닝 절차를 단계적으로 다룹니다.

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

핵심 요약

  • Profiling Toolsperf, ftrace, BPF 기반 도구로 CPU 사이클, 캐시 미스, 함수 호출 빈도 등을 정밀 계측하여 병목을 데이터로 식별합니다.
  • Hot Path Identificationperf top/perf report로 CPU 시간을 가장 많이 소비하는 함수(핫 패스)를 찾아 최적화 대상을 좁힙니다.
  • Cache Optimization — 데이터 지역성(Locality) 개선, 캐시 라인 정렬(__cacheline_aligned), false sharing 방지로 L1/L2/L3 캐시 적중률을 높입니다.
  • Compiler Optimizations-O2, LTO(Link Time Optimization), PGO(Profile-Guided Optimization), AutoFDO 등으로 컴파일러가 생성하는 기계어 품질을 개선합니다.
  • Lock Contention Analysisperf lock, lockstat으로 잠금 경합 지점을 찾고, per-CPU 변수, RCU, lockless 알고리즘으로 경합을 제거합니다.
  • Memory Allocation Patterns — slab 캐시, per-CPU 할당자, 오브젝트 풀링으로 빈번한 kmalloc/kfree 오버헤드를 줄이고 단편화를 방지합니다.
  • Kernel Config TuningCONFIG_PREEMPT 모델, 틱리스(NO_HZ_FULL), 스케줄러 파라미터 등 커널 빌드/런타임 설정이 성능에 미치는 영향을 파악합니다.
  • Benchmarking Methodology — 재현 가능한 환경에서 기준선(baseline)을 설정하고, 단일 변수만 변경하여 통계적으로 유의미한 차이를 검증하는 체계적 방법론입니다.

단계별 이해

  1. 프로파일링으로 병목 식별 — 추측이 아닌 데이터 기반으로 최적화 대상을 찾습니다.

    perf record -g로 콜 그래프를 수집하고, perf report/FlameGraph로 CPU 시간 분포를 시각화하여 핫 패스를 정확히 파악합니다.

  2. 캐시와 메모리 접근 패턴 분석 — 하드웨어 PMU 카운터로 캐시 미스율, TLB 미스, 메모리 대역폭을 측정합니다.

    perf stat -e cache-misses,cache-references로 캐시 적중률을 확인하고, 구조체 레이아웃 최적화(pahole)로 캐시 라인 활용을 개선합니다.

  3. 잠금 경합과 동기화 병목 해소perf lock/proc/lock_stat으로 경합이 심한 잠금을 찾고, 세분화(fine-grained locking)나 lockless 패턴으로 대체합니다.

    per-CPU 변수, RCU read-side, seqlock 등 읽기 우세 워크로드에 적합한 동기화 프리미티브를 선택합니다.

  4. 컴파일러 최적화와 커널 설정 조정 — LTO, PGO, -march=native 등 컴파일 옵션과 CONFIG_* 커널 설정이 성능에 미치는 영향을 측정합니다.

    NO_HZ_FULL로 전용 CPU의 틱 인터럽트를 제거하거나, isolcpus로 지연 민감 태스크를 격리하는 등 런타임 튜닝도 함께 적용합니다.

  5. 체계적 벤치마킹과 회귀 검증 — 재현 가능한 환경에서 기준선을 설정하고, 변경 전후 성능을 통계적으로 비교합니다.

    최소 3~5회 반복 측정으로 분산을 확인하고, 단일 변수만 변경하여 인과 관계를 명확히 합니다. CI에 성능 회귀 테스트를 통합하면 장기적 품질을 유지할 수 있습니다.

문서 역할: perf 서브시스템은 PMU와 명령어 수준 계측 원리를, Intel PCM은 언코어·메모리·전력 계측을, RTLA / timerlat / osnoise는 실시간(Real-time) 지연 분석을 맡습니다. 이 문서는 그 도구들을 어떻게 조합해 병목을 좁히는지에 초점을 둡니다.
관련 표준: Intel SDM (PMU, 성능 카운터), AMD APM (IBS, 성능 모니터링) — 커널 성능 프로파일링(Profiling)에서 참조하는 하드웨어 성능 모니터링 규격입니다. 종합 목록은 참고자료 — 표준 & 규격 섹션을 참고하세요.

성능 최적화 개요

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

성능 최적화 전체 워크플로 (Iterative Cycle) 1단계: 기준선 수립 (Baseline) perf stat -ddd ./workload vmstat 1 · iostat -xz 1 · sar -n DEV 1 cat /proc/pressure/* · turbostat IPC, 캐시 미스율, 시스콜 빈도, I/O 지연 기록 2단계: 병목 식별 (Identify) USE 방법론: 리소스별 U/S/E 점검 TSA: 스레드 상태 분석 (On-CPU/Sleep/Runnable) perf record -g → Flame Graph 생성 toplev -l1~l3: TMA 병목 카테고리 분류 3단계: 근본 원인 분석 (Root Cause) CPU 병목: perf annotate, perf c2c, toplev -l4+ 메모리 병목: pcm-memory, numastat, DAMON 잠금 병목: perf lock, lockstat, contention BPF I/O 병목: blktrace, biolatency, iostat await 네트워크 병목: ethtool -S, softnet_stat, tcpretrans 4단계: 최적화 적용 (Optimize) 코드 최적화: 구조체 정렬, prefetch, likely/unlikely 잠금 최적화: 세분화, RCU, per-CPU, lockless 커널 튜닝: sysctl, CONFIG, 스케줄러 파라미터 빌드 최적화: LTO, PGO, AutoFDO, -march=native I/O 최적화: io_uring, readahead, 스케줄러 선택 5단계: 검증 및 회귀 방지 (Verify & Prevent Regression) perf diff baseline.data optimized.data → 함수별 변화 비교 hyperfine --warmup 3 -r 10 'v1' 'v2' → 통계적 유의성 검정 CI/CD 벤치마크: 커밋별 자동 성능 테스트, CoV < 5% 확인 Differential Flame Graph: 빨강(회귀)/파랑(개선) 시각 비교 목표 미달 → 반복 목표 달성? → Yes: 문서화 및 배포 최적화 도메인별 주요 도구 매핑 CPU / 파이프라인 perf, toplev, PCM 메모리 / 캐시 perf c2c, DAMON, pcm-mem 잠금 / 동기화 lockstat, perf lock, BPF I/O / 스토리지 blktrace, biolatency, fio 네트워크 ethtool, XDP, tcpretrans

성능 모니터링 원리 (PMU)

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

PMU 하드웨어 아키텍처 상세 CPU Core (PMU) Fixed Counters (3개) IA32_FIXED_CTR0: INST_RETIRED IA32_FIXED_CTR1: CPU_CLK_UNHALTED IA32_FIXED_CTR2: REF_CLK 이벤트 고정, 변경 불가 Programmable Counters (4~8개) PMC0: EventSel=0x2E, UMask=0x41 PMC1: EventSel=0xC0, UMask=0x00 PMC2~N: 사용자 정의 이벤트 IA32_PERFEVTSELx MSR로 이벤트 선택 Control & Status IA32_PERF_GLOBAL_CTRL (활성화) IA32_PERF_GLOBAL_STATUS (오버플로) IA32_PERF_GLOBAL_OVF_CTRL (리셋) IA32_PEBS_ENABLE (정밀 샘플링) MSR을 통해 PMU 제어 커널 인터페이스 (perf_event 서브시스템) perf_event_open() 이벤트 멀티플렉싱 PMI 핸들러 (NMI) ring buffer 기록 mmap 공유 User Space perf stat perf record perf top perf c2c perf mem Intel PCM bcc/bpftrace 이벤트 멀티플렉싱 (Multiplexing) PMC 4~8개보다 많은 이벤트 동시 모니터링 시 → 커널이 시분할로 PMC 전환 → 결과에 추정값(스케일링 계수) 포함 → 그룹(-e '{A,B}')으로 동시 측정 보장

PMU 동작 메커니즘

구성 요소역할예시
성능 카운터 레지스터(Register)특정 이벤트 발생 횟수를 카운팅x86: PMC0~PMCn (일반적으로 4~8개)
이벤트 선택 레지스터카운팅할 이벤트 종류를 지정cycles, instructions, cache-misses, branch-misses
오버플로 인터럽트(Interrupt)카운터가 지정값에 도달하면 인터럽트 발생PMI(Performance Monitoring Interrupt)
PEBS 버퍼정밀 샘플링 데이터를 하드웨어 버퍼에 저장IP, 레지스터 상태, 메모리 주소 자동 기록
LBR 스택최근 분기 기록 (Last Branch Record)최근 8~32개 분기의 소스/타겟 주소

perf_event_open() 상세

perf_event_open(2)은 커널의 PMU 접근 인터페이스입니다. 이 시스템 콜을 통해 하드웨어/소프트웨어 이벤트를 프로그래밍하고, 파일 디스크립터(File Descriptor)를 반환받아 read()/mmap()으로 데이터를 수집합니다.

/* perf_event_open() 시스템 콜 인터페이스 */
#include <linux/perf_event.h>
#include <sys/ioctl.h>
#include <sys/mman.h>

struct perf_event_attr attr;
memset(&attr, 0, sizeof(attr));

/* 카운팅 모드: cycles 이벤트 */
attr.type           = PERF_TYPE_HARDWARE;
attr.config         = PERF_COUNT_HW_CPU_CYCLES;
attr.size           = sizeof(attr);
attr.disabled       = 1;          /* ioctl ENABLE로 시작 */
attr.exclude_kernel = 0;          /* 커널 이벤트 포함 */
attr.exclude_hv     = 1;          /* 하이퍼바이저 제외 */

/* 시스콜 호출: pid=0(자기 자신), cpu=-1(모든 CPU) */
int fd = perf_event_open(&attr, 0, -1, -1, 0);

/* 샘플링 모드: 100000 이벤트마다 샘플 */
attr.type           = PERF_TYPE_HARDWARE;
attr.config         = PERF_COUNT_HW_CPU_CYCLES;
attr.sample_period  = 100000;     /* 100K cycles마다 샘플 */
attr.sample_type    = PERF_SAMPLE_IP | PERF_SAMPLE_CALLCHAIN
                    | PERF_SAMPLE_TID | PERF_SAMPLE_TIME;
attr.precise_ip     = 2;          /* PEBS 정밀 샘플링 (:pp) */
attr.wakeup_events  = 100;        /* 100개 샘플마다 깨우기 */

/* mmap ring buffer로 샘플 데이터 수집 */
struct perf_event_mmap_page *header;
size_t mmap_size = (1 + 128) * sysconf(_SC_PAGESIZE);
header = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE,
              MAP_SHARED, fd, 0);

/* 이벤트 그룹: 리더 fd에 다른 이벤트를 연결 */
int leader_fd = perf_event_open(&cycles_attr, 0, -1, -1, 0);
int member_fd = perf_event_open(&insn_attr, 0, -1, leader_fd, 0);
/* → 동일 타임슬라이스에서 측정 → IPC 비율 정확 */

/* 카운터 활성화/비활성화/리셋 */
ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
ioctl(fd, PERF_EVENT_IOC_RESET, 0);

perf_event_attr 주요 필드

필드설명값 예시
type이벤트 소스PERF_TYPE_HARDWARE, PERF_TYPE_RAW, PERF_TYPE_TRACEPOINT
config이벤트 식별자PERF_COUNT_HW_CPU_CYCLES, raw: 0x003c
sample_periodN 이벤트마다 샘플100000 (주기), 0(카운팅)
sample_freq초당 샘플 수99 (freq_모드일 때)
sample_type샘플에 포함할 정보IP | CALLCHAIN | TID | TIME | ADDR
precise_ipPEBS 정밀도0(skid 있음), 1(:p), 2(:pp), 3(:ppp)
read_formatread() 반환 형식TOTAL_TIME_ENABLED | TOTAL_TIME_RUNNING (멀티플렉싱 보정)
exclude_kernel커널 이벤트 제외0/1 (perf_event_paranoid에 따라 제한)

샘플링 vs 카운팅

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

ℹ️

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

프로파일링 방법론

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

  1. 전체 지표 수집 (perf stat): IPC(Instructions Per Cycle), 캐시 미스율, 분기 예측(Branch Prediction) 실패율 등 시스템 전체의 성능 특성을 파악합니다. 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)

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

스레드 상태의미진단 도구최적화 방향
On-CPUCPU에서 실행 중perf record (On-CPU Flame Graph)알고리즘, 컴파일러 최적화(Compiler Optimization), SIMD
Runnable실행 가능하나 CPU 대기runqlat, perf sched latencyCPU 추가, 친화도(Affinity), 부하 분산(Load Balancing)
Sleep (I/O)I/O 완료 대기offcputime, biolatencyI/O 최적화, 캐싱, 비동기 I/O
Sleep (Lock)잠금(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) 시스템 콜(System Call)을 통해 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커널+사용자 공간(User Space)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은 카운팅 모드로 이벤트 총계를 수집합니다. 기본 사용법은 디버깅(Debugging) 페이지(Page)를 참조하고, 여기서는 분석적 해석에 집중합니다.

# 상세 카운터 (-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 미스, 메모리 대역폭(Bandwidth) 포화 확인
  • IPC 1.0~2.0: 혼합 워크로드 — 분기 예측 실패, 파이프라인(Pipeline) 스톨 확인
  • 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는 핫스팟 함수의 어셈블리(Assembly)/소스 코드에 샘플 분포를 매핑(Mapping)하여, 어떤 명령어가 가장 많은 사이클을 소모하는지 보여줍니다.

# 기본 사용법 (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 (프로세스(Process) 중단)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스케줄러(Scheduler) 컨텍스트 스위치, 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 커널 모듈(Kernel Module)코어별 성능 카운터, 전력 측정
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: 메모리 대역폭 모니터링

소켓(Socket)/채널별 메모리 대역폭(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 문자 장치(Character Device) 제공
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)이 준비되고, 런타임에 동적 패치(Patch)를 통해 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 인터페이스로, 공유 링 버퍼(Ring Buffer)를 통해 시스템 콜 오버헤드를 줄입니다. 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혼잡 제어(Congestion Control) 알고리즘 (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스왑(Swap) 적극성 (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)간접 호출 무결성(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선점(Preemption) 비활성화서버 처리량 최대화
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런큐(Runqueue) 대기 시간(Latency) 히스토그램-m (ms), -P (PID별)
cpudistOn-CPU 시간 분포-O (Off-CPU)
메모리cachestat페이지 캐시(Page Cache) 히트/미스율인터벌(초) 지정
memleak메모리 누수 탐지 (malloc/free)-p PID, -a (할당 추적)
oomkillOOM kill 이벤트 추적실시간 알림
디스크 I/Obiolatency블록 I/O 지연 히스토그램-mD (ms, 디스크별)
biosnoop개별 블록 I/O 요청 추적-Q (큐 시간 포함)
biotop프로세스별 블록 I/O (top 형식)인터벌 지정
네트워크tcpretransTCP 재전송(Retransmission) 추적-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와 함께 성능의 양대 축입니다. 물리 메모리(Physical Memory) 할당, 가상 메모리(Virtual Memory) 매핑, 페이지 캐시, 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/자동 계산비상 메모리 예약 (워터마크(Watermark) 조정, 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단편화(Fragmentation) 방지를 위한 워터마크 부스트 (0: 비활성화)

스케줄러 튜닝

스케줄러는 CPU 시간 할당을 결정하므로 워크로드 특성에 맞는 튜닝이 성능에 직접적 영향을 미칩니다. Linux는 CFS(Completely Fair Scheduler), RT(Real-Time), Deadline 세 가지 스케줄링 클래스(Scheduling Class)를 제공하며, 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)태스크(Task) 최소 실행 시간줄이면 지연↓ 컨텍스트 스위치↑
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 친화도 및 격리(Isolation)

# 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, 구조체(Struct) 패딩(Padding), 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 압박)

네트워크 성능 최적화

네트워크 스택(Network Stack)은 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 드라이버 수준에서 패킷(Packet)을 처리하여, 커널 네트워크 스택을 우회합니다. 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이 패킷 해시(Hash)로 큐 분산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소켓소켓 폴링(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
💡

네트워크 최적화 우선순위(Priority): (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 정책, 파일시스템(Filesystem) 마운트(Mount) 옵션, 그리고 블록 레이어 파라미터에 의해 결정됩니다.

I/O 스케줄러 선택

스케줄러알고리즘적합 워크로드디바이스
none (noop)큐에 넣고 바로 전달고성능 NVMe, 가상화(Virtualization) 게스트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배 (거의 선형 확장)

CPU 캐시 최적화

현대 CPU에서 성능 병목의 대부분은 메모리 접근 지연에서 발생합니다. L1 캐시 접근은 ~1ns이지만 DRAM 접근은 ~100ns로 100배 느립니다. 캐시라인(Cache Line) 크기(x86: 64바이트)를 이해하고 데이터 배치를 최적화하는 것이 핵심입니다.

CPU 캐시 계층 구조 및 접근 레이턴시 CPU 레지스터 ~0.3ns | 수 KB | 가장 빠름 L1 Cache (Data + Instruction) ~1ns (4 cycles) | 32~48 KB/코어 | 8-way set associative | 캐시라인 64B L2 Cache (Unified) ~4ns (12 cycles) | 256KB~1.25MB/코어 | 8~16-way | inclusive/non-inclusive L3 / LLC (Last Level Cache, 공유) ~12ns (36 cycles) | 3~100+MB/소켓 공유 | 12~20-way | MESI/MOESI 코히런시 DRAM (Main Memory) ~100ns (300 cycles) | 로컬 NUMA ~80ns, 원격 NUMA ~150ns | DDR5-4800: ~38GB/s/채널 레이턴시 비율 (L1 = 1 기준) L1: 1x L2: 4x L3: 12x DRAM: 100x

캐시라인 정렬 (Cacheline Alignment)

x86 CPU의 캐시라인 크기는 64바이트입니다. 자주 접근하는 데이터가 캐시라인 경계를 넘으면 두 개의 캐시라인을 로드해야 하므로 성능이 저하됩니다. 커널은 이를 위한 정렬 매크로를 제공합니다.

/* ── 캐시라인 크기 확인 ── */
/* 커널: include/linux/cache.h */
#define L1_CACHE_SHIFT    6
#define L1_CACHE_BYTES    (1 << L1_CACHE_SHIFT)    /* 64 */
#define SMP_CACHE_BYTES   L1_CACHE_BYTES

/* ── 구조체 캐시라인 정렬 ── */
struct optimized_struct {
    /* 핫 데이터: 첫 번째 캐시라인에 집중 배치 */
    atomic_t       refcount;          /* 4B */
    unsigned int   flags;             /* 4B */
    void           *data;             /* 8B */
    unsigned long  last_access;       /* 8B */
    spinlock_t     lock;              /* 4B */
    /* 총 28B → 첫 캐시라인(64B)에 여유 있음 */

    /* 콜드 데이터: 자주 접근하지 않는 필드 */
    char           name[64];          /* 두 번째 캐시라인 */
    struct list_head list;           /* 세 번째 캐시라인 */
} ____cacheline_aligned;

/* ── False sharing 방지: per-CPU 카운터 ── */
struct per_cpu_stats {
    unsigned long packets;           /* CPU 0이 수정 */
    unsigned long bytes;
} ____cacheline_aligned_in_smp;
/* SMP 환경에서 각 인스턴스가 별도 캐시라인에 배치 */
/* → CPU 0의 수정이 CPU 1의 캐시를 무효화하지 않음 */

DEFINE_PER_CPU(struct per_cpu_stats, cpu_stats);

/* ── 구조체 패딩 시각화 (pahole 도구) ── */
/* pahole -C optimized_struct vmlinux */
/* struct optimized_struct {
 *     atomic_t   refcount;    /*     0     4 */
 *     unsigned   flags;       /*     4     4 */
 *     void *     data;        /*     8     8 */
 *     /* --- cacheline 1 boundary (64 bytes) --- */
 *     ...                                          */

/* ── 읽기 전용과 읽기-쓰기 분리 ── */
struct mixed_access {
    /* 읽기 전용 필드 (여러 CPU가 동시 읽기) */
    const unsigned int type;
    const unsigned int max_size;
    void *const ops;

    /* 쓰기 필드: 별도 캐시라인으로 분리 */
    struct {
        atomic_t counter;
        unsigned long timestamp;
    } ____cacheline_aligned_in_smp write_fields;
};
# 사용자 공간에서 캐시라인 크기 확인
getconf LEVEL1_DCACHE_LINESIZE     # 64
getconf LEVEL1_DCACHE_SIZE         # 32768 (32KB)
getconf LEVEL2_CACHE_SIZE          # 262144 (256KB)
getconf LEVEL3_CACHE_SIZE          # 16777216 (16MB)

# lscpu로 캐시 토폴로지 확인
lscpu --caches=all

# pahole: 구조체 레이아웃 및 패딩 분석
sudo apt install dwarves
pahole -C task_struct vmlinux | head -50
# → 각 필드의 오프셋, 크기, 캐시라인 경계 표시
# → holes (패딩 바이트) 감지

# perf로 캐시 미스 프로파일링
perf stat -e L1-dcache-loads,L1-dcache-load-misses,\
L1-dcache-stores,LLC-loads,LLC-load-misses \
-a -- sleep 10

# perf c2c로 false sharing 탐지
perf c2c record -a -- sleep 10
perf c2c report --stdio
# Shared Data Cache Line Table에서 Rmt HITM이 높은 주소 확인

False Sharing 상세 분석 및 해결

False Sharing: 문제와 해결 문제: 같은 캐시라인의 다른 필드 수정 캐시라인 (64 bytes) counter_a (CPU0) counter_b (CPU1) counter_c (CPU2) padding CPU 0 counter_a++ 반복 MESI: M→I→M→I→... 캐시라인 전체 무효화! CPU 1 counter_b++ 반복 MESI: M→I→M→I→... 캐시라인 전체 무효화! 캐시 바운스 해결: 각 필드를 별도 캐시라인에 배치 캐시라인 1: counter_a (CPU0 전용) + 패딩 캐시라인 2: counter_b (CPU1 전용) + 패딩 CPU 0 counter_a++ 반복 MESI: M (유지) 무효화 없음! CPU 1 counter_b++ 반복 MESI: M (유지) 무효화 없음! 성능 영향 비교 (4개 스레드, 각 1억 회 증가) False sharing 있음: ~2.8초 False sharing 제거: ~0.3초 (9배 빠름) False Sharing 탐지 워크플로 1. 증상 확인 코어 수↑ 성능↓ 2. perf c2c record HITM 이벤트 수집 3. perf c2c report 핫 캐시라인 식별 4. 해결 ____cacheline_aligned / per-CPU

분기 예측 최적화 (Branch Prediction)

현대 CPU는 파이프라인 깊이가 15~20+ 단계이므로, 분기 예측(Branch Prediction) 실패 시 파이프라인 플러시(Flush)로 15~20 사이클이 낭비됩니다. Linux 커널은 분기 예측을 돕는 다양한 매크로와 기법을 제공합니다.

분기 예측 최적화 계층 1단계: 컴파일러 힌트 (소스 코드 수준) likely() / unlikely() __builtin_expect 기반 핫 경로를 fall-through에 배치 static_branch (Jump Label) 런타임 코드 패칭 비활성 시 NOP, 분기 비용 0 __hot / __cold 섹션 배치 .text.hot / .text.unlikely I-캐시 효율 향상 2단계: 컴파일러 최적화 (빌드 수준) PGO: 프로파일 기반 최적화 AutoFDO: perf 기반 최적화 BOLT: 바이너리 재배치 -fbranch-probabilities 3단계: 알고리즘 / 코드 패턴 (설계 수준) 분기 없는 코드: CMOV, 비트 연산 테이블 룩업: switch → 함수 포인터 배열 정렬된 데이터: 예측 가능한 패턴 분기 예측 성능 측정 perf stat -e branches,branch-misses ./program → 분기 예측 실패율 측정 perf annotate --symbol=func → 분기 명령어별 미스 비율 확인 toplev -l2 → Bad Speculation > 10%이면 분기 최적화 효과적
/* ── likely()/unlikely() 매크로 내부 ── */
/* include/linux/compiler.h */
#define likely(x)   __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)

/* 사용 예: 에러 처리 경로를 unlikely로 표시 */
int do_read(struct file *f, char *buf, size_t len)
{
    if (unlikely(!f))
        return -EBADF;        /* .text.unlikely 섹션으로 분리 */

    if (likely(len <= PAGE_SIZE))
        return fast_read(f, buf, len);  /* fall-through 경로 */

    return slow_read(f, buf, len);
}

/* ── static_branch: 런타임 패칭 (0 오버헤드) ── */
/* 피처 플래그가 비활성 상태일 때 NOP으로 컴파일 */
DEFINE_STATIC_KEY_FALSE(my_debug_feature);

void hot_path(void)
{
    /* 비활성 시: NOP (분기 없음, 0 오버헤드) */
    /* 활성화 시: 런타임에 JMP으로 패칭 */
    if (static_branch_unlikely(&my_debug_feature))
        debug_trace();

    do_real_work();
}

/* 활성화 (코드 패칭 발생, IPI로 모든 CPU 동기화) */
static_branch_enable(&my_debug_feature);

/* ── 분기 없는 코드 패턴 ── */
/* 나쁜 예: 분기 예측 실패 빈번 (50/50 조건) */
for (i = 0; i < n; i++)
    if (data[i] >= 128)
        sum += data[i];

/* 좋은 예: 분기 없는 조건부 덧셈 (CMOV 생성) */
for (i = 0; i < n; i++) {
    int mask = -(data[i] >= 128);  /* 0 or -1 (all bits) */
    sum += data[i] & mask;
}

/* ── 함수 포인터 테이블로 switch 대체 ── */
/* switch/case가 많으면 간접 분기 예측 실패 */
typedef int (*handler_fn)(struct request *req);
static const handler_fn handlers[] = {
    [REQ_READ]  = handle_read,
    [REQ_WRITE] = handle_write,
    [REQ_SYNC]  = handle_sync,
};
return handlers[req->type](req);
# 분기 예측 성능 측정
perf stat -e branches,branch-misses,branch-loads,branch-load-misses \
  -- ./program
# branch-misses / branches = 분기 예측 실패율
# 1% 미만: 양호, 5% 이상: 최적화 필요

# 핫 분기의 예측 실패 위치 확인
perf record -e branch-misses:pp -c 1000 -- ./program
perf annotate --symbol=hot_function
# → 특정 조건 분기에 높은 미스 비율 → 패턴 분석

# toplev로 Bad Speculation 비율 확인
sudo python3 toplev.py -l2 -- ./program
# Bad Speculation > 10% → 분기 최적화 효과적
# Branch_Mispredicts > 5% → likely/unlikely 또는 분기 제거
# Machine_Clears > 1% → 메모리 오더링 문제

# PGO(Profile-Guided Optimization)로 자동 분기 최적화
# GCC:
gcc -fprofile-generate -O2 program.c -o program
./program    # 대표 워크로드 실행 → *.gcda 생성
gcc -fprofile-use -O2 program.c -o program_pgo
# → 컴파일러가 실제 실행 데이터로 분기 확률 최적화

메모리 접근 최적화

메모리 접근 패턴은 캐시 효율과 직접 연결됩니다. 순차 접근은 하드웨어 프리페처(Prefetcher)가 자동으로 예측하지만, 랜덤 접근이나 포인터 추적(Pointer Chasing) 패턴은 소프트웨어 프리페치와 데이터 구조 최적화가 필요합니다.

메모리 접근 패턴별 최적화 전략 순차 접근 (Sequential) 배열 순회, 스트리밍 I/O HW Prefetcher가 자동 감지 → 높은 캐시 히트율 최적화: 데이터 구조를 배열 기반으로 설계 readahead 튜닝, 큰 I/O 크기, NT store 랜덤 접근 (Random) 해시 테이블 탐색, B-tree 노드 접근 HW Prefetcher 무효 → 높은 캐시 미스 최적화: SW prefetch, 데이터 크기 축소 핫 데이터를 컴팩트하게 배치, Huge Pages 포인터 추적 (Pointer Chasing) 연결 리스트, 트리, 그래프 순회 다음 주소를 로드해야 다음 접근 가능 → 직렬화 최적화: 배열 기반 풀, prefetch(next) 객체 크기를 캐시라인에 맞춤, slab 할당자 활용 스트라이드 접근 (Strided) 행렬 열 접근, 큰 구조체 배열의 특정 필드 일정 간격 접근 → 캐시라인 활용률 저하 최적화: SoA(Struct of Arrays) 변환 핫 필드 분리, 루프 타일링(Loop Tiling) NUMA-aware 메모리 할당 numactl --membind=N: 로컬 노드 고정 | first-touch 정책: 첫 접근 CPU의 노드에 할당 | 인터리브: 균등 분산 데이터 구조 설계 원칙 AoS → SoA 변환 | 핫/콜드 데이터 분리 | 구조체 크기를 캐시라인 배수로 | per-CPU 데이터로 공유 제거 | prefetch로 메모리 레이턴시 숨기기
/* ── 소프트웨어 프리페치 (Software Prefetch) ── */
/* include/linux/prefetch.h */
prefetch(ptr);          /* T0: L1/L2/L3에 로드 (읽기 의도) */
prefetchw(ptr);         /* T0 + Exclusive: 쓰기 의도 (MESI E 상태) */
prefetch_range(ptr, len); /* 범위 프리페치 (큰 데이터) */

/* 연결 리스트 순회 시 다음 노드 프리페치 */
list_for_each_entry(entry, &head, list) {
    prefetch(entry->list.next);  /* 다음 노드를 미리 캐시에 로드 */
    process(entry);              /* 현재 노드 처리 중 로드 완료 */
}

/* 해시 테이블 탐색 시 프리페치 */
for (i = 0; i < batch_size; i++)
    prefetch(&hash_table[keys[i] & mask]);
for (i = 0; i < batch_size; i++)
    results[i] = lookup(&hash_table[keys[i] & mask]);

/* ── AoS vs SoA 변환 ── */
/* AoS (Array of Structures): 캐시 비효율 */
struct particle_aos {
    float x, y, z;         /* 위치 (12B) */
    float vx, vy, vz;       /* 속도 (12B) */
    float mass;             /* 질량 (4B) */
    char name[32];          /* 이름 (32B) → 캐시 오염 */
} particles[N]; /* 위치만 순회해도 전체 60B 로드 */

/* SoA (Structure of Arrays): 캐시 효율적 */
struct particle_soa {
    float *x, *y, *z;      /* 위치만 접근 시 연속 메모리 */
    float *vx, *vy, *vz;
    float *mass;
    char **name;           /* 필요할 때만 접근 */
};

/* ── Non-Temporal Store (스트리밍 저장) ── */
/* 캐시를 오염시키지 않고 DRAM에 직접 기록 */
/* 대량 데이터 복사/초기화 시 사용 */
static inline void nt_memcpy(void *dst, const void *src, size_t len)
{
    /* movntdq: 캐시 우회 128-bit 저장 */
    /* 커널: copy_page_nt(), clear_page_nt() */
    /* 주의: sfence로 순서 보장 필요 */
}

/* ── NUMA-aware 할당 ── */
/* 현재 CPU의 NUMA 노드에 메모리 할당 */
ptr = kmalloc_node(size, GFP_KERNEL, numa_node_id());

/* 특정 노드에 페이지 할당 */
page = alloc_pages_node(node, GFP_KERNEL, order);

/* per-CPU 할당: 자동으로 로컬 NUMA 노드 사용 */
ptr = this_cpu_ptr(&per_cpu_buffer);

커널 부팅 최적화

커널 부팅 시간은 서버 가동률, 컨테이너 시작 속도, 임베디드 시스템 반응성에 직접 영향을 미칩니다. initcall 순서 분석, 병렬 초기화, 불필요한 드라이버 제거가 핵심입니다.

커널 부팅 단계 및 최적화 포인트 1. 부트로더 (GRUB/U-Boot) 커널 이미지 로드, 커맨드라인 전달 최적화: 타임아웃 제거, 커널 압축 방식 (LZ4 vs GZIP) 2. 초기 부팅 (start_kernel → rest_init) 메모리 초기화, 인터럽트 설정, 스케줄러 시작 최적화: NUMA 초기화 병렬화, 최소 메모리 매핑 3. initcall 실행 (가장 긴 단계) 디바이스 드라이버, 파일시스템, 네트워크 초기화 early (0) core (1) postcore (2) arch (3) subsys (4) fs (5) device (6) late (7) 4. 사용자 공간 전환 (init/systemd) PID 1 실행, 서비스 시작 최적화: systemd-analyze, 서비스 병렬화, 지연 시작 부팅 시간 분석 도구 dmesg (타임스탬프) bootchart / bootgraph.py systemd-analyze blame/plot initcall_debug 커널 파라미터 커널 부트 파라미터: initcall_debug → dmesg로 각 initcall 소요 시간 표시 scripts/bootgraph.py: dmesg 출력을 SVG 타임라인으로 시각화 → 병목 initcall 즉시 식별
# ── 부팅 시간 측정 ── 

# 커널 부팅 시간 확인 (dmesg 기반)
dmesg | grep -E "Booting Linux|Freeing unused kernel"
# → 두 메시지 사이의 시간이 커널 초기화 시간

# systemd 전체 부팅 시간 분석
systemd-analyze
# Startup finished in 1.234s (firmware) + 0.567s (loader)
#                   + 2.345s (kernel) + 8.901s (userspace)
# graphical.target reached after 13.047s in userspace

# 서비스별 시간 분석 (가장 느린 서비스 순)
systemd-analyze blame
# 3.456s NetworkManager-wait-online.service
# 1.234s plymouth-quit-wait.service
# 0.987s dracut-initqueue.service

# 부팅 시퀀스 시각화 (SVG 타임라인)
systemd-analyze plot > boot-timeline.svg

# 크리티컬 패스 분석 (병렬화 불가능한 최장 경로)
systemd-analyze critical-chain

# ── initcall 분석 ──

# 커널 부트 파라미터에 추가
# initcall_debug → 각 initcall의 소요 시간 표시
# GRUB: GRUB_CMDLINE_LINUX에 "initcall_debug" 추가

# dmesg에서 느린 initcall 찾기
dmesg | grep "initcall" | sort -k 5 -t '=' -rn | head -20
# → calling  acpi_init+0x0/0x... @ 1 returned 0 after 234567 usecs

# bootgraph.py로 시각화 (커널 소스 내 도구)
dmesg > dmesg.log
python3 scripts/bootgraph.py dmesg.log > bootgraph.svg

# ── 부팅 최적화 기법 ──

# 1. 불필요한 모듈 비활성화
lsmod | wc -l           # 현재 로드된 모듈 수
# make localmodconfig → 현재 로드된 모듈만 빌드

# 2. 커널 압축 방식 변경 (속도 vs 크기)
# CONFIG_KERNEL_LZ4=y   → 가장 빠른 해제 (부팅 속도 최적)
# CONFIG_KERNEL_GZIP=y  → 기본 (균형)
# CONFIG_KERNEL_XZ=y    → 가장 작은 크기 (느린 해제)

# 3. 병렬 초기화 (비동기 프로브)
# 커널 부트 파라미터: async_probe=* → 모든 디바이스 비동기 프로브
# 또는 특정 드라이버만: async_probe=nvme,ahci

# 4. initramfs 최적화
# dracut --no-hostonly → 필요 최소 모듈만 포함
# 불필요한 모듈 제외: dracut --omit="plymouth nfs iscsi"

# 5. systemd 서비스 최적화
systemctl mask NetworkManager-wait-online.service  # 네트워크 대기 제거
systemctl mask plymouth-quit-wait.service          # 부트 스플래시 대기 제거
# 지연 시작: Type=idle 또는 timer 기반

# 6. EFI 부팅 최적화
# efi=runtime → EFI 런타임 서비스 비활성화 (보안 부팅 불필요 시)

# ── 컨테이너/VM 부팅 최적화 ──
# CONFIG_PREEMPT_NONE=y          → 서버 처리량 최적
# CONFIG_HZ_100=y                → 틱 오버헤드 최소
# CONFIG_MODULES=n               → 내장 빌드 (모듈 로드 시간 제거)
# CONFIG_PRINTK=n                → 콘솔 출력 제거 (극한 최적화)
# console=none quiet loglevel=0  → 부트 로그 억제

I/O 성능 최적화 상세

스토리지 I/O 최적화는 I/O 스케줄러 선택, readahead 정책, 블록 레이어 파라미터, 그리고 비동기 I/O 인터페이스 선택을 포함합니다. 특히 io_uring은 기존 aio 대비 획기적인 성능 향상을 제공합니다.

I/O 경로 및 최적화 포인트 Application (read/write/io_uring) VFS (Virtual File System) O_DIRECT: 페이지 캐시 우회 Page Cache (readahead) read_ahead_kb: 128→2048 Filesystem (ext4/xfs/btrfs) noatime, commit=60 Block Layer (I/O Scheduler) none/mq-deadline/bfq/kyber Device Driver (NVMe/SCSI/virtio) nr_requests, max_sectors_kb io_uring SQPOLL+IOPOLL 비동기 I/O 인터페이스 비교 POSIX aio: 스레드 풀 기반, 시스콜 오버헤드 높음 | libaio: 커널 AIO, O_DIRECT 필수, 제한적 io_uring: 공유 링 버퍼, SQPOLL(시스콜 0), IOPOLL(인터럽트 0), 버퍼드 I/O 지원, 네트워크 I/O 통합
# ── blktrace: 블록 I/O 추적 ──
# 블록 레이어의 I/O 요청 흐름을 상세 추적
blktrace -d /dev/nvme0n1 -o - | blkparse -i - | head -50
# Q: 요청 큐 진입, G: 요청 생성, I: 삽입, D: 디스패치, C: 완료
# → Q→D 지연이 크면 I/O 스케줄러 병목
# → D→C 지연이 크면 디바이스 병목

# blktrace → iowatcher 시각화
blktrace -d /dev/sda -w 30
iowatcher -t sda.blktrace.* -o io_analysis.svg

# ── iowait 분석 ──
# CPU가 I/O 대기 중인 시간 비율
iostat -xz 1
# %iowait > 10%: I/O 병목 의심
# await > 10ms (SSD): 디바이스 과부하 또는 큐 포화

# biolatency: I/O 지연 히스토그램 (BPF)
biolatency-bpfcc -mD 10
# → 디바이스별 I/O 완료 시간 분포
# → p99 지연이 p50 대비 10배 이상이면 큐잉 문제

# ── readahead 튜닝 ──
# 순차 읽기 워크로드: readahead 증가
echo 4096 > /sys/block/sda/queue/read_ahead_kb  # 4MB
# 랜덤 읽기 워크로드: readahead 축소
echo 32 > /sys/block/sda/queue/read_ahead_kb    # 32KB

# posix_fadvise로 개별 파일 readahead 제어
# POSIX_FADV_SEQUENTIAL: 순차 접근 힌트 (readahead 2배)
# POSIX_FADV_RANDOM: 랜덤 접근 힌트 (readahead 비활성화)
# POSIX_FADV_DONTNEED: 페이지 캐시에서 제거

# ── io_uring vs aio 성능 비교 ──
# fio 벤치마크 (NVMe, 4K 랜덤 읽기)
fio --name=aio --ioengine=libaio --rw=randread --bs=4k \
    --iodepth=128 --numjobs=4 --size=1G --direct=1
# → libaio: ~800K IOPS

fio --name=uring --ioengine=io_uring --rw=randread --bs=4k \
    --iodepth=128 --numjobs=4 --size=1G --direct=1 \
    --sqthread_poll=1 --hipri=1
# → io_uring SQPOLL+IOPOLL: ~1.2M IOPS (50% 향상)

# ── NVMe 최적화 ──
# NVMe는 자체 하드웨어 큐가 충분하므로 I/O 스케줄러 불필요
echo none > /sys/block/nvme0n1/queue/scheduler
# 높은 큐 깊이 설정 (NVMe는 64K 큐 지원)
echo 1024 > /sys/block/nvme0n1/queue/nr_requests
# I/O 병합 비활성화 (NVMe는 내부에서 처리)
echo 2 > /sys/block/nvme0n1/queue/nomerges
# IRQ 친화도: NVMe 큐를 CPU에 1:1 매핑
# NVMe 드라이버가 자동으로 MSI-X 인터럽트를 CPU에 분산

네트워크 성능 최적화 상세

네트워크 성능 최적화는 NIC 하드웨어 수준의 인터럽트 분산(RSS/XPS)부터 TCP 스택 튜닝, 그리고 커널 우회(XDP/AF_XDP)까지 다층적으로 접근해야 합니다.

RSS / RPS / RFS / XPS 패킷 분산 아키텍처 NIC Hardware RSS 하드웨어 해시 → 큐 MSI-X 인터럽트 큐별 인터럽트 → CPU GRO (Hardware) 패킷 집계 (오프로드) Coalescing 인터럽트 병합 Ring Buffer DMA 링 (ethtool -G) 커널 수신 경로 (softirq) NAPI Poll netdev_budget RPS SW 해시 → CPU RFS 소비 스레드 CPU GRO (SW) 소프트웨어 집계 XDP 드라이버 수준 처리 GSO 세그멘테이션 커널 송신 경로 XPS (송신 큐→CPU) TSO/GSO (세그멘트) Qdisc (fq/fq_codel) checksum offload 네트워크 성능 최적화 체크리스트 1. NIC 드롭 확인: ethtool -S eth0 | grep drop → 링 버퍼 크기 조정 (ethtool -G) 2. 인터럽트 분산: RSS 큐 수 = CPU 수 (ethtool -L), 또는 RPS/RFS 활성화 3. 인터럽트 코얼레싱: ethtool -C eth0 adaptive-rx on → 지연↓처리량↑ 균형 4. TCP 튜닝: BBR + fq qdisc + 버퍼 크기 조정 | 고대역폭: tcp_rmem/wmem 증가
# ── GRO/GSO/TSO 오프로드 확인 및 설정 ──
ethtool -k eth0
# generic-receive-offload: on   → GRO (수신 패킷 집계)
# generic-segmentation-offload: on → GSO (송신 세그멘테이션)
# tcp-segmentation-offload: on  → TSO (TCP 세그멘트 오프로드)

# 오프로드 활성화/비활성화
ethtool -K eth0 gro on gso on tso on

# ── 인터럽트 코얼레싱 상세 ──
ethtool -c eth0
# rx-usecs: 50      → 50us마다 인터럽트 (높이면 처리량↑ 지연↑)
# tx-usecs: 50
# rx-frames: 128    → 128프레임마다 인터럽트

# 적응형 코얼레싱 (트래픽에 따라 자동 조절)
ethtool -C eth0 adaptive-rx on adaptive-tx on

# 저지연 워크로드 (금융/게임): 코얼레싱 최소화
ethtool -C eth0 rx-usecs 0 tx-usecs 0

# ── RSS/RPS/RFS/XPS 설정 ──

# RSS: NIC 하드웨어 큐 수 설정
ethtool -l eth0              # 현재/최대 큐 수 확인
ethtool -L eth0 combined 8   # 8개 큐 (CPU 수에 맞춤)

# RSS 해시 함수 설정
ethtool -N eth0 rx-flow-hash tcp4 sdfn
# s: src IP, d: dst IP, f: src port, n: dst port

# RPS: 소프트웨어 패킷 분산 (RSS 미지원 시)
echo ff > /sys/class/net/eth0/queues/rx-0/rps_cpus

# RFS: 패킷을 소비 스레드의 CPU로 전달
echo 32768 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
echo 32768 > /proc/sys/net/core/rps_sock_flow_entries

# XPS: 송신 큐를 CPU에 매핑
echo 1 > /sys/class/net/eth0/queues/tx-0/xps_cpus  # CPU 0
echo 2 > /sys/class/net/eth0/queues/tx-1/xps_cpus  # CPU 1

# ── 네트워크 진단 ──

# softnet_stat: CPU별 패킷 처리 통계
cat /proc/net/softnet_stat
# 열 1: processed, 열 2: dropped, 열 3: time_squeeze
# dropped > 0: netdev_max_backlog 부족
# time_squeeze > 0: NAPI 예산 부족 (netdev_budget 증가)

# TCP 재전송 추적
ss -ti | grep -E "retrans|rto"
tcpretrans-bpfcc  # BPF 기반 실시간 추적

# conntrack 테이블 상태
conntrack -C  # 현재 연결 수
sysctl net.netfilter.nf_conntrack_max  # 최대 연결 수
# conntrack 테이블 포화 → 새 연결 거부

Lock Contention 상세 분석

다중 CPU 환경에서 잠금 경쟁(Lock Contention)은 확장성의 가장 흔한 병목입니다. 코어 수를 늘려도 성능이 정체되거나 오히려 저하되는 경우, 잠금 경쟁을 먼저 의심해야 합니다.

Lock Contention 분석 및 최적화 워크플로 증상 감지 코어 4→32 성능 정체 perf top: spin_lock 높음 잠금 식별 perf lock report lockstat → 경쟁 Top 10 원인 분석 perf lock contention -b 경쟁 콜스택 확인 최적화 적용 세분화/RCU/per-CPU lockless 자료구조 잠금 최적화 전략 매트릭스 잠금 세분화 (Sharding) 글로벌 잠금 → per-bucket 잠금 해시 테이블: 버킷별 spinlock 리스트: 세그먼트별 잠금 효과: N배 병렬성 (N=버킷수) 잠금 유형 변경 spinlock → mutex (긴 임계영역) mutex → rwsem (읽기 위주) rwsem → RCU (읽기 극다수) 효과: 읽기 경로 무잠금화 잠금 제거 (Lockless) per-CPU 변수: 공유 제거 atomic 연산: cmpxchg, atomic_add lockless 큐: LKMPG CAS 기반 효과: 완전 무잠금 (최대 확장성) 선택 가이드: 임계영역 < 1us, IRQ 컨텍스트 → spinlock | 임계영역 > 1us, 슬립 가능 → mutex 읽기:쓰기 > 10:1 → rwsem/RCU | 카운터/통계 → per-CPU + 주기적 합산 경쟁 > 1000/sec → 세분화 필요 | 코어 수 비례 성능↓ → false sharing 확인 (perf c2c) Amdahl의 법칙: 직렬 구간 10%면 최대 10배 확장. 직렬 구간 = 잠금 보유 시간 / 전체 시간
# ── 잠금 경쟁 종합 분석 워크플로 ──

# 1단계: perf top으로 잠금 관련 함수 확인
perf top -g
# → _raw_spin_lock, mutex_lock, rwsem_down_read 비율 확인
# → 10% 이상이면 잠금 경쟁 심각

# 2단계: perf lock으로 상세 분석
perf lock record -a -- sleep 30
perf lock report --sort=contended
#                     Name  acquired  contended  avg wait  total wait
#     &mm->mmap_lock     89012        890       12.3us    10947us
# → contended가 높은 잠금이 병목

# 3단계: BPF 기반 실시간 잠금 경쟁 분석 (6.2+)
perf lock contention -b -s 10
# → 가장 경쟁이 심한 10개 잠금의 콜스택 즉시 표시

# 4단계: 잠금 보유 시간 분석
bpftrace -e 'tracepoint:lock:contention_begin {
    @start[tid] = nsecs;
}
tracepoint:lock:contention_end /@start[tid]/ {
    @hold_us = hist((nsecs - @start[tid]) / 1000);
    delete(@start[tid]);
}'

# 5단계: lockdep으로 잠금 의존성 분석
# CONFIG_PROVE_LOCKING=y (개발/테스트 환경)
cat /proc/lockdep_stats
# → 잠금 클래스 수, 의존성 체인 깊이 확인

컴파일러 최적화 상세

커널 빌드의 컴파일러 최적화는 GCC/Clang의 최적화 레벨, LTO(Link Time Optimization), PGO(Profile-Guided Optimization), AutoFDO를 포함합니다. 각 기법의 적용 조건과 성능 영향을 정확히 이해해야 합니다.

LTO / PGO / AutoFDO 최적화 워크플로 비교 LTO (Link-Time Optimization) a.o (IR 코드) b.o (IR 코드) 전역 최적화 (링커) vmlinux (최적화됨) PGO (Profile-Guided Optimization) 1. 계측 빌드 2. 워크로드 실행 3. profraw → profdata (llvm-profdata) 4. 프로파일 기반 재빌드 vmlinux (PGO 최적화) AutoFDO 1. 표준 빌드 + 실행 2. perf record -b -e cycles:pp 3. create_gcov 변환 vmlinux (AutoFDO 최적화) 최적화 기법 비교 기법 컴파일러 빌드 복잡도 성능 향상 주의사항 LTO (Full) Clang 필수 낮음 (1회 빌드) 코드 1~3%, 크기 5~10%↓ 빌드 시간 + 메모리 증가 ThinLTO Clang 필수 낮음 (병렬 빌드) Full LTO의 ~90% 빌드 시간 Full LTO의 1/3 PGO Clang (커널) 높음 (2회 빌드) 1~5% 처리량 향상 대표 워크로드 선정 중요 AutoFDO GCC 중간 (프로파일 수집) PGO와 유사 프로파일 수집이 쉬움
# ── GCC vs Clang 최적화 레벨 비교 ──
# -O0: 최적화 없음 (디버깅용)
# -O1: 기본 최적화 (인라인, 상수 전파)
# -O2: 표준 최적화 (커널 기본값, 대부분의 최적화)
# -O3: 공격적 최적화 (자동 벡터화, 루프 언롤링)
#      → 커널에서는 주의 (코드 크기 증가 → I-캐시 압박)
# -Os: 크기 최적화 (I-캐시 친화적, 임베디드)

# 커널 최적화 레벨 설정
# CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y  → -O2
# CONFIG_CC_OPTIMIZE_FOR_SIZE=y         → -Os

# ── BOLT: 바이너리 수준 최적화 (Post-Link) ──
# BOLT는 링크 후 바이너리를 프로파일 기반으로 재배치
# 핫 코드를 연속 메모리에 배치 → I-캐시/TLB 효율 향상
perf record -e cycles:u -j any,u -- ./program
perf2bolt -p perf.data -o perf.fdata ./program
llvm-bolt ./program -o program.bolt \
  -data=perf.fdata -reorder-blocks=ext-tsp \
  -reorder-functions=hfsort -split-functions
# → 5~15% 성능 향상 (I-캐시 개선)

# ── 커널에서 -march=native 사용 ──
# CONFIG_MNATIVE=y (커널 6.1+, 해당 CPU에 최적화된 명령어 사용)
# 주의: 다른 CPU에서 부팅 불가 → 배포 커널에서는 사용 금지
⚠️

최적화와 보안의 균형: LTO + CFI(Control-Flow Integrity)를 함께 사용하면 보안과 성능을 동시에 개선할 수 있습니다. 단, Retpoline(Spectre v2 완화)과 KPTI(Meltdown 완화)는 1~5%의 성능 비용이 발생합니다. perf stat -d로 완화 기법의 실제 비용을 측정하고, 보안 요구사항과 성능 목표 사이에서 균형을 결정하세요.

구조체 레이아웃 최적화

커널 자료구조의 메모리 레이아웃(Layout)은 캐시 효율에 직접적인 영향을 미칩니다. 필드 배치 순서, 패딩 최소화, 핫/콜드 데이터 분리는 커널 개발에서 필수적인 최적화 기법입니다.

구조체 레이아웃 최적화 원칙 비효율적 레이아웃 (패딩 낭비) 바이트 0 char pad 7B long 8B int pad long 8B 바이트 32 short pad 6B 총 크기: 40 바이트 패딩: 13 바이트 (32.5% 낭비) 캐시라인: 1개 사용 (일부 낭비) struct bad { char a; long b; int c; long d; short e; }; 최적화된 레이아웃 (패딩 최소화) 바이트 0 long 8B long 8B int 4B short char 1B 총 크기: 24 바이트 패딩: 1 바이트 (4.2% 낭비) 캐시라인: 1개 (효율적 사용) struct good { long b; long d; int c; short e; char a; }; 핫/콜드 데이터 분리 패턴 캐시라인 0 (핫 데이터) — 64 bytes refcount(4) | flags(4) | *data(8) | *ops(8) | lock(4) | state(4) → 읽기/쓰기 패스에서 매번 접근하는 필드 캐시라인 1 (읽기 전용) — 64 bytes type(4) | max_size(4) | *name(8) | config[32] → 초기화 후 변경 없음, 여러 CPU 동시 읽기 캐시라인 2 (콜드 데이터) — 64 bytes debug_name[32] | create_time(8) | stats(16) → 디버깅/관리 시에만 접근, 성능 경로 밖 ____cacheline_aligned (쓰기 전용) per-CPU 통계: hit_count, miss_count, latency_sum → false sharing 방지를 위해 별도 캐시라인 정렬
/* ── pahole을 활용한 구조체 분석 ── */
/* pahole: DWARF 디버그 정보에서 구조체 레이아웃 추출 */
/* 설치: sudo apt install dwarves */

/* 비효율적 구조체 (패딩 낭비 예시) */
struct bad_layout {
    char           a;      /* 0      1 */
                             /* 1      7 (패딩) */
    unsigned long  b;      /* 8      8 */
    int            c;      /* 16     4 */
                             /* 20     4 (패딩) */
    unsigned long  d;      /* 24     8 */
    short          e;      /* 32     2 */
                             /* 34     6 (패딩) */
};
/* sizeof: 40, 패딩 17바이트 (42.5% 낭비!) */

/* 최적화된 구조체 (크기순 내림차순 배치) */
struct good_layout {
    unsigned long  b;      /* 0      8 */
    unsigned long  d;      /* 8      8 */
    int            c;      /* 16     4 */
    short          e;      /* 20     2 */
    char           a;      /* 22     1 */
                             /* 23     1 (패딩) */
};
/* sizeof: 24, 패딩 1바이트 (4.2%) — 16바이트 절약! */

/* ── 커널 실전 패턴: 핫/콜드 분리 ── */
struct net_device {
    /* 첫 번째 캐시라인: 핫 경로 (패킷 수신/송신) */
    char                   name[IFNAMSIZ];
    struct netdev_rx_queue  *_rx;
    unsigned int           num_rx_queues;
    unsigned int           real_num_rx_queues;

    /* 읽기 전용 필드 (초기화 후 변경 없음) */
    const struct net_device_ops *netdev_ops;
    const struct ethtool_ops  *ethtool_ops;

    /* 콜드 필드: 설정 변경 시에만 접근 */
    unsigned int           mtu;
    unsigned short         type;
    /* ... */

    /* 쓰기 필드: per-CPU 통계 */
    struct pcpu_sw_netstats __percpu *tstats;
} ____cacheline_aligned_in_smp;
# pahole로 구조체 분석
pahole -C task_struct vmlinux
# → 각 필드의 오프셋, 크기, 패딩 홀 표시
# /* XXX 4 bytes hole, try to pack */
# → "try to pack" 힌트로 최적화 기회 식별

# 패딩이 많은 구조체 찾기
pahole --holes 1 vmlinux | head -50
# → 1바이트 이상 패딩이 있는 모든 구조체

# 특정 구조체의 캐시라인 경계 표시
pahole --show_reorg_steps -C sk_buff vmlinux

# 구조체 크기가 캐시라인보다 큰 경우 분석
pahole --sizes vmlinux | awk '$2 > 64 {print}' | sort -k 2 -rn | head -20
# → 캐시라인(64B)보다 큰 구조체들

고급 perf 기법

기본적인 perf stat/perf record를 넘어, 고급 perf 기법을 활용하면 더 정밀한 성능 분석이 가능합니다.

perf mem: 메모리 접근 분석

perf mem은 PEBS/IBS 기반으로 메모리 로드/스토어의 레이턴시와 데이터 소스(L1/L2/L3/DRAM/원격 NUMA)를 분석합니다.

# 메모리 접근 프로파일링
perf mem record -- ./program
perf mem report --sort=mem --stdio
# 출력:
# Overhead  Samples  Memory access      Symbol
# 35.20%    1234     L3 or L3 hit       hot_function
# 28.50%    987      Local RAM hit       hash_lookup
# 12.30%    456      Remote RAM hit      remote_access
# → Remote RAM hit가 높으면 NUMA 배치 문제

# 메모리 접근 데이터 소스별 분류
perf mem report --sort=mem,sym --stdio
# → 함수별로 어떤 메모리 계층에서 데이터를 가져오는지 확인

# PEBS를 활용한 정밀 메모리 레이턴시 분석
perf record -e cpu/mem-loads,ldlat=30/P -c 1000 -- ./program
# ldlat=30: 30 사이클 이상 소요된 메모리 로드만 샘플링
perf report --sort=sym,dso,mem
# → 느린 메모리 접근이 집중된 함수/데이터 식별

perf sched: 스케줄링 분석

# 스케줄링 이벤트 기록
perf sched record -- sleep 10

# 태스크별 지연 통계
perf sched latency --sort max
#                  Task    Runtime(ms)  Switches  Avg delay(ms)  Max delay(ms)
#     myapp:12345  45.234       234         0.123         15.678
# → Max delay가 높은 태스크가 지연 스파이크 원인

# 시간순 이벤트 타임라인
perf sched timehist
#   time      cpu  task            wait time  sch delay  run time
# 1234.567    [0]  myapp:12345     0.045ms    0.012ms    1.234ms
# → sch delay(스케줄링 지연)가 높으면 CPU 부족

# CPU별 태스크 매핑 시각화
perf sched map
# → ASCII 타임라인으로 CPU 간 태스크 이동 패턴
# → 잦은 마이그레이션은 캐시 효율 저하

# 스케줄러 웨이크업 지연 분석
perf sched timehist --wakeup-events
# → 태스크가 깨어난 후 실제 실행까지의 지연

perf probe: 동적 트레이싱

perf probe는 커널 함수에 동적으로 트레이스 포인트를 삽입하여, 함수 인자(Argument), 반환값, 지역 변수를 캡처합니다. kprobe/kretprobe를 간편하게 사용할 수 있는 인터페이스입니다.

# 커널 함수에 동적 프로브 삽입
perf probe --add 'do_sys_openat2 filename:string'
# → do_sys_openat2 함수 진입 시 filename 인자 캡처

# 프로브 이벤트 기록
perf record -e probe:do_sys_openat2 -aR -- sleep 5
perf script
# → 어떤 프로세스가 어떤 파일을 열었는지 추적

# 함수 반환값 캡처
perf probe --add 'do_sys_openat2%return $retval'

# 구조체 멤버 접근
perf probe --add 'tcp_sendmsg sk->sk_state size'

# 프로브 제거
perf probe --del '*'

# 사용 가능한 프로브 포인트 확인
perf probe --vars do_sys_openat2
perf probe --line do_sys_openat2

# ── 실전 활용: 성능 디버깅 ──

# 특정 파일 시스템 함수의 지연 시간 측정
perf probe --add 'ext4_file_read_iter'
perf probe --add 'ext4_file_read_iter%return'
perf record -e probe:ext4_file_read_iter,\
probe:ext4_file_read_iter__return -aR -- sleep 10
perf script | head -30
# → 함수 진입/종료 타임스탬프로 지연 계산

# 사용자 공간 프로브 (uprobe)
perf probe -x /usr/lib/x86_64-linux-gnu/libc.so.6 --add 'malloc size=%di'
# → malloc 호출 시 요청 크기(size) 캡처
perf record -e probe_libc:malloc -p $(pidof myapp) -- sleep 5
perf script
# → 프로세스별 메모리 할당 패턴 분석

# USDT (User Statically Defined Tracing) 프로브
# 애플리케이션에 미리 정의된 트레이스 포인트
perf probe --list-dtrace-events=/usr/lib/x86_64-linux-gnu/libc.so.6
# → 라이브러리에서 사용 가능한 USDT 프로브 목록

perf script: 후처리 및 커스텀 분석

perf scriptperf.data의 원시 이벤트를 텍스트로 출력하여 커스텀 후처리를 가능하게 합니다. Flame Graph 생성, 시간별 분석, 커스텀 통계 추출에 활용합니다.

# 기본 사용법: 이벤트 텍스트 출력
perf record -g -F 99 -a -- sleep 10
perf script > perf_output.txt

# 출력 필드 커스터마이징
perf script -F pid,tid,cpu,time,event,ip,sym,dso
# → 필요한 필드만 선택하여 후처리 편의성 향상

# Flame Graph 생성 파이프라인
perf script | stackcollapse-perf.pl | flamegraph.pl > flame.svg

# 시간 범위 필터링
perf script --time "1234.567,1235.890"
# → 특정 시간 구간의 이벤트만 추출

# CPU 필터링
perf script --cpu 0-3
# → 특정 CPU의 이벤트만 추출

# Python 스크립트로 커스텀 분석
perf script -s analyze_latency.py
# → perf script의 Python 바인딩으로 복잡한 분석 자동화

# 특정 심볼만 필터링
perf script | grep -E "copy_page|clear_page"
# → 특정 함수의 호출 패턴과 빈도 분석

전력 vs 성능 트레이드오프

현대 CPU는 전력 관리(Power Management)와 성능이 밀접하게 연결되어 있습니다. C-State(유휴 상태), P-State(주파수/전압 스케일링), 터보 부스트(Turbo Boost) 설정이 성능에 직접적인 영향을 미칩니다.

CPU 전력 관리 계층 및 성능 영향 C-States (유휴 상태) C0 (활성) C1 (Halt) C3 (Sleep) C6 (Deep) 깊은 C-State → 전력↓↓ but 깨어나는 지연↑↑ C1: ~1us 지연 | C3: ~50us | C6: ~200us 저지연 워크로드: processor.max_cstate=1 에너지 절약: 기본값 (깊은 C-State 허용) P-States / 주파수 스케일링 cpufreq governor: performance schedutil powersave performance: 항상 최대 주파수 (벤치마크) schedutil: 부하에 따라 동적 조절 (기본값) Turbo Boost: 전력/열 여유 시 최대 주파수 초과 → 벤치마크 시 no_turbo=1로 비활성화 (일관성) 전력/주파수 모니터링 도구 turbostat --interval 1: CPU별 주파수, C-State 상주율, 전력, 온도 실시간 모니터링 perf stat -e power/energy-pkg/,power/energy-cores/: RAPL 에너지 소비 측정 | cpupower monitor: 주파수/거버너 상태 워크로드별 전력/성능 튜닝 가이드 서버 (최대 처리량): performance governor + no_turbo=0 + max_cstate=1~2 저지연 RT: performance + no_turbo=1 + max_cstate=0 + isolcpus + nohz_full
# ── 전력/주파수 상태 확인 ──
turbostat --interval 1 --show Core,CPU,Avg_MHz,Busy%,Bzy_MHz,PkgWatt,CoreTmp
# Core CPU  Avg_MHz  Busy%  Bzy_MHz  PkgWatt  CoreTmp
# 0    0    2800     95.2   3200     85.2     72
# → Bzy_MHz < Max Turbo: 전력/열 제한으로 스로틀링

# CPU 주파수 거버너 설정
cpupower frequency-info            # 현재 설정 확인
cpupower frequency-set -g performance  # 최대 성능

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

# C-State 제한
# 커널 부트: processor.max_cstate=1 intel_idle.max_cstate=0
# 런타임: /dev/cpu_dma_latency에 0 쓰기 → C0만 허용
exec 3> /dev/cpu_dma_latency
echo -ne '\x00\x00\x00\x00' >&3  # 0us latency 요구

# RAPL 에너지 측정 (perf)
perf stat -e power/energy-pkg/,power/energy-cores/,power/energy-ram/ \
  -a -- sleep 10
# energy-pkg:      123.45 Joules  → 패키지 전체 (12.3W 평균)
# energy-cores:     89.01 Joules  → CPU 코어만
# energy-ram:       34.56 Joules  → DRAM

# 에너지 효율 지표: Performance per Watt
# = 처리량(ops/sec) / 전력(Watts)
# → schedutil은 performance 대비 처리량 약간↓ 전력 크게↓

NUMA 최적화 상세

NUMA(Non-Uniform Memory Access) 환경에서는 메모리 접근 레이턴시(Latency)가 CPU와 메모리의 물리적 위치에 따라 크게 달라집니다. 로컬 노드 접근은 ~80ns이지만 원격 노드 접근은 ~150ns로 거의 2배 느립니다. 대규모 서버에서 NUMA 최적화는 성능에 결정적인 영향을 미칩니다.

2-Socket NUMA 토폴로지 및 메모리 접근 경로 NUMA Node 0 (Socket 0) CPU 0-3 CPU 4-7 CPU 8-11 CPU 12-15 L3 Cache (공유, 16~48MB) iMC (메모리 컨트롤러) Ch0 Ch1 Ch2 Ch3 NUMA Node 1 (Socket 1) CPU 16-19 CPU 20-23 CPU 24-27 CPU 28-31 L3 Cache (공유, 16~48MB) iMC (메모리 컨트롤러) Ch0 Ch1 Ch2 Ch3 UPI/QPI 메모리 접근 레이턴시 비교 로컬 DRAM: ~80ns (distance: 10) 원격 DRAM (UPI): ~150ns (distance: 21) → 1.9배 느림 NUMA 최적화 전략 1. numactl --membind=N: 특정 노드에 메모리 고정 | 2. first-touch 정책: 데이터 초기화를 사용할 CPU에서 수행 3. 인터리브 할당: 대역폭 위주 워크로드 | 4. 자동 NUMA 밸런싱: 커널이 접근 패턴 분석 후 페이지 자동 마이그레이션
# ── NUMA 토폴로지 확인 ──
numactl --hardware
# available: 2 nodes (0-1)
# node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
# node 1 cpus: 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
# node distances:
# node   0   1
#   0:  10  21
#   1:  21  10
# → distance 21/10 = 2.1배 원격 접근 느림

# NUMA 메모리 사용량 확인
numastat
# numa_hit: 로컬 노드 할당 (좋음)
# numa_miss: 원격 노드 할당 (나쁨)
# → numa_miss / (numa_hit + numa_miss) > 10% → 최적화 필요

# 프로세스별 NUMA 메모리 분포
numastat -p $(pidof myapp)

# ── NUMA-aware 실행 ──

# 노드 0에 CPU와 메모리 모두 고정
numactl --cpunodebind=0 --membind=0 ./app

# 메모리 인터리브 (대역폭 최대화)
numactl --interleave=all ./app

# 메모리 선호 노드 (fallback 허용)
numactl --preferred=0 ./app

# ── 자동 NUMA 밸런싱 ──
# 커널이 접근 패턴 분석 후 페이지 자동 마이그레이션
cat /proc/sys/kernel/numa_balancing
# 1: 활성화 (기본) — 대부분의 워크로드에 적합
# 0: 비활성화 — 수동 바인딩 사용 시

# NUMA 밸런싱 통계
grep -E "numa_" /proc/vmstat
# numa_pte_updates: PTE 스캔 횟수
# numa_hint_faults: NUMA 힌트 폴트 발생
# numa_pages_migrated: 마이그레이션된 페이지 수

# ── NUMA 메모리 대역폭 측정 ──
# perf bench NUMA 메모리 접근 벤치마크
perf bench numa mem -p 2 -t 8 -P 1024 -T 0
# → 로컬 vs 원격 메모리 접근 대역폭 비교

# pcm-numa: 실시간 NUMA 트래픽 모니터링
sudo pcm-numa
# → 소켓별 로컬/원격 메모리 접근 비율

# perf로 NUMA 미스 이벤트 측정
perf stat -e node-loads,node-load-misses,\
node-stores,node-store-misses -a -- sleep 10
# node-load-misses / node-loads = 원격 접근 비율
# > 10%이면 NUMA 배치 최적화 필요

# ── NUMA-aware 커널 자료구조 ──
# kmalloc_node(): 특정 노드에 커널 메모리 할당
# alloc_pages_node(): 특정 노드에 페이지 할당
# per_cpu: 자동으로 로컬 NUMA 노드 사용
# 네트워크: NIC 인터럽트 → 로컬 NUMA 노드의 CPU로 전달
💡

NUMA 최적화 우선순위: (1) numastat으로 원격 접근 비율 확인. (2) 10% 이상이면 numactl --membind로 로컬 바인딩 시도. (3) 메모리 대역폭이 중요하면 --interleave=all 고려. (4) DB 워크로드는 NUMA 자동 밸런싱보다 수동 바인딩이 효과적. (5) pcm-memory로 소켓별 메모리 대역폭 포화 여부 확인.

보안 완화 기법의 성능 영향

Spectre, Meltdown 등 CPU 취약점 완화(Mitigation) 기법은 보안을 강화하지만 성능 비용이 발생합니다. 워크로드 특성에 따라 성능 영향이 크게 달라지므로, 정확한 측정이 필요합니다.

완화 기법취약점커널 CONFIG성능 영향비활성화 방법 (주의)
KPTI (PTI)MeltdownPAGE_TABLE_ISOLATION시스콜 집약: 5~30%, 일반: 1~5%pti=off (Intel만 영향)
RetpolineSpectre v2RETPOLINE간접 호출 많은 코드: 5~15%spectre_v2=off
IBRS/IBPBSpectre v2하드웨어 마이크로코드1~5%spectre_v2=off
SSBD (STIBP)Spectre v4마이크로코드SMT 환경: 1~3%spec_store_bypass_disable=off
MDS 완화MDS/TAA마이크로코드 + 커널HT 비활성화 시 최대 50%mds=off
L1TF 완화L1TFX86_BUG_L1TF가상화 환경: 1~10%l1tf=off
# 현재 적용된 완화 기법 확인
grep -r . /sys/devices/system/cpu/vulnerabilities/ 2>/dev/null
# spectre_v1: Mitigation: usercopy/swapgs barriers
# spectre_v2: Mitigation: IBRS
# meltdown:   Mitigation: PTI
# mds:        Mitigation: Clear CPU buffers

# 완화 기법의 성능 비용 측정
# 1. 현재 (완화 활성화) 벤치마크
perf stat -r 5 -e cycles,instructions,cache-misses -- ./benchmark
# → 기준선 기록

# 2. 완화 비활성화 후 벤치마크 (테스트 환경에서만!)
# 커널 부트 파라미터: mitigations=off
# → 모든 CPU 완화 기법 비활성화 (보안 위험!)

# 3. 차이 비교
# → 시스콜 집약 워크로드에서 KPTI 비용이 가장 큼
# → perf trace -s로 시스콜 빈도 확인 → 빈도가 높으면 영향 큼

# 개별 완화 기법 비용 측정 (정밀)
# A: 모든 완화 활성 (기본)
# B: pti=off만 비활성 → KPTI 비용 측정
# C: spectre_v2=off만 비활성 → Retpoline 비용 측정

# AMD CPU에서는 KPTI 불필요 (Meltdown 영향 없음)
# → AMD에서 KPTI가 활성화되어 있으면 불필요한 성능 손실
dmesg | grep -i "isolation"
# Kernel/User page tables isolation: disabled on AMD
⚠️

보안 경고: mitigations=off절대 프로덕션 환경에서 사용하지 마세요. 성능 비용 측정 목적으로만 격리된 테스트 환경에서 사용해야 합니다. 각 완화 기법의 비용을 개별적으로 측정하여 정보에 기반한 결정을 내리세요. AMD CPU에서는 Meltdown 완화(KPTI)가 불필요하므로 자동으로 비활성화됩니다.

성능 최적화 종합 체크리스트

성능 최적화 작업을 체계적으로 수행하기 위한 체크리스트입니다. 각 단계를 순서대로 점검하면 주요 병목을 놓치지 않을 수 있습니다.

성능 최적화 종합 체크리스트 CPU 최적화 1. IPC 확인 (perf stat -ddd) → < 1.0이면 메모리 바운드 2. 핫 함수 식별 (Flame Graph) → annotate로 명령어 분석 3. toplev TMA → Frontend/Backend/BadSpec 분류 메모리 최적화 1. 캐시 미스율 (L1/LLC) → 구조체 정렬, SoA 변환 2. NUMA 배치 (numastat) → membind/인터리브 3. TLB 미스 → Huge Pages / THP 설정 잠금/동기화 최적화 1. 잠금 경쟁 (perf lock/lockstat) → 핫 잠금 식별 2. false sharing (perf c2c) → cacheline_aligned 3. 잠금 세분화/RCU/per-CPU 적용 I/O 최적화 1. I/O 스케줄러 선택 (NVMe: none, HDD: mq-deadline) 2. readahead/dirty 파라미터 튜닝 3. io_uring 적용 (SQPOLL+IOPOLL) 네트워크 최적화 1. NIC 드롭/에러 (ethtool -S) → 링 버퍼/코얼레싱 2. 인터럽트 분산 (RSS/RPS) → CPU 매핑 3. TCP 튜닝 (BBR, 버퍼) + XDP (필요 시) 빌드/컴파일러 최적화 1. LTO/ThinLTO 적용 (Clang) → 코드 크기↓ IPC↑ 2. PGO/AutoFDO → 프로파일 기반 분기/배치 최적화 3. CONFIG 튜닝: HZ, PREEMPT, 보안 완화 비용 측정 핵심 원칙 1. 추측하지 말고 측정하라 (Don't guess, measure!) — 모든 변경은 데이터로 검증 2. 한 번에 하나만 변경하라 — 여러 변경을 동시에 적용하면 원인 분석 불가

최적화와 관련된 다른 주제를 더 깊이 이해하고 싶다면 다음 문서를 참고하세요. 각 문서는 이 페이지에서 다룬 최적화 기법의 기반이 되는 서브시스템의 내부 구조와 동작 원리를 상세하게 설명합니다.

💡

추천 학습 순서: (1) perf 서브시스템 → PMU 원리와 perf 도구 완전 가이드. (2) CPU 캐시 → 캐시 계층, MESI 프로토콜, false sharing 상세. (3) 프로세스 스케줄러 → CFS/EEVDF 알고리즘, 로드밸런싱 내부. (4) 메모리 관리 → 페이지 할당자, slab, VMA 내부 구조. (5) io_uring → 비동기 I/O 인터페이스 설계와 커널 구현.

참고 자료