TechEmpower Framework Benchmarks: Round 20
I wrote this comment based on pre-release benchmarks, based on Physical benchmarks run against Git commits from Jan 23 - but it should still largely apply to the final benchmarks:<p>It’s kind of frustrating to spend a few hours investigating preview results of the latest techempower benchmarks just to come to the conclusion that (a) truly high performance requires tuning your code to linux, to the network interface cards, and a deep understanding of what your code is doing and (b) right now at the top of the benchmarks, common high performance Rust code is mostly “unsafe” memory-wise, and the same is true of C++ for obvious reasons. After that:<p>* When looking at garbage collected languages with very minimal implementations (Jooby mostly), Java can come out on top but requires 4 Gigs of memory which is more than I would ever like to use.<p>* After Java comes C# but performance slows down the second you want to do anything less optimized, like use third-party libraries.<p>* Finally, we’ve Go, which in a surprising twist is basically neck and neck with some pure PHP server implementations, but once I include those, I might as well mention there’s a JS implementation that skips the Node.js async event loop and offers performance on par with C++ and Rust, but heavily uses C++ internally and is thus more “unsafe”.<p>What this tells me is kind of what I expected, implementation matters more than language… at this point, memory usage aside for Java, an efficient HTTP&#x2F;1.1 server can be implemented in basically any language and when tuned or stripped down tends to run faster than the commonly used web servers, like Nginx, Tomcat, Express, etc. Often this means writing plain text RAW HTTP to a socket and managing sockets efficiently.<p>Which brings us back to Go, though. Of all the implementations I’ve looked at so far, even the PHP one, only the Go benchmark is written like “standard Go” such that most people writing a service in Go will write something high performance without trying to make it high performance. Effectively, you don’t need to ensure all your dependencies use special optimized code routines to get something relatively optimized working quickly in Go with low memory overhead unlike Java.<p>I’m a bit shocked by this conclusion as I was really hoping that Rust’s high performance use cases would win out, as it’s true that Rust can get 3x faster than Go, but on the Rust side, both actix and ntex are too immature as neither’s hit 1.0 yet, while tokio has hit 1.0 but its server, warp, is slower than Go.<p>Irritating is that each of the techempower benchmarks use different implementations. For example, there’s a benchmark that’s supposed to measure 20 calls to Postgres, but one benchmark gets to the top by making only one long SQL statement that changes 20 rows. Another implementation uses pgx’s Batch functionality to send multiple queries in batches (a big timesaver, but not technically standard libpq), but then the standard Go variant doesn’t use Batching even when it could (which means we can’t compare custom implementations to generic Go ones fairly): <a href="https:&#x2F;&#x2F;github.com&#x2F;TechEmpower&#x2F;FrameworkBenchmarks&#x2F;search?l=Go&amp;q=batch" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;TechEmpower&#x2F;FrameworkBenchmarks&#x2F;search?l=...</a>