C Mastery / False Sharing and Allocation Overhead
Part 9 — Performance

False Sharing and Allocation Overhead

This chapter covers two subtle performance killers: false sharing (in multithreaded code) and allocation overhead (in allocation-heavy code).

Why This Matters

False sharing can make multithreaded code slower than single-threaded, even with no shared data. Allocation overhead can dominate the runtime of allocation-heavy programs. Both are invisible in profiles unless you know what to look for.

Prerequisites

Core Concept

False sharing

Two threads update *different* variables that happen to reside on the same cache line. The cache-coherence protocol invalidates the line across cores, so the threads serialize their updates — even though they never touch the same byte.

struct {
    int counter_a;   /* thread 1 updates */
    int counter_b;   /* thread 2 updates */
} counters;          /* both ints on the same 64-byte cache line */

Allocation overhead

Every malloc/free carries cost: metadata, free-list search, and possibly a system call. Many small allocations are far more expensive than a few large ones, and can fragment the heap.

Examples

False sharing

struct PaddedCounter {
    int value;
    char padding[60];   /* push to its own cache line */
};

Aligning each counter to its own cache line (typically 64 bytes) eliminates false sharing. C11 provides _Alignas:

struct alignas(64) Counter { int value; };

Allocation overhead

/* bad: many tiny allocations */
for (int i = 0; i < N; i++)
    nodes[i] = malloc(sizeof(Node));

/* good: one arena or a pool */
Node *block = malloc(N * sizeof(Node));   /* one allocation */

How It Works

A cache line is the unit of coherence. When a core writes any byte in a line, the whole line is invalidated in other cores' caches. Two "independent" variables on the same line thrash. Allocation overhead comes from the allocator's metadata and search; fewer, larger allocations amortize it.

Variations

Padding and alignment

_Alignas(64) or manual padding separates hot per-thread variables. On some platforms, malloc already returns 16-byte-aligned memory, but that does not help with cache-line separation.

Arena/pool allocation

Arenas and pools (c.mem.arena, c.mem.pool) avoid per-object overhead and improve locality.

Common Mistakes

Undefined Behavior

Portability

safe over-estimate, or query the platform.

Under the Hood

perf c2c (Linux) detects false sharing. Allocation profiling (heaptrack, Valgrind massif) measures allocation overhead.

Practical Usage

Exercises

1. Write a two-thread counter program and demonstrate false sharing, then fix with _Alignas(64). 2. Measure perf c2c to confirm the false sharing. 3. Compare many-small-allocations vs. one-arena allocation in a benchmark.

Deep Challenge

Write a false-sharing test with two atomic_int counters, show the slowdown, and fix it with alignment. Then explain why volatile is not the right tool and why atomics alone don't fix false sharing.

References

Verification

verified.`

Progress

Concept checkboxes

Mastery levels

ConceptCurrent level (0–8)Target level
c.perf.false-sharing06
c.perf.alloc-overhead05