On Pagedraw’s Performance Engineering

tl;dr: don’t use performance as an unsubstantiated excuse to write convoluted code

Jared Pochtar
3 min readFeb 28, 2019

A lot of people on https://news.ycombinator.com/item?id=19273403 responded to what we wrote about performance:

Performance is never a justification for anything if you haven’t measured it

I stand by what we wrote 100%, but to add some color:

In my experience, people often write code in convoluted ways so it’s not slow. The problem is, when they do this, they’re often wrong about what’s slow. There’s lots of superstition about performance, and many people (myself included) have bad intuition about performance. There’s a real cost to this premature optimization: much of the advice encourages convoluted code.

We hated bad code quality, so I like to say

What would you do if you weren’t afraid (of performance regressions)?

We actually cared about good performance, so we had to spend time on proper performance engineering. It turns out that works better than following often-wrong “best practice” superstitions.

To do performance engineering, you have to constantly measure the performance of your code.

There’s 2 parts to this idea.

1. Most things are fast enough that optimizing them is not worth making the code less readable, and is a waste of time.

2. Measuring, rewriting, and repeating is the right way to do performance anyway, full stop. There’s lots of superstition around performance, which is absurd because it’s extraordinarily measurable.

For point 1:

We goalled on a clear and flexible style, which helped us move incredibly fast with very few people. See https://norvig.com/spell-correct.html for an example of a golden piece of code in this style. Rewriting for performance takes moves us away from that goal.

Most of the code we wrote was fast enough (<2ms) that making it faster wouldn’t be noticeable, so it would definitely be a waste of time to optimize. Making code that’s 0.1% of your runtime 100x faster only makes your latency <0.1% lower.

It’s not because we’re brilliant coders: we just realized that most of the code we wrote ran on arrays with few enough elements that even O(n²) algos could run in <1ms. Some of the code we wrote ran on a server, where network was the dominant factor, and we could always just scale up our servers with money. Some of the code ran on click, where it could be as slow as 5 seconds without the user really caring.

For point 2:

There’s no such thing as “fast” code and “slow” code — you can put in the work to optimize any piece of code, and make it incrementally faster.

Because you can put in work to optimize any particular code, we either budgeted hours of engineering time for performance, or set performance goals. In either case, you want to spend your time optimizing the code that’s the slowest, so you get the most “bang for your buck.”

Most people have terrible intuitions about performance. We certainly did — if we’d prematurely optimized the things we’d thought were going to be slow, we’d have wasted a ton of time

I felt there were lots of superstitions about performance that were just wrong. For example, considering big-O time complexity was often wrong for us, because our n was usually pretty low, so we were dominated by constant factors.

Performance is incredibly measurable. You should always measure, rewrite, and repeat until you hit your perf budget. Otherwise you’re just shooting in the dark.

Because we goalled on clean code, tearing and replacing whole subsystems for performance was relatively easy.

--

--