A High-Performance Web Project With Ruby: Is it Possible?

17 July 2018

In this modern world, full of innovation and high-tech products, you can hardly win the race with a slow and low-performing invention. Especially, when it comes to web development where each second of loading determines user experience and quality of interaction with your app. People value high speed and convenience, so these characteristics should be your primary priorities when implementing a web application. To achieve this goal, first of all, you have to choose a strong technology stack with a capacity to build a powerful software. Some technology like NodeJS and Python are the undoubted leaders on these parameters, but what if you want to involve a well-forgotten Ruby on Rails due to your sincere devotion to this guest from the last century?

Let’s figure out together what perspectives of developing a high-performance web app you have when using this fairly traditional solution.

High-Performed project with Ruby

Ruby Through The Prism Of Modern Benchmarks

In this article, the main aspect of our analysis is a speed of the application. However, first of all, let’s define what the term “speed” really means in the web dev dimension.

The main characteristics determining a speed of any web app are latency and throughput. Latency is an amount of time required by server to respond to a single request, while throughput is a parameter that is responsible for a number of requests served at the same time, i.e. how much responses a server can give per one second. Throughput is generally regulated by CPU and parallelism, and depends on a number of CPU cycles responding to a web request, and efficiency of saturation of all the CPU cores of the host machine. In our case, latency has the leading role since it is inversely proportional to throughput. Consequently, if we halve the latency of our web application, we double its maximum throughput. In addition, latency has impact on the end-user experience. For example, a 1000 millisecond response time manifests as an extra 1000 milliseconds the user must spend waiting for the web page to load.

So now you should have a clear idea of the main components of a category “speed”, and it’s time to go to the practical part. We’ll compare Ruby on Rails with other frameworks and programming languages in a series of multidirectional benchmarks conducted by Jeremy Evans, a sequel author at TechEmpower. Prepare popcorn, we begin.

Test 1. Community-Contributed Test Implementation

Jeremy Evans jointly with a community of TechEmpower conducted an exciting test of performance of different Ruby frameworks. Here you can enjoy the results. As you can see, Sequel has 91,082 performance rate that is 30 times higher than Rails’ 3,084 indicator. This means Sequel generates 30 times more responses per second than Rails, so that its throughput is better. However, take a look at the section above and remember that for web dev, latency is the much more important. Therefore, experiment №1 doesn’t provide us with killing arguments against Ruby on Rails inferiority. Anyway, good start, and let’s continue.

Test 2. Benchmark Trip-Ups

Now let’s assess these TechEmpower web framework benchmarks by parameters of latency and maximum throughput across six synthetic benches. TechEmpower employed pretty fat servers with 4 CPUs having 10 cores and 20 threads. In total, they worked with 40 cores and 80 hyperthreads, and 528 GB of RAM.

To conduct the multiple-queries benchmark, they sequentially executed 20 queries against a SQL database and then returned the result. We see in Round 14, the common Rails setup (puma-mri-rails) generates 531 requests per second, while Roda, an extremely lightweight Ruby web framework, in combination with Sequel, generates about 7000 requests per second.

Finally, we may ask the same question as after the first experiment. Is Rails really more than 10 times slower than Roda and Sequel? Is the indicator of 531 requests per second maximum we can get using Ruby on Rails?
The fact is TechEmpower’s Rails setup is humiliated in comparison with their Roda setup. Their configurations of Puma server allow to run only 8 processes. At the same time, auto-tuned Roda has the capacity to run around 100 processes. In this way, Rails benchmark is using, at best case scenario, about 15-20% of the available hyperthreads, while the Roda benchmark involves all of them. To remedy this sad situation, we just need to fix a pull request in open-sourced TechEmpower, and get better results for Round 15.

Test 3. Average Request Latency

In this test, TechEmpower measures a request latency that is a more fundamental parameter, because things like global VM locks and other concurrency features typically are not of high importance when processing a single request. On the multiple-query database test, for Puma and Rails it took 129 milliseconds. The Roda/Sequel/Puma stack ended up at 31.3 milliseconds.

To summarize this experiment, we’ll repeat that the settings of Puma for Rails on TechEmpower are really crippled compared to the Roda’s ones. This gives us a reason to believe that Rails simply adds 100 milliseconds of latency to the average web app response over a microframework or other competing platform.

Test 4. Ruby on Rails is doing more staff

The main peculiarity of Ruby on Rails is that it is a feature-rich framework, i.e. it has a lot of code and executes more operations on each request than other frameworks. This is not good or bad; it just means more actions you take, slower benchmark results you get, and vice versa. TechEmpower demonstrated to us a table of scores where Ruby on Rails takes one of the last places, however, without revealing what lies behind this. We see only impossible-to-skim array of tags.

To complete this picture and get truly valid results, it is enough to create a new app in Rails and analyze the middleware stack (rake middleware). As a result, you’ll see that Rails do a lot of work that a good web app should do but not all of other frameworks perform this task for you even by default.