pahole: See Every Hole, Pad Byte, and Cache-Line Sin Hiding in Your Structs

2026-06-29

You write a struct in C, the compiler dutifully pads it out, and you have no idea your hot data structure just grew from 24 bytes to 40 because you put a char next to a long. pahole — short for "poke-a-hole" — ships in the dwarves package (Arnaldo Carvalho de Melo, perf maintainer). Point it at any binary compiled with -g and it prints every struct layout, the exact offset of every field, the size of every hole, and where you cross a cacheline boundary.

Compile something with debug info and ask:

$ cc -g -c thing.c -o thing.o
$ pahole -C my_packet thing.o
struct my_packet {
        char                       flag;                 /*     0     1 */

        /* XXX 3 bytes hole, try to pack */

        int                        seq;                  /*     4     4 */
        char                       type;                 /*     8     1 */

        /* XXX 7 bytes hole, try to pack */

        long                       payload;              /*    16     8 */

        /* size: 24, cachelines: 1, members: 4 */
        /* sum members: 14, holes: 2, sum holes: 10 */
        /* last cacheline: 24 bytes */
};

Ten wasted bytes out of 24 — that's 42% air. pahole will also reorganize for you:

$ pahole --reorganize -C my_packet thing.o
struct my_packet {
        long                       payload;              /*     0     8 */
        int                        seq;                  /*     8     4 */
        char                       flag;                 /*    12     1 */
        char                       type;                 /*    13     1 */
        /* size: 16, cachelines: 1, members: 4 */
};

/* saved 8 bytes! */

For a struct allocated a million times, that's 8 MB of RAM and L1 pressure gone, for free.

Where the mainstream alternative fails: you can stare at a sizeof() all day and not know why it's 40 instead of 32. gdb ptype /o shows offsets but not holes, doesn't suggest a packing, and won't scan every struct in an object file. __builtin_offsetof() at runtime means recompiling and printing. pahole works on already-shipped binaries — including ones you didn't build.

Real tricks worth knowing:

If you write performance-sensitive C, kernel modules, BPF, or wire protocols, pahole turns invisible padding into an editable text file. The Linux kernel uses it in tree to keep task_struct from blowing up; you can use it to keep your hot path inside one cacheline.

Key Takeaway: sizeof() tells you the bill; pahole tells you exactly which padding bytes are on it and how to delete them.

All newsletters