All posts

performance

33 posts
LINUX

The Fleet: Six Machines, and What the Hypervisor Can't Do

The plan was to consolidate everything onto one Proxmox box. Then a perf tool needed real PMU counters, a firmware flash needed a real serial port, and testing on ARM meant a phone running a custom kernel. Meet the fleet — and what each machine taught me.

PERFORMANCE

Reading Flame Graphs: Where Your CPU Time Actually Goes

A flame graph turns thousands of stack samples into one picture where width is time and the widest boxes are your problem. How sampling profilers work, how to read the graph in ten seconds, and the traps — inlining, wait time, broken stacks — that mislead beginners.

LINUX

mmap vs read(): When Mapping a File Actually Wins

mmap is sold as the fast way to read files, but it loses as often as it wins. The deciding factor is page faults versus syscalls — and whether you touch the data once or many times.

PERFORMANCE

False Sharing: The Cache Line That Kills Your Parallelism

You split work across eight threads with zero shared state, and it runs slower than one thread. The culprit is false sharing — independent variables that happen to live on the same cache line, fighting over it.

MEMORY

How malloc Works: Arenas, Bins, and Fragmentation

malloc looks like one function, but underneath it's a memory allocator juggling free lists, size classes, and the kernel. Knowing how it works explains fragmentation, allocator contention, and why your RSS never goes down.

ALGORITHMS

Hash Tables: The Real Cost of a Collision

Hash tables promise O(1) lookups, and then a benchmark shows them losing to a plain array. The gap between the textbook and reality is collisions, cache misses, and the resize you forgot about.

COMPILERS

How a Compiler Turns Your Loop Into SIMD

Modern CPUs can add eight numbers in one instruction, and your compiler will rewrite plain loops to use them — but only if you don't accidentally forbid it. Here's what auto-vectorization needs to fire.

DATABASES

B-Trees vs LSM-Trees: The Storage Engine Tradeoff

Every database picks one of two ways to put data on disk. The choice between B-trees and LSM-trees is really a choice about whether you optimize for reads or writes — and you can't have both for free.

NETWORKING

TCP Slow Start: Why Your First Megabyte Is Always Slow

A fresh TCP connection doesn't blast data at line rate — it tiptoes. Slow start is the reason a cold connection feels sluggish, and understanding it explains half of web performance.

LINUX

epoll: The Syscall Under Every Event Loop

nginx, Node, Redis, and every async runtime you use sit on one Linux primitive. Why select died at scale, how epoll inverts the cost model, the level- vs edge-triggered trap, and the thundering herd that took years to fix.

DATABASES

SQL Indexes Under the Hood: Why Your Query Ignores Yours

An index is a sorted structure with physical properties, not a magic 'go faster' flag. Leftmost prefixes, covering indexes, selectivity, and the honest reasons the planner chose a sequential scan over the index you lovingly created.

PERFORMANCE

Zero-Copy I/O: Stop Paying to Move Bytes You Never Touch

A naive file-to-socket server copies every byte four times and crosses the kernel boundary twice per chunk. sendfile collapses it to zero user-space copies — and the lineage from there (splice, MSG_ZEROCOPY, io_uring) is the story of modern fast I/O.

COMPILERS

What -O2 Actually Does: A Tour of Compiler Optimizations

The gap between -O0 and -O2 is often 10x, and it's not magic — it's a pipeline of individually simple transformations over an intermediate representation. SSA, inlining, the passes that matter, and why your careful micro-optimization was already done for you.

LINUX

Anatomy of a System Call: What read() Really Costs

A function call costs ~2ns. A syscall costs ~100ns before doing any work — and that's after decades of optimization plus a Spectre-era tax. Where the time goes, why vDSO exists, and what that means for API design.

MEMORY

Arena Allocators: Free Everything at Once

The fastest malloc is a pointer bump; the fastest free is resetting one offset. Arenas trade individual deallocation for speed, locality, and the death of use-after-free within a lifetime — a trade half your workloads should take.

ALGORITHMS

Big-O in Practice: When the Constant Factor Eats the Asymptote

Binary search loses to linear scan on small arrays. Hash maps lose to sorted vectors. Big-O tells you how algorithms scale, not which one is faster — and on modern hardware the gap between those two questions is enormous.

WEB

HTTP Caching: The Distributed System You Already Deployed

Between your server and each user sit browser caches, CDN tiers, and shared proxies you don't operate — all obeying Cache-Control headers you may have set by accident. The freshness/validation model, and the headers that cause incidents.

SYSTEMS

SSD Internals: Why Your Disk Is a Small Computer That Lies

Flash can't overwrite in place, erases in giant blocks, and wears out per cell — so every SSD runs a translation layer that remaps, garbage-collects, and levels wear behind your back. Write amplification, TRIM, and the cliff at 90% full, explained.

CONCURRENCY

Spinlocks vs Mutexes: When Burning CPU Is the Right Call

One lock burns CPU while it waits; the other pays a syscall to sleep. The right choice depends on hold time versus context-switch cost — and the best answer, futexes and adaptive locks, is 'both, in that order'.

PERFORMANCE

Branch Prediction: Why Sorted Data Runs Faster

The same loop over the same data runs 5x faster when the array is sorted. The instructions are identical. The difference is a gambler inside your CPU that's either winning or losing.

DATABASES

How Query Planners Think: Costs, Cardinality, and Lies

SQL says what, the planner decides how — by estimating row counts through a chain of guesses and picking the cheapest of thousands of plans. When the estimate is wrong by 1000x, so is the plan. Reading EXPLAIN is learning to audit those guesses.

ALGORITHMS

Arrays vs Linked Lists: The Benchmark Textbooks Don't Run

The textbook says linked lists win at insertion, O(1) vs O(n). Run the benchmark and the array wins insertion-heavy workloads too — often by 10x. The memory hierarchy voted, and it didn't vote for pointers.

SYSTEMS

Ring Buffers: The Data Structure at Every Fast Boundary

NIC queues, io_uring, audio pipelines, logging, SPSC channels — wherever a fast producer meets a consumer on a budget, there's a fixed array with two indices. The design decisions that matter: power-of-two sizing, the full/empty problem, and who waits.

ALGORITHMS

What Your Language's sort() Actually Runs

Nobody ships textbook quicksort. Python runs Timsort, C++ runs introsort, and both are layered hybrids built around one insight: real-world data isn't random, and worst cases must be impossible.

DATABASES

Buffer Pools: Why Databases Don't Trust Your OS

Databases read disk through fixed-size pages cached in a buffer pool they manage themselves — duplicating what the kernel's page cache already does. The reasons why (eviction control, WAL ordering, O_DIRECT) are a masterclass in when to bypass the OS.

PERFORMANCE

CPU Caches: The Memory Hierarchy Your Code Lives In

DRAM is ~100ns away; your CPU can do ~400 arithmetic operations in that time. Caches exist to hide that gap, and whether they succeed is decided by how your code lays out and walks its data.

MEMORY

Page Tables and the TLB: The Cost of Every Address

Every pointer dereference triggers a virtual-to-physical translation through a four-level radix tree. The only reason your machine isn't 5x slower is a tiny cache called the TLB — and when it misses, you feel it.

FUNDAMENTALS

Why 0.1 + 0.2 ≠ 0.3, and Why It's Worse Than You Think

Everyone knows floating point is 'imprecise.' Fewer know that the same expression can give different results on different machines, that summation order changes your answer, and why associativity — the thing algebra promised you — is a lie in float.

NETWORKING

QUIC and HTTP/3: Rebuilding TCP in Userspace, on Purpose

HTTP/2 fixed application-layer head-of-line blocking and exposed the transport-layer version underneath. QUIC's answer: reimplement reliability per-stream over UDP, encrypt the transport headers, and make connections survive IP changes.