-
EC2onRails - 2 Small Instances or 1 High-CPU Medium Instance?
Posted on May 17th, 2009 7 commentsI recently watched the excellent RailsLab videos on tuning and optimizing Rails applications. One of the videos suggests that a great way to scale a Rails application is to separate the web and database components onto separate machines. We have been happily using Amazon’s EC2 system for about a year. Amazon’s recent introduction of the High-CPU Instance (High-CPU) made me curious how it would perform when compared against two Small Instances in a cluster (Cluster), since they would cost the same.
You will be happy to know that after hours of rigorous testing, I have identified a clear winner. Read on for the details.
For those of you that are not familiar with Amazon’s specifications for the two instance types:
Small Instance (default) High-CPU Medium Instance - 1.7 GB memory
- 1 EC2 Compute Unit (1 virtual core with 1 EC2 Compute Unit)
- 160 GB instance storage (150 GB plus 10 GB root partition)
- 32-bit platform
- I/O Performance: Moderate
- Price: $0.10 per instance hour
- 1.7 GB of memory
- 5 EC2 Compute Units (2 virtual cores with 2.5 EC2 Compute Units each)
- 350 GB of instance storage
- 32-bit platform
- I/O Performance: Moderate
- Price: $0.20 per instance hour
Following the RailsLab recommendation means that you place the web / application server and database on separate instances for best performance. I am sure that two small instances configured as below would outperform a single small instance. However, we wondered how the High-CPU instance would compare against two Small instances when configured like this:

Two Small instances vs. One High-CPU instance
Hypothesis:
Two Small instances will outperform one High-CPU instance in a standard Rails application.
Setup:
For these tests, I used 5 EC2 instances configured as shown here:

Testing setup
Great care is required when doing performance tests to ensure your results are legitimate. There are many factors that affect server performance and not accounting for any of these factors can make your results invalid. For example, in many web server performance tests that I read, the authors simply run Apache Bench (ab) against a couple of configurations then proclaim a winner. A couple of potential problems with this approach are 1) the results are not statistically testable and 2) something may have changed on the server between test runs.
In order to get valid results, we need to take multiple samples of the performance. With those samples we need to get both the mean and the standard deviation. When we have this information, we can perform a two-sample t-test to statistically prove that one configuration is better. Fortunately, the httperf tool provides the information that we need.
For these tests, each instance was configured exactly the same. Each instance was created using the EC2onRails code from my fork on Github. Both the Small Instance cluster (Cluster) and the High-CPU Instance (High-CPU) were running the same version of all code. They also had the exact same data in the database. The code used in these tests is from a live production site. All tests were performed at the same time on both configurations. Each test was then repeated on the other configuration to ensure the testing server was not biasing the results.
Details:
- Ubuntu 8.04
- Rails 2.3.2
- Nginx 0.6.36
- Passenger 2.2.2
- MySQL
- Memcached
Process: (all of these steps are performed on each testing server at the same time)
- Warm up each server using ab:
ab -n 100 -c 1 ec2-xxx-xxx-xxx-xxx.compute-1.amazonaws.com/ - Run this a second time to ensure the server is ready.
- Check that all requests were served successfully.
- Using an automated script:
- Send 5 requests using httperf to estimate the time required to get 45 samples.
- Get at least 45 samples using httperf.
- Run the test again using the other testing server.
- Verify that the two samples from different testing servers are not statistically different from one another.
Results:
No ActiveRecord:
The first set of tests was performed against a page that doesn’t access the database. This page is not cached and has numerous images, scripts, and partials. This should be a test of the instance’s ability to process Rails requests. Given the additional “Compute Units” available to the High-CPU instance, I expect it to win easily.
The results:
Small Instance (default) High-CPU Medium Instance Duration 233.15 291.26 Average (requests/sec.) 14.0 22.1 Std.Dev. 0.7 1.8 Samples 46 58 Max 15.4 24.5 Min 11.3 15.8 Avg. High 15.4 25.7 Avg. Low 12.6 18.5 2xx 3268 6428 Conclusion: The High-CPU instance is statistically proven to be faster at the 99% confidence level. The High-CPU instance is about 58% faster than the Cluster at serving standard Rails requests.
Moderate Database usage:
The next set of tests was performed against a page that has moderate database usage. This page accesses eight different models. The page is not cached and has several images, scripts, and partials. This should be a test of the instance’s ability to process Rails requests as well as handle the database load. With the additional memory and dedicated machine for the database, the Cluster might have a chance here.
The results:
Small Instance (default) High-CPU Medium Instance Duration 233.15 247.61 Average (requests/sec.) 6.7 11.3 Std.Dev. 0.4 0.5 Samples 52 49 Max 7.4 12.1 Min 5.4 9.6 Avg. High 7.5 12.3 Avg. Low 5.9 10.3 2xx 1760 2812 Conclusion: The High-CPU instance wins again by a large margin. The High-CPU instance is statistically proven to be faster at the 99% confidence level. I thought that the Cluster would perform better in this test. I was not expecting the High-CPU instance to perform 69% faster.
Heavy Database usage:
This set of tests was performed against a page that has heavy database usage. This page accesses numerous models. The page is not cached and has several images, scripts, and partials. This should be a test of the instance’s ability to process Rails requests as well as handle the database load. With the additional memory and dedicated machine for the database, the Cluster might have a chance here.
The results:
Small Instance (default) High-CPU Medium Instance Duration 252.18 268.19 Average (requests/sec.) 2.0 4.4 Std.Dev. 0.1 0.2 Samples 50 53 Max 2.2 4.4 Min 1.6 3.7 Avg. High 2.2 4.4 Avg. Low 1.8 3.6 2xx 494 1073 Conclusion: The High-CPU instance wins again by a large margin. The High-CPU instance is statistically proven to be faster at the 99% confidence level. I thought that the Cluster would perform better in this test. I was not expecting the High-CPU instance to perform 120% faster.
Cached Page performance:
This set of tests was performed against a page that was cached. It has several images, scripts, and partials. This should be a test of the instance’s ability to server pure web server (nginx) requests. Since nginx uses such small amounts of memory and CPU, both configurations might be similar.
The results:
Small Instance (default) High-CPU Medium Instance Duration 725.40 642.11 Average (requests/sec.) 93.0 350.4 Std.Dev. 18.5 46.4 Samples 144 128 Max 122.2 399.6 Min 39.9 180.4 Avg. High 130 443.2 Avg. Low 56.0 257.6 2xx 67500 224999 Conclusion: The High-CPU instance wins again by a very large margin. The High-CPU instance is statistically proven to be faster at the 99% confidence level. I thought that the Cluster would perform better in this test. I was not expecting the High-CPU instance to perform 277% faster.
UPDATE: Testing performance under load1
Load Testing:
The advantage of having a separate database server should become more pronounced as the site becomes more heavily used. The tests so far were testing responsiveness not ability to handle load. In order to test load, I am using ab with 12 concurrent users like this: “ab -n 6186 -c 12 http://174.129.193.145/page/id” Each test was tailored to last about three minutes.
The results for the medium database usage page:
Small Instance (default) High-CPU Medium Instance Concurrency Level 12 12 Average (requests/sec.) 6.55 18.62 Time per Request 1831.977 [ms] (mean) 644.545 [ms] (mean) Time per Request 152.665 [ms] (mean, across all concurrent) 53.712 [ms] (mean, across all concurrent) Percentage of requests served within a certain time (ms) 50% 1374 621 66% 1575 682 75% 1709 728 80% 1812 759 90% 2198 837 95% 2974 896 98% 7700 963 99% 15966 1039 100% 47130 (longest) 1864 Conclusion: The High-CPU instance wins again by a very large margin. Notice in particular how the Cluster is slower overall and some of the requests queue up and take a very long time to serve. Only getting 80% of your requests in under 2 seconds is simply not acceptable in a production environment. On the other hand, the High-CPU instance is able to serve 100% of the requests in under 2 seconds.
I thought that the Cluster would perform better in this test. I was not expecting the High-CPU instance to perform 184% faster. Apparently the High-CPU machine is so much more powerful that even having the database on its own box isn’t enough for the small cluster to win.
Overall Conclusion:
The test results were very surprising. I expected the performance between the two configurations to be similar, particularly in database intensive tests.
Given the dramatically better performance of the High-CPU instance in every test situation, I can’t recommend using a cluster of two Small instances for a Rails application. Your money will be much better spent using a single High-CPU instance.
In future posts, I will explore the various deployment options. Next up is Apache+Passenger vs. Nginx+Passenger.
- These tests were performed at the same time as the ones above. I simply neglected to post them originally ↩
Performance Testing, Rails Amazon EC2, EC2, EC2onRails, Nginx, Passenger, Performance Testing, Rails, Ruby on Rails6 responses to “EC2onRails - 2 Small Instances or 1 High-CPU Medium Instance?”

-
Thanks for doing those tests — very interesting in deed!
i’m currently running 3 smalls — one db, and two web but am straining the memory requirements of the web server already so couldn’t see myself going back to that setup.
Maybe it’s time to go even bigger, but back to one machine… hmmm.
Was your my.cnf any different for the scenarios?
-
Very interesting post! Quite a surprise to see the High-CPU instance outperform the Cluster at every account. Can’t wait to see your result of Apache+Passenger vs. Nginx+Passenger. Keep up the good work.
-
This is a great post!
These results make intuitive sense to me because for most rails apps, the Rails tier is more CPU intensive and the Database tier is more IO intensive. By splitting a simple app between 2 virtual machines of equal size, you are giving half the CPU to the Database, where really it can keep up with a lot less CPU allocation. By put it all in one (Virtual) host, the OS does that CPU allocation for you. I would bet that this app benchmark’s primary bottleneck was CPU (or possibly memory) on the app server tier.
Where this breaks down is in larger, more complex apps, where you want to scale resources (CPU, memory, disk) for your database independently from how you scale your app tier. So - like virtually every other performance and scaling discussion - it depends on your app…
Nevertheless this analysis is extremely useful and valuable, especially for apps that have moderate throughput and complexity. Keep up the great work and let us know what we at New Relic can do to help the cause! Cheers,
-Lew Cirne
-
Очень понравился ваш блог! Подписался на rss. Буду регулярно читать.
1 Trackbacks / Pingbacks
-
Engine Yard Solo vs. EC2onRails: Pay Engine Yard extra or do it yourself? | Ruby, Rails, and Technology May 24th, 2009 at 18:46
[...] test results for the EC2onRails server are not statistically different than the test results in my last post. This gives me confidence in the validity of these [...]


Chris Nolan.ca May 18th, 2009 at 03:39