I did the previous version of this 3 years ago in 2021. In that benchmark I compared Laravel Octane (with Swoole), Fastify & Go Fiber – with Go Fiber coming out a clear winner as far as performance was concerned.
Three years is a significant amount of time period. The software development field is ever changing and the landscape has evolved significantly – we’ve seen the rise of Bun & Deno, maturing of ReactPHP, emergence of FrankenPHP server and interesting alternatives like HyperExpress in the Node.js ecosystem. So I decided it was time for a fresh round of benchmarks, this time diving deeper with more variations, different optimization strategies and some new contenders. What surprised me most wasn’t just the raw numbers but how different implementation choices within the same stack could lead to drastically different results.
Let’s see what do we have here now.
The Environment 🛠️
The environment in which all these API endpoints were run was a VM provisioned with 8 GB of RAM and 2 CPU cores, running on my Apple Macbook Pro with Intel i7 processor (quad core) and 32 GB RAM. Below are the specifics with software versions.
- Ubuntu 22.04 LTS
- Node.js 20.17.0
- PHP 8.3.1
- Go 1.23.2
- PostgreSQL 15.5
The Setup 🛠️
I created a simple but realistic test scenario:
- Two database tables:
posts
andcomments
(classic blog-style setup). These tables were seeded with around 10,000 records inposts
table and around 50,000 records incomments
table. - API endpoint that fetches a single post with all its comments and returns the data as JSON
- Load testing with the
hey
tool:- 1000 concurrent connections
- 30-second test duration
- HTTP/2
- Same PostgreSQL database for all tests
Simple enough, right? Well, the results tell quite a story!
The Results: Starting from the Bottom 📊
Let’s walk through our findings, starting with the more modest performers and working our way up to the speed demons.
Standard PHP with Laravel
Starting at the baseline, standard Laravel with PHP-FPM managed about 41 requests per second. Before you raise your eyebrows, remember that Laravel is optimized for developer productivity rather than raw performance. But could we do better with PHP?
Enter Laravel Octane with FrankenPHP
Things get interesting when we move to Laravel Octane and requests are served by FrankenPHP – the purpose built server for PHP applications. Here’s how different optimizations affected performance:
- With Eloquent ORM (Base Implementation):
- Without regex routing: 208 req/s
- With regex routing: 203 req/s
- Already a 5x improvement over standard Laravel!
- Average response time: ~4.9 seconds
- With DB Query Builder:
- Without regex routing: 271 req/s
- With regex routing: 270 req/s
- ~30% improvement over base implementation
- Average response time: ~3.6 seconds
- With DB Query Builder + Caching using Redis:
- Without regex routing: 322 req/s
- With regex routing: 336 req/s
- ~62% improvement over base implementation
- Average response time: ~2.9 seconds
Key observations from Laravel Octane testing:
- Removing the ORM provides a significant performance boost.
- Adding caching further improves throughput.
- Regex vs Non-Regex routing shows minimal impact.
- Response times improve dramatically with optimizations.
Moving to async PHP with ReactPHP
Taking a completely different approach with ReactPHP showed even more impressive results:
- Without connection pooling: 427 req/s
- With connection pooling: 826 req/s
That’s a massive jump! Nearly 20x faster than standard Laravel and 2.5x faster than the best Laravel Octane configuration.
To my knowledge, ReactPHP does not come with a way to implement DB connection pooling. That means a new connection is made for every request that wants to interact with DB. I wrote a basic connection pooling class for this test.
The JavaScript Ecosystem
Next up the performance ladder is Bun, the new kid on the block. Its a package manager & runtime environment for Javascript just like Node.js. I ran the API endpoint made using Fastify on Bun & here’s the result:
- Fastify on Bun: 474 req/s
Though its not breaking any records, thing to remember is that Bun is still young and evolving. But I was surprised by these numbers considering all I’ve read so far has been praise after praise of Bun extolling its performance gains over Node.js. Maybe I missed something here regarding setup or configuration – I’ll probably look into it further down the line.
Node.js, however, performed as was expected:
- Fastify: 1,164 req/s
- HyperExpress: 1,560 req/s
HyperExpress outperformed Fastify here & the big reason for that is µWebSockets.js – the web server which HyperExpress relies on. µWebSockets.js is written in C++.
I would’ve liked to try HyperExpress via Bun as well but µWebSockets.js is not compatible with Bun yet.
The Speed Champion: Go
Now we’re entering the speed demon territory. Go implementations dominated the performance charts with different configurations:
- Go + GORM + FastHTTP with regex routing:
- 1,662 req/s
- Uses an ORM for database operations
- FastHTTP for HTTP handling
- Regex-based routing
- Go +
pgx
+ net/http with regex routing:- 2,591 req/s
- Direct database driver
- Standard HTTP library
- Regex-based routing
- Go +
pgx
+ net/http without regex routing:- 4,198 req/s
- Direct database driver
- Standard HTTP library
- Simple routing
- Go +
pgx
+ FastHTTP without regex routing:- 4,265 req/s
- Direct database driver
- FastHTTP for HTTP handling
- Simple routing
Key Learnings 🎓
- Impact of ORMs:
- Go: Moving from GORM to
pgx
improved performance by ~157% - Laravel: Switching from Eloquent to Query Builder improved performance by ~30%
- Lesson: ORMs are convenient but come with a performance cost
- Go: Moving from GORM to
- Connection Pooling Matters:
- Nearly doubled ReactPHP’s performance
- Critical for sustained high performance
- Essential for any production deployment
- HTTP Libraries Make a significant Difference:
- FastHTTP consistently outperformed standard net/http in Go
- HyperExpress showed significant advantages over standard Fastify in Node.js
- Routing Complexity Impact:
- Most noticeable in Go (~38% decrease with regex routing)
- Minimal impact in Laravel Octane
- Consider simplifying routes on critical paths
Practical Takeaways 🎯
- If Maximum Performance is Your Goal:
- Go +
pgx
+ FastHTTP is your best bet. - Avoid ORMs on critical paths.
- Keep routing simple.
- Implement connection pooling (where applicable).
- Go +
- For PHP applications:
- Consider ReactPHP for high-concurrency APIs.
- If sticking with Laravel:
- Use Laravel Octane. FrankenPHP is a very good choice. I did not get around to testing Laravel Octane with Swoole – it will probably give slightly better performance.
- Consider removing Eloquent from hot paths.
- Implement caching where possible.
- Use connection pooling where possible. Laravel Octane has built in connection pooling.
- Node.js applications:
- HyperExpress shows impressive performance.
- Standard Fastify remains a solid choice.
- Both offer good performance for most use cases.
The Bottom Line 🎬
Key thing to remember – performance isn’t everything. Each stack has its strengths:
- Go excels in raw performance.
- Node.js offers a great balance of performance and developer experience.
- Modern PHP (ReactPHP/Laravel Octane) can be surprisingly fast.
- Bun shows promise for future performance gains.
Some of the things that should be considered before deciding on a tech stack:
- Team expertise
- Development speed requirements
- Maintenance considerations
- Actual performance needs
Your choice of technology often depends more on your team’s expertise and existing infrastructure than raw performance numbers. While these benchmarks are insightful, most businesses will (& should) stick to their current stack unless there’s a compelling reason to switch. Moving away from your established stack, with its accumulated knowledge & tooling, can lead to fragmentation and increased complexity. The exception? Large engineering organisations that have the resources and scale to effectively maintain multiple specialized stacks for different use cases.
What do you think? Have you ever been in a situation where you had to decide which tech stack to use (other than the fact that you are familiar with it)? Share your thoughts, comments and/or feedback in comments! 🙂