Skip to content
Work Practice Products Coding interviews Machine learning coding Quant developer HFT course Blog Method Why me About Enquire Book a call

Quant developer HFT course · Module 8: Measuring latency

Measuring latency: percentiles, tails and honest numbers

Lesson · 12 min

The question

Your manager asks how fast the trading system is. You have nanosecond timestamps for a day of messages. What do you report, and why not the average?

The distribution, not a number

Latency is a distribution. Most messages take about the same time; a few take much longer, because of a cache miss here, an interrupt there, a page fault, a burst queueing behind itself. Plotted, it has a sharp body and a long tail to the right.

1.61.82.02.22.42.62.83.03.54610204080150p50p99p99.9messages (illustrative), by latency in µs
An illustrative latency distribution with a log-like axis. The median sits in the body; the 99th and 99.9th percentiles are in a tail many times slower, which the mean barely reflects.

The mean mixes the two. A handful of 100 µs outliers among millions of 2 µs messages moves the mean a little and tells you nothing about either the typical message or the bad ones. In trading, the bad ones matter disproportionately: the moments when the system is slowest are often the busiest, most volatile moments, which are when speed earns money and slowness loses it.

Percentiles

Report percentiles: the latency below which a given fraction of messages fall.

  • p50 (median): the typical message.
  • p90, p99: how bad it gets regularly. On a million messages a day, 1% is ten thousand messages.
  • p99.9, p99.99: the rare events.
  • max: the worst case, which is noisy but should never be ignored.

Always give the count and the period with them. "p99 of 4.1 µs over 18 million messages on Tuesday" means something; "p99 of 4.1 µs" alone does not.

Two rules about percentiles that interviewers like:

  • You cannot average them. The mean of each hour's p99 is not the day's p99. Combine the underlying histograms, then compute.
  • Tail percentiles need enough data. The p99.99 of ten thousand samples is the single worst sample.

Recording them cheaply

Storing every latency for a day and sorting it works offline. In a live system, the standard tool is a histogram with logarithmically sized buckets, in the style of HdrHistogram: recording a value is incrementing one counter, the memory is fixed, and the relative precision is bounded (say 1%) across a range from nanoseconds to seconds. Histograms from many threads, processes or hours combine by adding counters.

The recording must be cheap and must not distort the hot path: take timestamps on the hot thread, but push them through a ring to another thread that updates histograms (Module 4).

What to measure: tick-to-trade

For a trading system, the defining number is tick-to-trade: from the market-data packet that triggers a decision arriving at the network card to the resulting order leaving it, measured with hardware timestamps at both ends (Module 7). Internal stages (decode, book update, strategy, risk, encode) are measured with software timestamps from the same clock source, so a regression can be attributed to a stage. Only messages that lead to orders have a tick-to-trade; the time to process every message (whether or not it triggers anything) is tracked separately, because a slow book update on an unrelated message can delay the next one that matters.

Coordinated omission

A classic way load tests lie. A client sends a request, waits for the response, records the time, sends the next. If the system stalls for 10 ms, the client stalls with it and sends nothing; it records one slow response and then continues at normal speed.

Coordinated omission. A test client that waits for each response before sending the next skips the requests that should have been sent during a stall, so it records one slow sample instead of six.

  1. Step 1 / 3
    schedulenaive load testwhat users saw

    Requests are due every millisecond. The first two are fast.

  2. Step 2 / 3
    schedulenaive load testwhat users sawone slow requestsix requests delayed

    The system stalls. The naive client waits for its one slow request and sends nothing else.

  3. Step 3 / 3
    schedulenaive load testwhat users sawone slow requestsix requests delayed

    Six requests were due during the stall and real users would have waited on every one. The naive test recorded one.

But a real source does not wait. An exchange sends market data whether or not you are keeping up; during the stall, many messages arrive and queue, and each of them is late by the remaining stall time. The naive test recorded one bad sample where reality had hundreds. Its percentiles look far better than the system deserves.

The fixes: generate load on a fixed schedule independent of responses, and measure each request's latency from when it should have been sent; or correct the histogram for the missing samples, which HdrHistogram supports. In production, measure from the packet's arrival timestamp at the card, which captures queueing in front of your code.

Benchmarks and production

Lab measurements on replayed data establish what the code can do; production measurements show what it does. The two disagree, and the production numbers win. Keep both, and alert on changes in production percentiles, because a regression in p99 after a release is often the first sign of a problem.

Check yourself

Question 1 of 3

Why is the mean a poor summary of latency for a trading system?

Question 2 of 3

You have hourly p99 values for a day. How do you get the day's p99?

Question 3 of 3

What does coordinated omission do to a load test's results?

In the interview

Say your own answer out loud first, then open the one we would give.

How do you report the latency of a system?

As a distribution: percentiles such as p50, p99, p99.9 and the maximum, with the number of samples and the period. The mean hides the tail, and in trading the tail lines up with the moments that matter. For a trading system the key measurement is tick-to-trade with hardware timestamps, broken down by stage.

How do you record latency percentiles in a live system?

Timestamps on the hot path, passed through a ring to another thread that records them in log-bucketed histograms such as HdrHistogram: constant time per record, fixed memory, bounded relative error, and histograms can be merged across threads and time. Percentiles are computed from merged histograms, never averaged.

What is coordinated omission?

A measurement bias where the load generator waits for responses, so during a stall it stops sending and records one slow sample instead of all the requests that would have been delayed. It makes tails look far better than they are. Fixed-schedule load generation measured from intended send times, or histogram correction, avoids it.