pahole - 구조체 레이아웃 및 캐시 라인 분석 치트시트
pahole (“poke-a-hole”)은 DWARF 또는 BTF 디버그 정보를 읽고 C 구조체의 실제 메모리 레이아웃을 인쇄합니다 — 각 필드가 앉는 곳, 컴파일러가 삽입한 패딩의 양, 구조체가 걸친 캐시 라인의 개수. 컴파일러는 정렬을 만족시키기 위해 구조체를 패딩하며, 부주의하게 정렬된 구조체는 수십 바이트를 낭비할 수 있고 추가 캐시 라인을 걸칠 수 있습니다. 핫 데이터 구조체에서 이는 직접 캐시 미스로 변환됩니다. pahole은 낭비를 시각화하므로 필드를 재정렬하고 축소할 수 있습니다.
설치
| 플랫폼 | 명령어 |
|---|
| Debian/Ubuntu | sudo apt install dwarves |
| Fedora/RHEL | sudo dnf install dwarves |
| Arch Linux | sudo pacman -S pahole |
| 소스에서 | dwarves 프로젝트 빌드 |
| 확인 | pahole --version |
바이너리는 디버그 정보로 구축되어야 합니다 (-g). 제거된 바이너리에는 pahole이 읽을 것이 없습니다.
기본 사용법
| 명령어 | 설명 |
|---|
pahole ./myprogram | 바이너리의 모든 구조체 표시 |
pahole -C mystruct ./myprogram | 하나의 특정 구조체 |
pahole -C task_struct /sys/kernel/btf/vmlinux | BTF를 통한 커널 구조체 |
pahole --sizes ./myprogram | 구조체 크기 나열 |
pahole -H 1 ./myprogram | ≥1 구멍이 있는 구조체만 |
출력 읽기
struct example {
char flag; /* 0 1 */
/* XXX 7 bytes hole, try to pack */
long int counter; /* 8 8 */
char name[16]; /* 16 16 */
int id; /* 32 4 */
/* XXX 4 bytes hole, try to pack */
void * ptr; /* 40 8 */
/* size: 48, cachelines: 1, members: 5 */
/* sum members: 37, holes: 2, sum holes: 11 */
};
| 주석 | 의미 |
|---|
/* offset size */ | 각 필드의 바이트 오프셋 및 크기 |
XXX N bytes hole | 컴파일러가 삽입한 패딩 |
size: | 총 구조체 크기 |
cachelines: | 걸친 64바이트 라인의 개수 |
sum holes: | 낭비된 총 바이트 |
last cacheline: | 최종 라인에서 사용된 바이트 |
코드베이스 전체의 낭비 찾기
| 명령어 | 찾기 |
|---|
pahole -H 1 ./bin | 구멍이 있는 구조체 |
pahole --nr_members ./bin | 멤버 개수로 정렬 |
pahole -c 2 ./bin | ≥2 캐시 라인을 걸친 구조체 |
pahole -E ./bin | 중첩된 구조체를 인라인으로 확장 |
pahole -a ./bin | 정렬/패킹 세부사항 표시 |
# 어느 구조체가 패딩에 가장 많은 메모리를 낭비하나?
pahole -H 8 ./myprogram | head -50
레이아웃 수정
표준 수정은 큰 것부터 작은 것순으로 필드를 정렬하는 것입니다:
/* 이전: 48 바이트, 11 낭비 */
struct bad { char flag; long counter; char name[16]; int id; void *ptr; };
/* 이후: 40 바이트, 0 낭비 */
struct good { long counter; void *ptr; char name[16]; int id; char flag; };
| 기법 | 효과 |
|---|
| 내림차순 정렬로 정렬 | 대부분의 구멍 제거 |
| 핫 필드를 함께 그룹화 | 하나의 캐시 라인에 유지 |
| 콜드 필드를 두 번째 구조체로 분할 | 핫 데이터가 조밀하게 유지됨 |
__attribute__((packed)) | 패딩 제거 — 하지만 정렬되지 않은 접근을 느리게 할 수 있음 |
| Bitfield | 작은 플래그 압축 |
pahole은 --reorganize로 재정렬을 제안할 수도 있습니다:
pahole --reorganize -C mystruct ./myprogram
커널 작업 (BTF)
| 명령어 | 목적 |
|---|
pahole -C task_struct /sys/kernel/btf/vmlinux | 라이브 커널 구조체 검사 |
pahole -J vmlinux | DWARF에서 BTF 생성 |
pahole --btf_encode | eBPF 도구를 위한 BTF 인코딩 |
BTF 생성이 dwarves가 커널 빌드 종속성인 이유입니다 — eBPF의 CO-RE는 이에 따라 달라집니다.
이것이 중요할 때
| 상황 | 영향 |
|---|
| 구조체의 수백만 인스턴스 | 메모리 절약이 선형으로 확장 |
| 구조체 필드를 터치하는 핫 루프 | 더 적은 캐시 라인 = 더 적은 미스 |
| 네트워크/디스크 직렬화 | 더 작은 구조체, 더 적은 I/O |
| 임베디드/제약 메모리 | 모든 바이트가 중요함 |
| 구조체가 두 번 사용됨 | 신경 쓰지 마십시오 |
pahole vs 관련 도구
| 도구 | 답변 |
|---|
| pahole | 이 구조체가 어떻게 배치되고, 낭비가 어디에 있나? |
| perf | 실제로 캐시를 놓치고 있나? |
| valgrind (cachegrind) | 시뮬레이션된 캐시 동작 |
| heaptrack | 힙 메모리가 어디로 가는가? |
perf를 사용하여 캐시 미스가 병목이라는 것을 확인한 후 pahole으로 레이아웃을 수정합니다.
리소스