Skip to content

Expert · Performance and Node.js · topic 52 of 61 · 30 min

Performance thinking and Big-O

Explanation

Big-O notation describes how the work a piece of code does grows as its input grows: O(1) stays the same, O(n) grows in step, O(n²) explodes. Replacing an O(n²) nested search with an O(n) Set lookup can turn minutes into milliseconds. But always measure first: most code is fast enough, and guessing wastes time.

Why think about growth?

Code that is instant with 100 orders can freeze with 100,000. What matters is not the time for one test run on your laptop, but how the time GROWS when the data grows. Big-O notation is a simple way to describe that growth, ignoring small details like the speed of your computer.

Big-ONameExample10,000 items ≈ steps
O(1)constantarr[5], map.get(key), set.has(x)1
O(log n)logarithmicbinary search in a sorted array14
O(n)linearone loop, includes, find, sum10,000
O(n log n)linearithmicsorting with arr.sort~140,000
O(n²)quadratica loop inside a loop over the same data100,000,000

The most common slow pattern

A loop that calls includes, indexOf or find on another big array inside it is O(n × m). Converting the inner array to a Set or Map first makes each lookup O(1), so the whole thing becomes O(n + m).

javascript
// O(n × m): for each order, scan all blocked customers
const blocked = customers.filter((c) => c.blocked).map((c) => c.id);
const ok = orders.filter((o) => !blocked.includes(o.customerId));

// O(n + m): build a Set once, then each check is instant
const blockedSet = new Set(blocked);
const ok2 = orders.filter((o) => !blockedSet.has(o.customerId));
With 100,000 orders and 10,000 blocked customers, the first version does up to 1,000,000,000 comparisons; the second about 110,000.

Measure, do not guess

javascript
console.time('dedupe');
const unique = [...new Set(bigList)];
console.timeEnd('dedupe');               // dedupe: 3.2 ms

const t0 = performance.now();            // high-resolution timer (ms with decimals)
heavyWork();
console.log(`took ${(performance.now() - t0).toFixed(1)} ms`);

Measure with realistic data sizes, run more than once (the first run includes warm-up while the engine optimises your code), and use the Performance panel in browser DevTools or node --cpu-prof to see where time really goes.

Cheap wins

  • Do work once, outside loops: build Sets/Maps, compile regexes, create Intl formatters before the loop.
  • Avoid repeated string building in huge loops; collect parts in an array and join once.
  • Stop early: use find/some instead of filter(...)[0] or filter(...).length > 0.
  • Avoid unnecessary copies of big arrays in hot loops.
  • Paginate: do not load or render 50,000 rows at once.
Careful: "Premature optimisation is the root of all evil" (Donald Knuth). Do not make code harder to read for a speed-up nobody needs. First make it correct and clear, then measure, then optimise only the part the measurement points to.
Think of it like this: Big-O is like comparing ways to find a name: reading a phone book page by page is O(n); opening it in the middle and halving each time is O(log n); and asking a receptionist who knows everyone by heart is O(1). With a small list, any method works; with all of Mumbai's numbers, the method decides whether you finish today.
Tip: Space matters too: building a Set of a million ids uses memory. Big-O also describes space; usually you trade a little memory for a lot of speed, which is fine.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Performance thinking and Big-O · 1/6

Big-O

In this lesson you will learn to think about how code slows down as data grows.

Prefer a video by a teacher? Find free videos on “big o notation javascript explained time complexity” on YouTube ↗

In real life: A slow report that took 40 minutes

A logistics firm's nightly report matched 200,000 shipments against 50,000 invoices with find inside a loop. It took 40 minutes and sometimes timed out. Building a Map of invoices by shipment id first made the matching take under a second.

javascript
const invoices = [{ shipId: 'S1', amt: 900 }, { shipId: 'S2', amt: 450 }];
const shipments = [{ id: 'S2' }, { id: 'S1' }, { id: 'S3' }];
const byShip = new Map(invoices.map((inv) => [inv.shipId, inv]));
console.log(shipments.map((s) => byShip.get(s.id)?.amt ?? 'missing')); // [450, 900, 'missing']

Practice (0/2 done)

Task 1 · Code · Javascript

hasDuplicate(list) currently compares every pair (O(n²)). Rewrite it in O(n) using a Set. The test uses a list of 60,000 numbers.

Task 2 · Code · Javascript

Write missingInvoices(shipments, invoices) that returns the ids of shipments with no invoice (invoice.shipId === shipment.id), in shipment order, in O(n + m).

Quick quiz

Question 1

1.What is the Big-O of set.has(x)?

Question 2

2.A loop over n items that calls array.includes on another n items is…

Question 3

3.What should you do before optimising?

Question 4

4.Binary search on 1,000,000 sorted items needs about how many steps?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What is the Big-O of set.has(x)?

Answer: O(1) on average

Sets use hashing for near-constant-time lookups.

A loop over n items that calls array.includes on another n items is…

Answer: O(n²)

includes is itself a loop, so the total is n × n.

What should you do before optimising?

Answer: Measure where time is really spent

Measurement shows the real bottleneck.

Binary search on 1,000,000 sorted items needs about how many steps?

Answer: 20

log2 of a million is about 20.

About how many steps does binary search need for 1,000,000 sorted items?Related

Answer: About 20

Halving 1,000,000 about 20 times gets down to 1, because 2 to the power 20 is about a million.

From Big-O and searching

Code takes 4n² + 10n + 7 steps. What is its Big-O?Related

Answer: O(n²)

Drop the constant 4 and the smaller terms 10n and 7.

From Big-O time complexity

Why test your app with "Slow 4G" and CPU slowdown in DevTools?Related

Answer: Many real users are on budget phones and patchy mobile networks

Your laptop on Wi-Fi hides problems that users on cheap phones and mobile data feel every day.

From Making the Notes app fast

Why prefer "".join(parts) over s += part in a long loop?Related

Answer: s += part may copy the whole growing string each time, O(n²) in total

Strings are immutable, so += can create a new string every time; join builds the result once.

From The real cost of Python and JavaScript built-ins

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 4 more topics in “Performance and Node.js” before its exam: Memory and garbage collection, Node.js basics, Files and a tiny server in Node, Real project tooling.
  2. Module 11 exam: Performance and Node.js · 12 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Interview preparation and career. 61 topics left in the course before the final exam.

← Previous: Test doubles and testable design