Quant Memo
Core

C++ for Low-Latency Trading

C++ dominates the hot path of trading systems because it compiles to machine code with no garbage collector and no interpreter in the way, giving a programmer direct, predictable control over memory and timing.

Prerequisites: The GIL and Python Threading

Python is where most quant research happens, and it is essentially never what runs on the critical path of an exchange-facing trading system. The gap between "prototype that proves the idea works" and "code that reacts to a tick in under a microsecond" is wide, and C++ is the language that most consistently closes it — not because it's a fashionable choice, but because of specific, checkable properties Python doesn't have: no interpreter, no automatic memory manager pausing your program, and control over exactly where in memory your data lives.

The idea: nothing between you and the machine

A Python statement like total = total + tick.price is executed by an interpreter that, every single time, has to figure out what type total and tick.price are, look up what + means for them, and manage reference counts (see The GIL and Python Threading). C++ code doing the same thing is compiled ahead of time into machine instructions specific to that exact operation on that exact type — by the time the program runs, all of that lookup work has already happened once, at compile time, and the running program just executes the resulting instructions directly.

The other structural difference is memory. Python (and Java, and most managed languages) use a garbage collector that periodically scans memory to find and free objects nobody references anymore — convenient, but it runs on its own schedule and can pause your program for an unpredictable amount of time, which is intolerable when you need to guarantee a response within a fixed number of microseconds. C++ gives the programmer direct control: memory is allocated and freed explicitly (or automatically at a precisely known point, via RAII — Resource Acquisition Is Initialization, where an object's destructor runs deterministically when it goes out of scope), so there is no background process that can stall your hot path.

Worked example: predictable versus unpredictable timing

Trace what happens processing 1,000 incoming ticks in a language with a garbage collector versus C++. In the managed-language version, most ticks process in, say, 2 microseconds — but every so often (unpredictably, whenever the collector decides memory pressure warrants it) one tick's processing takes 500 microseconds or more because a collection cycle runs during it. Across 1,000 ticks, 999 might take ~2 μs and one might take 500 μs — the average looks fine (about 2.5 μs), but the tail latency (the worst-case, which is what matters when you're racing another firm on every single tick) is 250x the typical case.

In C++, memory for a fixed-size ring buffer of ticks is allocated once at startup; processing tick number 500 does exactly the same amount of work as tick number 1 — allocate nothing, touch a few cache lines, write a result. There is no background process that can interrupt it, so the timing distribution is tight: nearly every tick takes close to the same 0.2-0.5 microseconds, with no long unpredictable tail from garbage collection. This is the concrete reason a matching engine or a market-data handler (How a Matching Engine Works) is written in C++ (or Rust, or hand-tuned assembly for the most extreme cases): not because 2 μs versus 0.3 μs always matters, but because the worst case has to be bounded and predictable, and a garbage collector fundamentally cannot promise that.

// Pre-allocated fixed-size ring buffer -- no allocation on the hot path
struct TickBuffer {
    static constexpr size_t CAPACITY = 1 << 16;   // power of two, cheap wraparound
    Tick data[CAPACITY];
    size_t head = 0;

    void push(const Tick& t) {
        data[head & (CAPACITY - 1)] = t;   // bitwise AND replaces a modulo -- see bit-manipulation-fundamentals
        ++head;
    }
};

Worked example: stack allocation versus heap allocation

A second concrete cost C++ lets you avoid is heap allocation itself. Allocating memory on the heap (via new, or Python creating an object) requires the allocator to find a free block, which can involve searching, locking (if multiple threads are involved), and bookkeeping — commonly 20-100+ nanoseconds, and much worse under contention. A stack allocation — a local variable inside a function — costs essentially nothing: it's just moving the stack pointer, a single instruction, because the compiler already knows its exact size ahead of time.

// Heap allocation: a real, possibly-slow request to the allocator
Order* o = new Order(id, price, qty);   // ~20-100+ ns, plus a later delete

// Stack allocation: the compiler reserves the space at compile time
Order o(id, price, qty);                // effectively free, destroyed automatically
                                         // when o goes out of scope (RAII)

A hot-path order object built fresh on the stack for every incoming message, versus the same object allocated on the heap, differ by roughly two orders of magnitude in construction cost alone — multiplied across millions of messages a day, this is a large, measurable fraction of a system's total latency budget.

processing time per tick GC language: tight cluster + rare long tail GC pause C++: narrow, no tail
A garbage-collected language can look fast on average and still have a rare, large pause that dominates worst-case latency. C++'s absence of a collector removes that tail entirely.

C++'s advantage on the hot path is not raw arithmetic speed — it's the removal of unpredictability: no interpreter dispatch overhead, no garbage-collector pause, and the ability to choose exactly where data lives (stack versus heap) and when it's freed. That combination is what lets a system bound its worst-case latency, not just its average.

Where this shows up

In a quant-dev interview, expect questions on RAII, the difference between stack and heap allocation, and why a garbage-collected language struggles to guarantee tail latency — these test whether a candidate understands why C++ is chosen, not just that it is. In production, the split is consistent across the industry: research, signal discovery, and backtesting run in Python for iteration speed, while the order-entry path, the market-data handler, and the matching engine itself run in C++ (occasionally Rust), because that is the code whose worst-case timing directly determines whether a strategy gets filled first or last.

Writing C++ does not automatically make code fast — a naive C++ program full of unnecessary heap allocations, virtual function calls, and cache-unfriendly data layouts can lose to well-vectorized Python (see Vectorization vs Loops) on a batch numerical task. The language gives you the tools for predictable low latency; using them correctly is a separate, deliberate discipline.

Related concepts

Practice in interviews

Further reading

  • Stroustrup, The C++ Programming Language (4th ed., ch. 1)
  • Carruth, Efficiency with Algorithms, Performance with Data Structures (CppCon 2019)
ShareTwitterLinkedIn