InfrastructureSenior

Model Serving

Model serving is putting a trained model behind an interface that answers requests. Once a model is trained, serving it is a latency and cost problem before it is a machine learning problem. The inference itself is usually the smallest part of the response time, the hardware is throughput hardware being asked to do a latency job, and the thing most teams monitor — accuracy — is the one thing they cannot see live. This page covers the request path, the three shapes of serving and why the cheapest usually wins, batching and the right scaling signal, how to ship a new version without betting on it, and what to watch when the truth arrives weeks late.

The slow part
Rarely the model
Scale on
Queue depth
Accuracy live
Not observable

The model is not the slow part

Ask a team where their serving latency goes and most will say the model, because the model is the part they built and the part they profile. Then somebody instruments the whole path and finds the answer somewhere else: the service spent longer fetching the features the model needed than it spent running the model. That is the normal result, not the surprising one, and it changes what you should optimise first.

Serving is a request path, and the latency budget is divided across all of it rather than spent on the model. In a typical forty millisecond response, network costs about six milliseconds, fetching the features the model needs about fourteen, preprocessing about five, the inference itself about eleven, and serialisation and other overhead about four. Inference is therefore not the largest slice, which is why teams that profile only the model tune the smallest part of the problem. The practical consequences: cache or co-locate the feature store before optimising the model, measure the whole path rather than the predict call, and treat the model as one step among several rather than as the system.

The practical discipline is to measure the path rather than the call. Put a span around each step — receive, fetch features, preprocess, infer, postprocess, serialise — and look at the percentiles rather than the mean, because a mean hides exactly the requests users complain about. And set a budget in milliseconds before you choose an architecture, since the budget decides whether you can afford a network hop to a feature store at all, and that is a design question rather than a tuning one.

Three shapes of serving, and the cheapest one usually wins

Before building a service, answer a smaller question: does the prediction have to be made while the user waits? A great many do not. If the set of things you predict about is known in advance and changes slowly — customers, products, accounts — you can compute the predictions on a schedule, write them to a table, and let the application read a row. That is not a compromise; it is a serving architecture with no p99, no autoscaling and no cold start.

  • Batch (precomputed)

    Score everything nightly, write to a table, read a row at request time. Cheapest to run and to operate. Fails only when the input is not known in advance.

  • Online (per request)

    A service that takes input and returns a prediction. Necessary when the input arrives with the request — a search query, a basket, a form. Everything hard on this page is about this shape.

  • Streaming

    Predictions attached to events as they flow — a fraud score written onto each transaction. Latency matters but nobody is watching a spinner, which is a much kinder constraint.

A hybrid is common and worth knowing as a pattern: precompute the expensive parts on a schedule and combine them with request-time inputs in a cheap final step. A recommendation system that scores every user against every item nightly and then re-ranks the top few hundred with fresh session context is doing exactly this, and it turns an impossible latency budget into an easy one. Reach for it before reaching for a bigger machine.

Batching, accelerators and the right scaling signal

A GPU is throughput hardware. It reaches its rated performance by doing the same operation across many rows at once, and a single request of one row uses a small fraction of it while occupying the whole device. That is why serving runtimes do dynamic batching: hold arriving requests for a few milliseconds, group whatever showed up, run them as one batch. You trade a small, bounded amount of latency for a large multiple of throughput, and the maximum wait is a number you set rather than a hope.

SignalWhat it tells youVerdict
CPU utilisationHow busy the host is doing everything except the inference on the accelerator.The default, and wrong for GPU serving: the device saturates while CPU sits at 20%.
Queue depth / wait timeHow many requests are waiting for a slot, which is the definition of not keeping up.The signal that actually corresponds to user pain. Scale on this.
Requests per secondArrival rate, with no knowledge of how expensive each request is.Usable when request cost is uniform; misleading the moment input size varies.
Accelerator utilisationHow busy the device is — but a device can be 100% busy on a batch of one.Good for diagnosing waste, poor as a scaling trigger on its own.

Two more numbers decide the bill. Model loading time sets how badly a scale-up event hurts: a multi-gigabyte model that takes ninety seconds to load onto a device means autoscaling cannot respond to a spike, so you provision for the peak or you keep warm capacity. And memory, not compute, is usually what limits how many models fit on one device — which is why serving several small models from one runtime, each with its own version, is often the cheapest arrangement anyone finds.

Shipping a version without betting on it

A new model version is not a bug fix; it is a change in behaviour across every request at once, and its offline score is a prediction about how it will behave rather than an observation. So the deployment question is not "is it better" but "how do we find out at a survivable cost". Four mechanisms answer that, and they compose.

  • Shadow

    Send real traffic to the new version and throw its answers away, comparing them offline. Zero user risk, and it catches latency, crashes and skew before anyone is affected. Costs double inference.

  • Canary

    Route a small share of traffic to the new version and watch the operational metrics. Answers "does it survive production", not "is it better" — that needs an experiment.

  • A/B experiment

    A randomised split measured on a business metric with a stated duration and sample size. The only mechanism that answers whether the model is actually better for users.

  • Pinned rollback

    The previous version stays loaded and reachable, and the live version is a config value. Rollback then takes seconds and does not require the training pipeline to still work.

The order that works is shadow, then canary, then experiment — each one cheaper to abort than the next. Two details matter more than they look. Serve both versions from one runtime that can hold several versions at once, so switching is a routing decision rather than a deployment. And record the model version on every prediction you log; without it, an investigation three weeks later cannot separate the two populations and the experiment you ran becomes unanalysable.

You cannot watch accuracy, so watch what you can

Here is the uncomfortable fact that shapes model monitoring. Accuracy needs labels, and labels arrive late — a churn label after the subscription period, a fraud label after the chargeback, a recommendation label after the return window. For weeks, the only honest statement about a live model is that you do not know how well it is doing. Building a dashboard around accuracy therefore produces a panel that is either empty or stale, and teams learn to ignore it.

What you can observe immediately is everything except the outcome, and it is more useful than it sounds. Three layers, in the order they catch problems:

  • Operational: latency percentiles, error rate, queue depth, saturation. Breaks first and is unambiguous when it does.
  • Inputs: the distribution of each feature against the training snapshot, plus the rate of nulls and unseen categories. This is where an upstream change announces itself.
  • Outputs: the distribution of predictions and, for classifiers, of the scores. A model whose positive rate doubles overnight is telling you something before any label arrives.
  • Business proxies that resolve quickly — click-through, acceptance rate, override rate by human reviewers — as the earliest signal correlated with the outcome you cannot yet measure.

Anti-patterns worth naming

  • Predictions logged without the model version, the feature vector and a timestamp — which makes every later question unanswerable.
  • An online service built for inputs that were known the night before, paying for p99 that nobody needed.
  • Preprocessing reimplemented inside the service rather than shared with the training pipeline — skew by construction.
  • A rollback plan that means retraining, since the previous artifact was overwritten and the data has moved on.
  • A drift alert with no owner and no defined action, which is disabled within a month of its first seasonal false positive.

When to use it

Use it when

  • Predictions whose input only exists at request time — a query, a basket, an uploaded image — where precomputing is not available.
  • A model whose behaviour must be changeable without a deployment, using a version pointer and a rollback that takes seconds.
  • Several models or several versions sharing hardware, where one runtime holding them all is cheaper than a service each.
  • Anywhere the prediction feeds a decision that has to be explained later, since serving is where the log is written.

Avoid it when

  • A prediction target that is known in advance and changes slowly — score it on a schedule and read a row instead.
  • A dedicated serving platform for one small model that a library call inside the existing service handles at a fraction of the cost.
  • Real-time serving adopted because it sounds current, when nobody has written down the latency budget it is supposed to meet.
  • Building it before the monitoring: a model you cannot observe in production is a model you cannot safely change.

Found this useful?

Share it with someone who is working on the same problem.