Tips for Application Performance (2015)
nginx.com
nginx.com
1. Moved from a shared hosting server to my own custom server. This alone my latencies went from 20s (sleep wakeup latencies) or 5s (woke response latencies) down to about .5s-1s.
2. Use Streaming HTTP response to immediately send HTTP header & initial HTML data before database is even accessed
3. Reduce the number of database lookups with select_related()/prefetch_related(). Each cached page fragment should only have one SQL query at most.
4. Don't even bother with Django ORM and use Postgres prepared SQL statements directly.
5. Use database materialized view.
6. GZIP cached data & serve GZIPed response (has side benefit of effectively making cache 10x bigger)
7. Move from NGINX to H2O web server for HTTP/2 (awesome little server that does http/2 cache-aware server push. See https://h2o.examp1e.net)
8. Build a simple Javascript single-page app framework
9. Use Postgres JSON data types for API calls
10. Run a separate python logging process outside of Django response.
11. Further optimizations to speed up HTML (cut down repaints), CSS (GPU animations), JS, DNS, and TLS/SSL.
It's amazing the amount of crap & inefficiencies that are in web services. A CPU can do trillions of calculations a second.. why should a web site take trillions of calculations to send a single page?
A $5 Linode and stock Django should be able to get you the 500-1000ms.
Also, I'm not sure how you're measuring 250 us, but I doubt it's meaningful number, because in practice either of these two numbers will be greater than that:
1) The network latency to send as single packet from the user to your server. This is usually more like 10 ms, or 40x what you're quoting.
Great link I saw on lobste.rs -- a website in a SINGLE PACKET:
https://github.com/diracdeltas/FastestWebsiteEver
2) The time it takes the browser to render the page (e.g. loaded from the memory of a local server). Static HTML might render in 250 microseconds, but I doubt that anything with JavaScript or even CSS will.
In other words, I highly doubt you have 250 us end-user latency; it's basically impossible with the web and "normal conditions". You can choose to measure it in some weird way, but it doesn't reflect what your users are experiencing.
EDIT: I guess if you throw out the connection time, 250 us possible. But I don't think it's meaningful to throw out the connection time -- you at least have to average it over many requests.
I'm measuring response times at the server, with no network latencies. My optimization process involved reducing TTFB at first and then complete full page generation in later steps.
A lot of these points jive with my experience, especially #3. Django makes it too easy to accidentally create a view which makes 100s if not 1000s of db calls for no good reason.
Only for the default settings, which for brotli is the maximum compression ratio and for nginx isn't, but brotli seems to win in general for comparable compression times or sizes: https://certsimple.com/blog/nginx-brotli
Anyway, compression times don't matter if you're compressing things up ahead. E.g. nginx can serve static files compressed with gzip_static / brotli_static. It's true that you might have to serve both, but disk is cheap and improving latency and response time is hard, so I have no problem with that tradeoff. Or you could just have the zlib encode on the fly for old IE, if space is truly a concern.
I guess your experience with it has been positive?
> There are two JSON data types: json and jsonb. They accept almost identical sets of values as input. The major practical difference is one of efficiency. The json data type stores an exact copy of the input text, which processing functions must reparse on each execution; while jsonb data is stored in a decomposed binary format that makes it slightly slower to input due to added conversion overhead, but significantly faster to process, since no reparsing is needed. jsonb also supports indexing, which can be a significant advantage.[1]
[1] https://www.postgresql.org/docs/9.4/static/datatype-json.htm...
SELECT row_to_json(t)::TEXT ...Using materialized views cut down the 50ms page generation to about 10-15ms, by flattening queries into a flat table so there aren't joins.
Using prepared statements cut down 10-15ms page generation to 5ms. Each Postgres query has an entire setup process that involved about 7ms of overhead, and prepared statements cut all that overhead. You won't notice this overhead when your queries are 50-100ms each, but when your queries are only 10ms, it matters.
Getting rid of Gzip in the page generation by serving cached gzip cut down page generation times down to sub-millisecond range for cache hit fragments.