Premature Optimization and the Rise of Nginx Module Development
By Evan Miller
February 5, 2011
Once upon a time (2006), I was making a website, and I wanted users to be able to choose custom colors for their profile. The profiles had to have round corners like this one:
The goal: generate this GIF quickly.
Obviously, it wasn’t possible to generate all possible round corners in advance. With 16 million possible foreground colors and 16 million possible background colors, that would be about 300 trillion possible images to store. (This was in the days before transparent PNGs could reliably be used on the web, so I couldn’t skip the background color and just use the PNG alpha channel… even if I could, it would still mean “only” 16 million images to store.)
I first tried to generate the round corners on the fly, using Ruby and Image Magick. The drawing commands were simple enough: paint a background, draw a circle. Time per request: 150 milliseconds.
Of course, 150 milliseconds is eons in server time, so I decided I needed to make this program run faster. I had been told that Ruby is “slow”, so then I rewrote the program in C. Time per request: 30 milliseconds.
Not bad. Cache the results and I’d have myself a respectable round-corner server.
But it occurred to me that it was dumb to execute a drawing command for every web request. I was drawing the same circle over and over, and all I really wanted to do was rewrite its color palette. I looked up the GIF specification and figured out I could draw a single black and white template on server startup, then just mess with the palette on each request to deliver a circle with the requested colors. If it worked, this method would be even faster than delivering static images from disk, and require virtually no disk space or RAM for caching.
And it did work. Time per request: 75 micro-seconds, about 2,000 times faster than the original. I had solved the round-corner image problem for the rest of eternity.
Did I really need round corners delivered in 75 micro-seconds? No way. The website I was working on didn’t go anywhere, and even if it did, I don’t think round-corner delivery would have been a major cost sinkhole.
But in the course of re-writing the program in C, I decided to figure out how to turn my round-corner code into an add-on for an obscure Russian web server known as Nginx. No one had written an Nginx add-on before, so I spent a weekend turning those notes into Emiller’s Guide To Nginx Module Development.
Word got out, and a lot of developers read my guide. Over the past several years, they’ve used it to write dozens of add-ons extending the functionality of Nginx. (I am happy to report that most of these add-ons perform functions much more needful than the delivery of round corners.)
I’m not sure how much indirect credit I can take for Nginx’s popularity today (once obscure, the server now delivers about 8% of web traffic). But I like to think that its rise in the English-speaking world was due in part to my quest to make a round-corner generator that was prematurely awesome.
You’re reading evanmiller.org, a random collection of math, tech, and musings. If you liked this you might also enjoy:
Want to look for statistical patterns in your MySQL, PostgreSQL, or SQLite database? My desktop statistics software Wizard can help you analyze more data in less time and communicate discoveries visually without spending days struggling with pointless command syntax. Check it out!
Statistics the Mac way