Benchmark: Passenger (mod_rails) vs Mongrel vs Thin
Since the initial release of the teaser screencast, a lot of people have asked about Passenger’s (a.k.a. mod_rails) performance compared to Mongrel and Thin. Here’s the benchmark that you’ve all been waiting for.
In this benchmark, we compare the following software:
System specification:
- Intel Core 2 Duo, T5300 @ 1.73GHz
- 2 GB RAM
- Ubuntu Linux 7.10
- Apache 2.2.4 (Ubuntu package)
- Ruby 1.8.6 (Ubuntu package)
Test applications:
- Typo 5.0.3. This is an out-of-the-box setup with only a hello world post. Uses semi-static caching.
- Petstore revision 360, an application that the JRuby guys use for benchmarking. At first it wouldn’t start in Passenger because of a bug Petstore: they forgot to add
require 'forwardable'in app/models/cart_items.rb. I added this line myself. - Eldorado 0.9.1. This is an out-of-the-box setup with only a single forum and a single post added.
Typo and Eldorado both use SQLite 3. Petstore uses MySQL, because for some reason Petstore performs very badly when SQLite 3 is used.
People have also recommended Lovd By Less as test application, but I couldn’t get it working (database setup failed).
The applications, when served by Passenger, are setup at the following test domains:
- http://typo.test/
- http://petstore.test/
- http://eldorado.test/
Both the Mongrel cluster and the Thin cluster are setup at http://mongrel_cluster.test/. (Only either Mongrel or Thin is running during the benchmark, not both at the same time.)
And of course, we always use the production environment.
Backend configuration:
- Default Apache configuration, as provided by Ubuntu.
- Passenger: RailsMaxPoolSize is set to 10. This means that Passenger will never spawn more than 10 Rails application instances. Note that Passenger is compiled without optimizations, so if you install Passenger yourself and benchmark it, you might find that Passenger is slightly faster.
- Mongrel cluster: a cluster of 10 servers for each test application, behind mod_proxy_balancer, using default settings:
ProxyPass / balancer://mongrel_cluster/ ProxyPassReverse / balancer://mongrel_cluster/ <Proxy balancer://mongrel_cluster> BalancerMember http://127.0.0.1:3000 BalancerMember http://127.0.0.1:3001 BalancerMember http://127.0.0.1:3002 BalancerMember http://127.0.0.1:3003 BalancerMember http://127.0.0.1:3004 BalancerMember http://127.0.0.1:3005 BalancerMember http://127.0.0.1:3006 BalancerMember http://127.0.0.1:3007 BalancerMember http://127.0.0.1:3008 BalancerMember http://127.0.0.1:3009 </Proxy>
- Thin cluster: same configuration as Mongrel cluster.
Benchmark: Mongrel clusters
First we benchmark Typo served by Mongrel clusters. Because the actual application code is loaded by Mongrel during the first request, we do not want to count the first request’s time. So we run the following command to “warm up” the Mongrels, and discard its result:
ab -n 1000 -c 100 http://mongrel_cluster.test/
Next, we test 10,000 requests with 100 concurrent users:
ab -n 10000 -c 100 http://mongrel_cluster.test/
Result:
Concurrency Level: 100 Time taken for tests: 32.628480 seconds Complete requests: 10000 Failed requests: 0 Write errors: 0 Total transferred: 54890000 bytes HTML transferred: 51480000 bytes Requests per second: 306.48 [#/sec] (mean) Time per request: 326.285 [ms] (mean) Time per request: 3.263 [ms] (mean, across all concurrent requests) Transfer rate: 1642.83 [Kbytes/sec] received
We repeat these steps Petstore and Eldorado.
Petstore:
(for Petstore we benchmark the URI “/shop/viewCategory.shtml?category=DOGS” because it’s written in the INSTALL file)
Concurrency Level: 100 Time taken for tests: 109.786026 seconds Complete requests: 10000 Failed requests: 0 Write errors: 0 Total transferred: 40750000 bytes HTML transferred: 37680000 bytes Requests per second: 91.09 [#/sec] (mean) Time per request: 1097.860 [ms] (mean) Time per request: 10.979 [ms] (mean, across all concurrent requests) Transfer rate: 362.47 [Kbytes/sec] received
Eldorado:
Concurrency Level: 100 Time taken for tests: 70.889388 seconds Complete requests: 10000 Failed requests: 0 Write errors: 0 Total transferred: 41130000 bytes HTML transferred: 35530000 bytes Requests per second: 141.06 [#/sec] (mean) Time per request: 708.894 [ms] (mean) Time per request: 7.089 [ms] (mean, across all concurrent requests) Transfer rate: 566.60 [Kbytes/sec] received
Benchmark: Thin clusters
The benchmarking steps for Thin are the same.
Typo:
Concurrency Level: 100 Time taken for tests: 25.795470 seconds Complete requests: 10000 Failed requests: 0 Write errors: 0 Total transferred: 54910000 bytes HTML transferred: 51480000 bytes Requests per second: 387.66 [#/sec] (mean) Time per request: 257.955 [ms] (mean) Time per request: 2.580 [ms] (mean, across all concurrent requests) Transfer rate: 2078.78 [Kbytes/sec] received
Petstore:
(for Petstore we benchmark the URI “/shop/viewCategory.shtml?category=DOGS” because it’s written in the INSTALL file)
Concurrency Level: 100 Time taken for tests: 96.874308 seconds Complete requests: 10000 Failed requests: 42 (Connect: 0, Length: 42, Exceptions: 0) Write errors: 0 Non-2xx responses: 42 Total transferred: 40630350 bytes HTML transferred: 37547028 bytes Requests per second: 103.23 [#/sec] (mean) Time per request: 968.743 [ms] (mean) Time per request: 9.687 [ms] (mean, across all concurrent requests) Transfer rate: 409.58 [Kbytes/sec] received
Eldorado:
Concurrency Level: 100 Time taken for tests: 71.311031 seconds Complete requests: 10000 Failed requests: 5 (Connect: 0, Length: 5, Exceptions: 0) Write errors: 0 Non-2xx responses: 5 Total transferred: 41137070 bytes HTML transferred: 35518568 bytes Requests per second: 140.23 [#/sec] (mean) Time per request: 713.110 [ms] (mean) Time per request: 7.131 [ms] (mean, across all concurrent requests) Transfer rate: 563.34 [Kbytes/sec] received
Benchmark: Passenger
The benchmarking steps for Passenger are the same.
Typo:
Concurrency Level: 100 Time taken for tests: 25.131980 seconds Complete requests: 10000 Failed requests: 0 Write errors: 0 Total transferred: 55200000 bytes HTML transferred: 51260000 bytes Requests per second: 397.90 [#/sec] (mean) Time per request: 251.320 [ms] (mean) Time per request: 2.513 [ms] (mean, across all concurrent requests) Transfer rate: 2144.92 [Kbytes/sec] received
Petstore:
(for Petstore we benchmark the URI “/shop/viewCategory.shtml?category=DOGS” because it’s written in the INSTALL file)
Concurrency Level: 100 Time taken for tests: 104.428742 seconds Complete requests: 10000 Failed requests: 0 Write errors: 0 Total transferred: 40300000 bytes HTML transferred: 36700000 bytes Requests per second: 95.76 [#/sec] (mean) Time per request: 1044.287 [ms] (mean) Time per request: 10.443 [ms] (mean, across all concurrent requests) Transfer rate: 376.86 [Kbytes/sec] received
Eldorado:
Concurrency Level: 100 Time taken for tests: 72.587018 seconds Complete requests: 10000 Failed requests: 0 Write errors: 0 Total transferred: 41660000 bytes HTML transferred: 35530000 bytes Requests per second: 137.77 [#/sec] (mean) Time per request: 725.870 [ms] (mean) Time per request: 7.259 [ms] (mean, across all concurrent requests) Transfer rate: 560.47 [Kbytes/sec] received
Summary
Here’s a nice summary of the requests per second, in a table and in a graph:
| Typo | Petstore | Eldorado | |
|---|---|---|---|
| Mongrel | 306.48 | 91.09 | 141.06 |
| Thin | 387.66 | 103.23 | 140.23 |
| Passenger | 397.90 | 95.60 | 139.27 |

Conclusion
I was pleasently surprised by the results. During Passenger’s development, our performance baseline was Mongrel behind mod_proxy. According to earlier (simple and naive) tests, Passenger performed similar to Mongrel. I expected Thin to be a bit faster than Passenger. These new benchmarks however suggest Passenger is faster than Mongrel, and is on par with Thin.
But you know what they say: there are lies, damn lies, and statistics. Your hardware and software is different, so if you benchmark it yourself, you’re likely to get (slightly?) different results.






