Home PC Games Linux Windows Database Network Programming Server Mobile  
  Home \ Linux \ How to improve the performance of Ruby On Rails     - Mount NFS network file system (Linux)

- Linux user directory (Linux)

- Shell script to delete empty folders recursively (Linux)

- Ubuntu 14.04 How to install Cinnamon 2.4.5 (Linux)

- Ubuntu Eclipse configuration Gtk + 2.0 libraries (Linux)

- Compression software on a simple comparison of zip and gz (Linux)

- Ubuntu 14.10 PPA installed Android Studio (Linux)

- JBPM6 Tutorial - taught you how to install JBPM (Linux)

- RHEL5 stalled due to power service error system can not start (Linux)

- How to fix the Ubuntu Error script returned error exit status 1 (Linux)

- After installation of Debian 6.0 do a few things first (Linux)

- How to modify the SQL Server auto-increment value and the corresponding solution (Database)

- Ubuntu 14.10 Install Ubuntu Touch Music App 2.0 (Linux)

- Java annotations entry automatically generates SQL statements (Programming)

- Let OpenCV face detection score output codes (Programming)

- Nginx server security configuration (Server)

- Linux RPM (Linux)

- How to use the TF / SD card making Exynos 4412 u-boot boot disk in Mac (Linux)

- Docker + OpenvSwitch build experimental environment VxLAN (Server)

- MySQL Tutorial: About checkpoint mechanism (Database)

  How to improve the performance of Ruby On Rails
  Add Date : 2016-04-21      
  1 Introduction Introduction

We always say Rails Hao Mana, which has become almost Ruby and Rails community is a commonplace problem. However, in practice this is not quite correct. As long as the proper use of Rails, your applications running 10 times faster is not difficult. So how to optimize your use of it, we have to understand the following.

1.1 optimization step of a Rails app

Cause nothing but slow down your Rails applications for two reasons:

Use Ruby and Rails in Ruby and Rails should not be used as the preferred place. (Using Ruby and Rails do not do good work)
Excessive consumption of memory leads to need to use a lot of time for garbage collection.
Rails is a pleasant framework, and Ruby is a simple and elegant language. But if it is abused, it will be a considerable impact on performance. There are a lot of work is not suitable for use Ruby and Rails, you are best to use other tools, such as data processing on a large database obvious advantages, R language particularly suitable for statistically relevant work.

Memory problems are a leading cause of many Ruby applications slow. Rails performance optimization of the 80-20 rule is this: 80% of the speed is derived from the optimization of the memory, the remaining 20% ​​belongs to other factors. Why is memory consumption so important? Because the more you allocate memory, Ruby GC (Ruby's garbage collection mechanisms) needs to be done the more. Rails had already takes up a lot of memory, and it must occupy nearly 100M memory on average after each application has just started. If you do not pay attention to the memory control, your program increased by more than 1G of memory is very possible. Recycling requires so much memory, program execution is no wonder that most of the time have been occupied by the GC.

2 How do we make a Rails application run faster?

There are three ways you can make your application faster: expansion, caching and code optimization.

Expansion in today's very easy to implement. Heroku basically do this for you, and Hirefire is to make this process more automated. You can learn more about this automatic expansion content. Other hosting environment provides a similar solution. In short, if you can use it wants. But keep in mind that the expansion is not a silver bullet to improve performance. If your application only in response to a request within five minutes, there will be no expansion of what. There is little use Heroku + Hirefire easily lead to your bank account was overdrawn. I've seen my Hirefire the expansion of an application to 36 entities, so I paid $ 3100 for this. I flew it manually instances volume reduction to 2, and the code has been optimized.

Rails cache is also very easy to implement. Rails 4 blocks in the cache is very good. Rails documentation is excellent information about the cache of knowledge. There is also an article on Rails performance Cheyne Wallace also worth reading. Now set Memcached is simple. However, compared with the expansion, the cache can not be the ultimate solution for performance problems. If your code does not run over, then you will find yourself more and more resources will spend on the cache until the cache can no longer bring the speed increase.

Let your Rails application faster only reliable way is to code optimization. In the Rails scenario which is memory optimization. And of course, if you accept my proposal, and to avoid the Rails for its design capacity range, you'll have less code to be optimized.

Avoid memory-intensive feature Rails 2.1

Rails takes a lot of memory, some of the characteristics result in additional garbage collection. Listed below.

2.1.1 serializer

Serializer is the string read from the database to show practical method Ruby data type.

class Smth < ActiveRecord :: Base
  serialize: data, JSON
Smth.find (...). Data
Smth.find (...). Data = {...}
But convenience comes with 3x memory overhead. If you store 100M in data column, expect to allocate 300M just to read it from the database.

It consumes more memory to effective serialization, you see:

class Smth < ActiveRecord :: Base
  def data
    JSON.parse (read_attribute (: data))
  def data = (value)
    write_attribute (: data, value.to_json)

This will be twice as long as the memory overhead. Some people, including myself, see Rails' JSON serializer memory leaks, each about 10% of the requested amount of data. I do not understand the reason behind this. I do not know whether there has been a replicable. If you have experience, or know how to reduce the memory, please tell me.

2.1.2 Active Record

It is easy to manipulate the data with ActiveRecord. But the essence is the ActiveRecord package your data. If you have a table of data 1g, ActiveRecord that will take 2g, in some cases more. Yes, 90% of cases, you get extra convenience. But sometimes you do not need, for example, can reduce the ActiveRecord batch update overhead. The following code, which does not instantiate any model, it will not run the validation and callbacks.

. Book.where ( '? Title LIKE', '% Rails%') update_all (author: 'David')

Behind the scenes it's just execute SQL update statement.

update books
  set author = 'David'
  where title LIKE '% Rails%'
... Another example is iteration over a large dataset Sometimes you need only the data No typecasting, no updates This snippet just runs the query and avoids ActiveRecord altogether:
result = ActiveRecord :: Base.execute 'select * from books'
result.each do | row |
  # Do something with row.values_at ( 'col1', 'col2')

2.1.3 string Recall

Save the callback like before Rails / after the operation before / after, and a lot of use. But you write in this way may affect your performance. There are three ways you can write, for example: before saving callbacks:
before_save: update_status
before_save do | model |
before_save "self.update_status"

The first two can be a good way to run, but not the third. why? Since the implementation of Rails callbacks need to store the execution context (variables, constants, global instance, etc.) is in the callback time. If your application is large, you end up copying a large amount of data in memory. Because callbacks can be executed at any time, the memory can not be recovered before the end of your program.

Symbolic, in each callback request saved me 0.6 seconds.

2.2 write less Ruby

This is my favorite step. My professor of computer science University likes to say, the best code does not exist. Sometimes doing the task at hand requires other tools. The most common is the database. why? Because Ruby good at dealing with large data sets. Very, very bad. Remember, Ruby occupy a very large memory. So, for example, data processing 1G or 3G you may need more memory. It takes tens of seconds to garbage collection this 3G. Good one second database can process the data. Let me give you some examples.

2.2.1 Properties preload

Sometimes property denormalization model obtained from another database. For example, imagine that we are building a TODO list, including task. Each task can have one or several markers tag. Canonical data model like this:


Loading Rails tasks and their label, you should do:

tasks = Task.find (: all,: include =>: tags)
    > 0.058 sec

This code has a problem, it creates an object for each tag, it takes a lot of memory. Alternative solutions, the label will be preloaded database.

tasks = Task.select << - END
      array (
        select tags.name from tags inner join tasks_tags on (tags.id = tasks_tags.tag_id)
        where tasks_tags.task_id = tasks.id
      ) As tag_names
    > 0.018 sec

This only requires memory to store extra one, there is an array tag. No wonder three times faster.

2.2.2 Data collection

I said any code data collection to summarize or analyze data. These operations can be simply summed up, or something more complicated. In group Rankings. Suppose we have an employee, department, wage data set, we have to calculate wages ranking in a sector.

SELECT * FROM empsalary;
  depname | empno | salary
----------- + ------- + -------
 develop | 6 | 6000
 develop | 7 | 4500
 develop | 5 | 4200
 personnel | 2 | 3900
 personnel | 4 | 3500
 sales | 1 | 5000
 sales | 3 | 4800

You can use Ruby Ranking calculated:

salaries = Empsalary.all
salaries.sort_by! {| s | [s.depname, s.salary]}
key, counter = nil, nil
salaries.each do | s |
 if s.depname! = key
  key, counter = s.depname, 0
 counter + = 1
 s.rank = counter

Empsalary 100K program list data is completed in 4.02 seconds. Alternative Postgres query, use the window function to do the same work in more than four times within 1.1 seconds.

SELECT depname, empno, salary, rank ()
FROM empsalary;
  depname | empno | salary | rank
----------- + ------- + -------- + ------
 develop | 6 | 6000 | 1
 develop | 7 | 4500 | 2
 develop | 5 | 4200 | 3
 personnel | 2 | 3900 | 1
 personnel | 4 | 3500 | 2
 sales | 1 | 5000 | 1
 sales | 3 | 4800 | 2

4 times already impressive acceleration, and sometimes you get more, to 20 times. From my own experience for example. I have a three-dimensional OLAP cube with 600k rows. I did a program slicing and aggregation. In Ruby, 1G memory it took about 90 seconds to complete. Equivalent SQL query completed within 5.

2.3 Optimization Unicorn

If you are using Unicorn, then the following optimization techniques will apply. Unicorn Rails framework is the fastest web server. But you can still make it more run a little bit faster.

2.3.1 pre-loaded applications App

Unicorn can before creating a new worker process, pre-loaded Rails applications. This has two advantages. The first main thread-friendly GC mechanism (Ruby 2.0 above) can be copied by writing the shared data memory. The operating system will be transparent to copy these data to be modified to prevent the worker. Second, pre-loaded to reduce the worker process started. Rails worker process restart is common (further described later), so the worker reboot the faster we can get better performance.

If you need to open the application preloaded, you only need to add a line in the unicorn's configuration file:

preload_app true

2.3.2 Request requests between GC

Remember, GC processing time will account for 50% of the maximum application time. This is not the only problem. GC is often unpredictable and can trigger you do not want to run when it is running. So, how do you deal with?

First, we think, if completely disable GC What happens? This seems to be a very bad idea. Your application is likely to soon fill 1G of memory, and you have failed to find. If you run the server also several worker, then your application will soon run out of memory, even if your application is in the self-hosted server. Not to mention only 512M memory limit of Heroku.

In fact, we have a better way. So if we can not avoid GC, we can try to make time to run as much as possible to determine the GC, and run at leisure. For example, between two request, Run GC. This is easily achieved by configuration Unicorn.

2.1 For previous versions Ruby, there is a module called the unicorn OobGC:

require 'unicorn / oob_gc'
    use (Unicorn :: OobGC, 1) # "1" indicates "Force GC run after a request"

For 2.1 and later versions Ruby, best to use gctools (https://github.com/tmm1/gctools):

require 'gctools / oobgc'
use (GC :: OOB :: UnicornMiddleware)

But in the run between request GC there are some considerations. Most importantly, this optimization technique is perceived. That is, the user will clearly feel the performance improvement. But the server needs to do more work. Unlike GC runs when needed, this technique requires a server running frequent GC. So, you should make sure your server has sufficient resources to run the GC, and other worker in the process of running GC, there are enough worker to handle the user's request.

2.4 limited growth

I have to show you some of the applications will take up 1G memory example. If your memory is sufficient, so it takes up a chunk of memory is not a big problem. But Ruby may not pass on this memory back to the operating system. Let me elaborate on why.

Ruby to allocate memory by two heap. In Ruby Ruby own heap among all the objects in storage. Each object occupies 40 bytes (64-bit operating systems). When the object needs more memory, it will be in the operating system heap memory allocation. When the object is garbage collected and release it occupied in the operating system heap memory will be returned to the operating system, but Ruby's own heap among the memory will simply marked as available for free and will not be returned to operating system.

This means that, Ruby heap will only increase, not decrease. Imagine, if you read 1 million rows from the database, each row 10 column. Then you need to allocate at least 10 million objects to store data. Ruby worker usually occupies 100M memory after startup. To accommodate so much data, worker needs additional 400M memory (10 million objects, each occupying 40 bytes). Finally, even if these objects were recovered, the worker still using 500M of memory.

It should be a statement, Ruby GC can reduce the size of the heap. But I have not found in actual combat has this feature. Because in a production environment, reducing the stack trigger conditions rarely occur.

If your worker can only increase, the most obvious solution is too much of it whenever memory usage when you restart the worker. Some hosting services will do this, such as Heroku. Let's look at other ways to achieve this.

2.4.1 Internal Memory Control

Trust in God, but lock your car believe in God, but do not forget to lock your car. (Meaning: Most foreigners have religious beliefs, believe that God is omnipotent, but in daily life, who can count on God to help ourselves faith is faith, but have a difficult time rely on my own.). There are two ways you can make your application to achieve self-memory limit. I was in charge they do, Kind (friendship) and hard (mandatory).

Kind-friendly memory size memory limit is mandatory after each request. If the worker takes up too much memory, then the worker will end, and the unicorn will create a new worker. That's why I do it alone "kind". It does not cause your application interruption.

Get process memory size, the use of RSS metrics and MacOS or Linux OS gem on the windows. I'll show how to achieve this lower limit Unicorn configuration file:


class Unicorn :: HttpServer
 alias process_client_orig process_client
 undef_method: process_client
 def process_client (client)
  process_client_orig (client)
  rss = `ps -o rss = -p # {Process.pid}` .chomp.to_i / 1024
  exit if rss> KIND_MEMORY_LIMIT_RSS

Hard drive memory limit by asking the operating system to kill the process of your work, if it grows a lot. On Unix you can call setrlimit to set RSSx limit. As far as I know, this is only available on Linux. MacOS implementation is broken. I would appreciate any new information.

This restriction fragment from Unicorn hard disk configuration file:

after_fork do | server, worker |
class Unicorn :: Worker
  def set_memory_limits
    Process.setrlimit (Process :: RLIMIT_AS, HARD_MEMORY_LIMIT * 1024 * 1024)

2.4.2 External Memory Control

No automatic control (insufficient memory) to save you from the occasional OMM. Normally you should set some external tools. On Heroku, it is not necessary because they have their own monitoring. But if you are self-hosting, using monit, god is a good idea, or other monitoring solutions.

2.5 Optimization Ruby GC

In some cases, you can adjust the Ruby GC to improve its performance. I would say that these GC tuning is becoming increasingly important, Ruby 2.1's default settings, then the majority of people have been favorable.

GC good tuning you need to know how it works. This is a separate topic, do not belong to this article series. To learn more, read thoroughly Sam Saffron Secret Ruby GC this article. Ruby in my upcoming performance of a book, I dug deeper Ruby GC detail. Subscribe to this, when I finished the book beta version will give you send an e-mail.

My best advice is not to change GC settings unless you know what you want to do, and have enough theoretical knowledge to know how to improve performance. For users of versions 2.1 or later of Ruby, which is particularly important.

I know only one occasion GC optimization can really bring improved performance. That is, when you want to load large amounts of data at once. You can change the following environment variables to run to reduce the frequency of GC: RUBY_GC_HEAP_GROWTH_FACTOR, RUBY_GC_MALLOC_LIMIT, RUBY_GC_MALLOC_LIMIT_MAX, RUBY_GC_OLDMALLOC_LIMIT, and RUBY_GC_OLDMALLOC_LIMIT.

Please note that these variables are only available in version 2.1 and later Ruby. For versions prior to 2.1 may be missing one variable or variables instead of using the name.

When RUBY_GC_HEAP_GROWTH_FACTOR default value 1.8, which is used when the Ruby heap does not have enough space to allocate memory each time the number should be increased. When you need to use the large number of objects, you want to heap memory space growing faster. In this case, you need to increase the size of the factor.

Memory limits are used to define when you need to apply for heap space when the operating system, GC is triggered frequency. 2.1 and later versions of Ruby, the default limit is:

New generation malloc limit RUBY_GC_MALLOC_LIMIT 16M
Maximum new generation malloc limit RUBY_GC_MALLOC_LIMIT_MAX 32M
Old generation malloc limit RUBY_GC_OLDMALLOC_LIMIT 16M
Maximum old generation malloc limit RUBY_GC_OLDMALLOC_LIMIT_MAX 128M

Let me briefly explain the significance of these values. By setting the value or more, each time a new object is partitioned between 16M to 32M, and each object occupies the old time between 16M to 128M ( "old object" means that the object is garbage collected at least once invoked), Ruby will run GC. Ruby will be based on your memory model, dynamic adjustment of the current limit value.

So, when you have only a few objects, but takes up a lot of memory (such as reading a large file into a string object), you can increase the limit to reduce the frequency of GC is triggered. Remember, while you want to increase the limit value 4, and preferably is a multiple of the default values.

My advice is possible and advice from others is not the same. I may be suitable for, but not necessarily for you. These articles will be introduced, which applies to Twitter, and what applies to Discourse.

2.6 Profile

Sometimes these recommendations is not necessarily common. You need to figure out your problem. At this time, you should use the profiler. Ruby-Prof is a tool every Ruby users will use.

Want to know more about profiling, read Chris Heald's about and I use ruby-prof in Rails articles. There are perhaps a little outdated recommendations on the memory profiling.

2.7 write performance test cases

Finally, to improve Rails performance skills, although not the most important, it is to confirm the performance of the application will not you change the code again and result in performance degradation. Rails 3.x comes with a function of a performance testing and profiling framework. For Rails 4, you can use the same framework by rails-perftest gem.

3 summarizes the message

For the article, how to improve the performance of Ruby and Rails, to be exhaustive, indeed impossible. So, after this, I will summarize by writing a book about my experiences. If you think my advice helpful, please register mailinglist, when I'm ready after the book's preview, you will notice the first time. Now, let us take a hands-on, so Rails applications run faster now!
- Linux user groups, file permissions Detailed (Linux)
- IBM Data Studio to create objects using ---- double quotes / sensitive issues and the table / column renaming (Database)
- iOS9 new feature - stacked view UIStackView (Programming)
- GAMIT baseline solution using batch were counted (Linux)
- JDK installation and configuration environment variable under linuxb (Linux)
- gzip, bzip2, xz, tar, zip compression, archive Detailed (Linux)
- CentOS install SystemTap-2.6 (Linux)
- Steps to build MPICH2 development environment on CentOS 6.4 (Linux)
- Linux command line ten magic usage (Linux)
- Linux System Getting Started Learning: Linux common log file (Linux)
- MySQL 5.7 perfectly distributed transaction support (Database)
- OpenGL Programming Guide (8th edition of the original book) - compute shader (Programming)
- Linux file system (inode and block) (Linux)
- Delay for the specified IP port analog network to send and receive packets on Linux (Linux)
- Update GAMIT10.6 command (Linux)
- Oracle 12c users create (Database)
- Install the latest ATI / Nvidia graphics driver on Ubuntu (Linux)
- Ant command-line compiler Android project (Programming)
- Iptables on the request URL for IP access control (Linux)
- Linux /var/spool/ insufficient clientmqueue space solutions (Linux)
  CopyRight 2002-2022 newfreesoft.com, All Rights Reserved.