Why Software Ignores Fast Hardware

Hardware has improved dramatically in the last decade—unified memory, neural engines, and multi-core processors are now commonplace. Yet software frequently fails to exploit this power. Cold starts still take seconds, and ETL pipelines still crawl. The root cause is often a lack of "mechanical sympathy," a concept popularized in software by Martin Thompson, who borrowed the phrase from Formula 1 champion Sir Jackie Stewart.

Thompson applied these ideas to build the LMAX Architecture, which processes millions of events per second on a single Java thread. The underlying principles—predictable memory access, cache awareness, single-writer ownership, and natural batching—are not exotic tricks. They are practical guidelines that can improve the performance of anything from an AI inference service to a distributed data platform.

Sequential Memory Wins

To be sympathetic to the CPU, you must understand how it sees memory. Memory is organized into a hierarchy: each core has its own registers, L1 and L2 caches; cores share an L3 cache; and all cores share main memory (RAM). Access latency increases by orders of magnitude at each level further from the core.

CPUs try to hide this latency by predicting future access. They assume that recently used memory will be reused, that adjacent memory will be needed, and that patterns are repetitive. When these assumptions hold, performance is excellent. When they fail—particularly with random access across memory pages—performance collapses.

The practical implication is simple: prefer linear scans over point lookups. For example, an ETL pipeline should filter a source database sequentially rather than issuing individual key-based queries.

Prefer algorithms and data structures that enable predictable, sequential access to data.

Cache Lines and False Sharing

Cache lines, typically 64 bytes long, are the unit of memory transfer between the CPU and its caches. This granularity creates a problem: when two CPU cores write to different variables that happen to reside in the same cache line, they inadvertently contend for ownership of that line. This is false sharing.

The result is that threads are forced to serialize their writes through the shared L3 cache, negating the benefit of multiple cores. Low-latency applications often pad data structures so that each variable occupies its own cache line.

  • Without padding, latency rises nearly linearly as threads are added.
  • With padding, latency remains nearly constant regardless of thread count.

This only affects writes, not reads. Multiple cores can read the same line concurrently. Atomic variables are a frequent victim because they are one of the few types safe to share and modify across threads. Any data structure written by multiple threads is a candidate for padding if you are chasing performance.

The Single Writer Principle

Multithreading brings race conditions, context-switching overhead, and the brutal cost of mutexes. The Single Writer Principle avoids these problems by design. Rather than protecting a resource with a lock, you give ownership of that resource to one dedicated thread. Other threads submit work to it via asynchronous messaging.

Consider a text embedding service built around an AI model. Most runtimes allow only one inference at a time. A naive design—guarding the model with a mutex—suffers head-of-line blocking under load. Refactored around a single-writer "actor" thread, requests become messages. The actor can then group these requests into single batch inference calls and return results asynchronously.

Avoid protecting writable resources with a mutex. Instead, dedicate a single thread to own every write, and use asynchronous messaging to submit writes to it.

Building Batches Naturally

With a single writer handling requests, the next question is how to batch them. Waiting for a fixed count means blocking for an unbounded time if load is low. A fixed interval bounds the waiting time but wastes latency on every batch. Natural batching is the solution.

With natural batching, the actor starts a batch the moment a request arrives and closes the batch when either the batch is full or the queue is empty.

StrategyBest (µs)Worst (µs)
Timeout200400
Natural100200

Assuming a fixed per-batch latency of 100µs, the difference is clear. A timeout-based approach with a 100µs window adds that wait to every request, giving a best case of 200µs and a worst case of 400µs per request. Natural batching introduces no artificial wait, yielding a best case of 100µs and an upper bound of 200µs.

The principle is not limited to AI models. Any single-writer consuming messages can benefit.

If a single writer handles batches of writes, build each batch greedily: start as soon as data is available, and finish when the queue is empty or the batch is full.

From Principles to Systems

These ideas are not confined to individual applications. Sequential data access scales from a memory array to a data lake. The single-writer pattern supports IO-bound services and provides a foundation for event-driven architectures. Structuring systems to respect the hardware allows performance to follow naturally, regardless of scale.

Before optimizing, prioritize observability. Define your SLIs and SLOs, measure the current state, and have clear performance goals. You cannot improve what you cannot measure—but once you measure and understand your bottlenecks, mechanical sympathy will tell you exactly where to intervene.

Where to Go Next

Mechanical sympathy reaches far beyond the ground covered here. Martin Thompson has written extensively on related concepts that reward further study:

Thompson introduced the term to software engineering while presenting the LMAX architecture in 2010, with details published here the following year. His dedicated blog began that same year and accumulated a substantial body of articles shortly thereafter.

One reader also pointed out the Tiger Style coding methodology during review. It casts a wider net than mechanical sympathy, but its principles complement the ones described here well.

Credit and Thanks

Martin Thompson deserves recognition for bringing mechanical sympathy into software engineering, for documenting it so thoroughly, and for kindly reviewing this article’s final draft.

Martin Fowler first pointed me to Thompson’s work years ago and gave thoughtful feedback and mentorship throughout the writing process.

Thanks also to Thoughtworkers Mica Beneke and Joseph Wilson for their help refining this piece.

For editing, I used Anthropic’s Claude Opus 4.6 (via Claude Code) to catch syntactical errors and to advise on structure and scope—for instance, whether memory barriers deserved a mention here or belonged in an appendix. AI was not used to produce any content or figures.

Significant Revisions

07 April 2026: published