Practical interview design for engineering managers — cache behaviour, lock-free patterns, and kernel bypass.
Most low-latency C++ interviews fail in the same way: they test C++, but not low-latency. The candidate writes clean code, the interviewer feels good about the conversation, and the hire turns out to be incapable of reasoning about cache lines or kernel bypass under production load. This guide is for engineering managers at HFT firms and systematic funds who need a screen that actually distinguishes the latency-aware engineer from the strong generalist who happens to write fast-looking code.
Low-latency C++ engineering clusters around three areas:
Hardware-aware programming. Understanding CPU cache hierarchies (L1/L2/L3), cache line sizes (typically 64 bytes), false sharing, NUMA architecture on multi-socket systems, TLB misses and memory prefetching. A low-latency engineer writing code for a 3GHz CPU needs to think in terms of cycles, not milliseconds.
Lock-free and wait-free algorithms. Avoiding mutexes and condition variables on the hot path. Understanding C++ memory ordering semantics (std::memory_order_relaxed, acquire, release, seq_cst), the MESI cache coherence protocol, and when double-checked locking is safe.
System-level optimisation. Kernel bypass networking (DPDK, RDMA), CPU pinning, interrupt affinity, huge pages, NUMA-aware memory allocation, and the Linux real-time kernel. Understanding co-location realities: cross-connect latencies, kernel bypass vs NIC timestamping, PTP for clock synchronisation.
Q: You have two arrays of 1,000,000 integers. In loop A, you iterate through array1[i] then array2[i] (sequential). In loop B, you iterate through array1[i] then array1[1000000-i] (forward and reverse). Which is faster and why?
What you are testing: L1/L2 cache line behaviour, hardware prefetcher operation. Loop A is cache-friendly — both arrays are accessed sequentially and the prefetcher does its job. Loop B causes cache pressure from the reverse access pattern, which the prefetcher cannot handle well. Strong candidates will explain cache line size (64 bytes = 16 ints) and approximately quantify the difference.
Q: You have a struct with bool followed by double followed by bool. What is sizeof(this struct) and why? How would you reorder the fields to reduce it?
What you are testing: struct packing, alignment requirements, padding. The default layout is 24 bytes (1 + 7 padding + 8 + 1 + 7 padding). Reordered as double, bool, bool it becomes 16 bytes. Reveals whether they understand data alignment and cache line efficiency in practice.
Q: You are experiencing 200 microsecond worst-case latency spikes in a system that normally achieves 5 microseconds P50. What is your investigation approach?
Strong answers will cover: OS scheduling jitter (CPU frequency scaling, C-states, IRQ affinity), memory allocation patterns, lock contention, network stack variability, kernel interrupt handling. A follow-up worth asking: "Is this a bimodal distribution or a long tail?" Engineers who think in P99 and P99.9 — not just P50 — have the right mental model for a trading context where the worst-case tick matters, not the average.
Q: Implement a lock-free SPSC (single-producer, single-consumer) queue. What are its performance characteristics versus a mutex-based queue?
A strong candidate can write the SPSC queue using std::atomic and correct memory ordering semantics. A great candidate will also discuss: cache line alignment of head and tail to prevent false sharing, the tradeoff between seq_cst and relaxed ordering, and when SPSC is the right data structure.
Q: What is the ABA problem and how do you address it in a lock-free stack?
ABA: a lock-free compare_exchange reads value A, another thread changes it to B then back to A, and the original thread's CAS succeeds when it should not. Solutions: tagged pointers, hazard pointers, RCU.
Q: When is it safer to use a spinlock than an OS mutex?
Spinlocks are appropriate when contention is low and lock-hold time is short — a few hundred nanoseconds. Spinlocks become harmful when there are more spinning threads than available cores, or when lock-hold time is variable and could exceed a context-switch penalty (typically 1–3 microseconds on a tuned Linux system).
Q: You have optimised a hot code path. How do you prove the change actually helped — and didn't just move the problem?
This question separates engineers who operate empirically from those who optimise by feel. Strong answers will cover:
rdtsc / rdtscp for cycle-accurate timing of a specific code path, with awareness of serialisation requirements and CPU frequency scaling interactions.
perf stat for hardware counter measurement — cache misses, branch mispredictions, IPC — across a full run rather than a microbenchmark.
perf c2c for false sharing detection. This replaces older cachegrind-based workflows in production teams; perf c2c identifies cache lines being contended across cores, which is the actual problem you are looking for.
Intel VTune / AMD uProf for full pipeline analysis on the target microarchitecture.
The follow-up that separates good from great: "What's the difference between a microbenchmark result and your end-to-end P99 latency — and why might they diverge?" Engineers who understand that a tight microbenchmark can look excellent while the system-level tail latency worsens are thinking at the right level of abstraction.
Green flags:
Candidate can quantify. "L1 hit is about 4 cycles, L2 hit about 12, L3 hit about 40, DRAM access about 200" — not exact figures, but the right order of magnitude.
Candidate has profiled their own code. "I used perf c2c to identify a false sharing hotspot between the head and tail pointers in our order book snapshot code."
Candidate understands the hardware context. "At our co-location setup, the round-trip to the exchange gateway was 450ns, so any code path adding more than 50ns was worth examining."
Red flags:
Generic C++ optimisation advice without low-latency context. "I always use const references and avoid copies." Technically correct but does not demonstrate low-latency specific knowledge.
Unable to discuss memory model. A C++ engineer working on a low-latency system who cannot explain why std::atomic is needed is a significant gap.
Premature micro-optimisation focus. Candidates who jump to micro-optimisations before addressing architectural issues (false sharing, lock contention, unnecessary allocations) often have the wrong mental model.
Cannot reason about latency distributions. Candidates who talk only in averages and have no framework for P99, P99.9 or worst-case behaviour are unlikely to be effective in a context where tail latency directly affects execution quality.
Tell us what you're hiring for and we'll help you design the screen — and shortlist against it.
Platinum & Partners — Specialist quant and systematic search and contracting. London · New York · Singapore · Hong Kong