The Runtime Theory
Algorithms

Big-O Measures Scaling, Not Speed

O(n) says nothing about how fast your code is today — it predicts how it behaves as the input grows. What complexity analysis is really for, and when it misleads.

The Runtime Theory Team3 min read#complexity#performance#algorithms
On this page

Every job interview asks it, and almost everyone has the wrong mental model for why: "What's the complexity of that?" — as if the answer were a speed rating. It is not. Big-O is not about how fast your code runs; it is about how its cost changes as the input grows. The entire purpose is to make predictions about scale, and misreading it as a speed test explains a family of professional errors.

What the notation actually claims

The definition, in one sentence:

T(n) ∈ O(f(n)) means: for large n, the runtime T is bounded above by c·f(n) for some constant c.

Three clauses deserve their own attention:

  • For large n — the statement is explicitly about asymptotics; it is silent about n = 1000 or n = 100. Every benchmark of "n is small" exists outside the claim's universe.
  • up to a constant factor — two algorithms in the same class may differ by a constant that's enormous (that's what cache effects are: constant-factor, and decisive).
  • bounded by c·f(n) — it's an upper bound; an O(n²) algorithm can run in linear time on your actual data. The class is a ceiling, not a description of today.

So every invocation of big-O is a scaling prophecy, and the only valid question it answers is: what does this do to me as my input size multiplies by 10? If your input is fixed and small, the prophecy is about a future you don't have.

The four sentences that make it practical

The whole discipline compresses to four sentences:

text
O(1)      — cost is flat; the input never touches the hot path (hash lookup amortized)
O(log n)  — doubling the input adds a constant amount of work (binary search, B-tree)
O(n)      — the cost is proportional; doubling doubles it (a scan, a filter)
O(n log n) — the cost grows slightly superlinear; sorting is the famous case
O(n²) and up — the cost explodes; the function is only usable at fixed small n

The purely applied translation: if you're at O(n²) and data is heading for 10× growth, you have a scaling commitment — 100× cost — that no hardware purchase can keep up with. If you're at O(n) with a big constant, you have a speed problem that hardware can fix. They are different categories of problems and they deserve different responses.

Where big-O lies to you

1. Elements can be worse than the worst class. The guarantee is upper-bounded; your data may hit the exact case the algorithm is bad at. For quick sort, that's the most naive implementations with adversarial input — where O(n log n) average becomes O(n²) worst. Since comparison of classes says nothing about the constants, a "theoretically worse" algorithm repeatedly wins on real data: heapsort vs quicksort vs introsort is a constant-factor story within the same O(n log n) class.

2. Memory is a dimension that complexity notation forgets. An algorithm that is O(n) time but O(n²) memory is not free. Real systems are memory-bound long before they are CPU-bound; cards with 32 GB at 100 GB/s make the memory constant the actual boss.

3. Amortized claims hide rare spikes. Hash map inserts are O(1) amortized — the occasional rehash makes a single insert O(n). If your workload is latency-sensitive (p99 in a lock-free hot path), the variance matters more than the asymptotic class, and the "O(1)" label actively misleads.

4. The input might not be the input you think. O(n) for a string concat loop hides that the string is reallocated and copied in each iteration — sum of copies is O(n²) of the accumulated length. The class depends on which quantity you measure: an appending loop is O(n²) in output length despite "looking linear".

The workflow that never misuses it

A useful complexity habit is: measure first, classify second, and use the class to decide the next move:

  1. Benchmark the actual size — is n ever going to grow 10×? If no (fixed dataset, a bounded admin screen), the class is irrelevant and the constant is king.
  2. Classify the hot path — only the path your profiler says matters.
  3. Match the class to the growth — log-scale dashboards line up beautifully with complexity classes; the shape of a latency graph over time is your T(n) in production.
  4. Prefer real measurements for decisions — the class predicts; the profiler confirms.

The final, most useful reframe: complexity analysis exists because you cannot benchmark the future. You can only predict it. That's what big-O is for — and it's a prediction, not a verdict, exactly like every other engineering estimate.