pahole - Struct Layout & Cache-Line Analysis Cheatsheet
pahole (“poke-a-hole”) reads DWARF or BTF debug information and prints the actual memory layout of C structures — where each field sits, how much padding the compiler inserted, and how many cache lines the struct spans. Compilers pad structs to satisfy alignment, and a carelessly-ordered struct can waste dozens of bytes and straddle extra cache lines. On hot data structures that translates directly into cache misses. pahole makes the waste visible so you can reorder fields and shrink it.
Installation
| Platform | Command |
|---|
| Debian/Ubuntu | sudo apt install dwarves |
| Fedora/RHEL | sudo dnf install dwarves |
| Arch Linux | sudo pacman -S pahole |
| From source | build the dwarves project |
| Verify | pahole --version |
Your binary must be built with debug info (-g). Stripped binaries have nothing for pahole to read.
Basic Usage
| Command | Description |
|---|
pahole ./myprogram | Show all structs in the binary |
pahole -C mystruct ./myprogram | One specific struct |
pahole -C task_struct /sys/kernel/btf/vmlinux | Kernel struct via BTF |
pahole --sizes ./myprogram | List struct sizes |
pahole -H 1 ./myprogram | Only structs with ≥1 hole |
Reading the Output
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 */
};
| Annotation | Meaning |
|---|
/* offset size */ | Byte offset and size of each field |
XXX N bytes hole | Padding the compiler inserted |
size: | Total struct size |
cachelines: | How many 64-byte lines it spans |
sum holes: | Total wasted bytes |
last cacheline: | Bytes used in the final line |
Finding Waste Across a Codebase
| Command | Finds |
|---|
pahole -H 1 ./bin | Structs with holes |
pahole --nr_members ./bin | Sorted by member count |
pahole -c 2 ./bin | Structs spanning ≥2 cache lines |
pahole -E ./bin | Expand nested structs inline |
pahole -a ./bin | Show alignment/packing details |
# Which structs waste the most memory to padding?
pahole -H 8 ./myprogram | head -50
Fixing Layout
The standard fix is ordering fields from largest to smallest alignment:
/* Before: 48 bytes, 11 wasted */
struct bad { char flag; long counter; char name[16]; int id; void *ptr; };
/* After: 40 bytes, 0 wasted */
struct good { long counter; void *ptr; char name[16]; int id; char flag; };
| Technique | Effect |
|---|
| Order by descending alignment | Eliminates most holes |
| Group hot fields together | Keeps them in one cache line |
| Split cold fields into a second struct | Hot data stays dense |
__attribute__((packed)) | Removes padding — but may slow unaligned access |
| Bitfields | Pack small flags |
pahole can even suggest a reordering with --reorganize:
pahole --reorganize -C mystruct ./myprogram
Kernel Work (BTF)
| Command | Purpose |
|---|
pahole -C task_struct /sys/kernel/btf/vmlinux | Inspect a live kernel struct |
pahole -J vmlinux | Generate BTF from DWARF |
pahole --btf_encode | BTF encoding for eBPF tooling |
BTF generation is why dwarves is a kernel build dependency — eBPF’s CO-RE relies on it.
When This Matters
| Situation | Impact |
|---|
| Millions of instances of a struct | Memory savings scale linearly |
| Hot loop touching struct fields | Fewer cache lines = fewer misses |
| Network/disk serialization | Smaller structs, less I/O |
| Embedded/constrained memory | Every byte counts |
| A struct used twice | Do not bother |
| Tool | Answers |
|---|
| pahole | How is this struct laid out, and where is the waste? |
| perf | Am I actually cache-missing? |
| valgrind (cachegrind) | Simulated cache behavior |
| heaptrack | Where does heap memory go? |
Use perf to confirm cache misses are your bottleneck, then pahole to fix the layout causing them.
Resources