Otimizando seu projeto Rails          @timotta               
Otimização prematura     Premature optimization       is the root of all evil          -- DonaldKnuth                   
Ruby é...            Lenta               
Rails é...             Lento                
O argumento...              
Porém...           Enfileiramento           De requisições                   
     
Apache Benchmark    ab -n 1 -c 1 http://localhost:3000/politicos/piores                              
Resultado de 1 requisiçãoab -n 1 -c 1 http://localhost:3000/politicos/pioresTime taken for tests: 1.239 secondsRequests pe...
Resultado de 400 por 20ab -n 400 -c 20 http://localhost:3000/politicos/pioresTime taken for tests: 595.433 secondsRequests...
Sempre a primeira supeita          Queries               
Verificando log    > rm log/production.log    > GET http://localhost:3000/politicos/piores    > grep -c select log/product...
N+1 problem               
Usando include:    Politico.piores.include(:cargo,:partido)                         
QueriesSELECT `cargos`.* FROM `cargos`WHERE `cargos`.`id` IN (4, 5, 1, 2, 8, 6, 3, 9)SELECT `partidos`.* FROM `partidos`WH...
N+1 problem               
Agrupando no controller@query = Avaliacao. where(politico_id: @politicos, eleitor_id: @eleitor)@avaliacoes = @query.reduce...
Usando na view    Antes:    @politico.avaliacoes.do_eleitor(@eleitor)    Agora:    @avaliacoes[@politico.id]              ...
QueriesSELECT `avaliacoes`.* FROM `avaliacoes`WHERE `avaliacoes`.`politico_id`  IN (640, 620, 639, 683, ...)AND `avaliacoe...
Apache Benchmarkab -n 1 -c 1 http://localhost:3000/politicos/pioresTime taken for tests: 0.658 secondsRequests per second:...
Evolução            
Vamos analisar mais...               
Cache dos filtrosdef cargos_for_select Rails.cache.fetch(cargos_for_select) do  Cargo.all.collect do |cargo|   [ cargo.no_...
Memcached    Gemfile:    gem memcache-client    environments:    config.cache_store = :mem_cache_store,      [localhost:11...
Resultadoab -n 400 -c 20 http://localhost:3000/politicos/pioresTime taken for tests: 300.668 secondsRequests per second: 1...
Evolução            
() sobre memcached    Rails 1       Memcache 1   Key ”ABC”    Rails 2       Memcache 2   Key ”XYZ”    Rails 3       Memcac...
Vamos aproveitar os CORES       Mas e o GIL?      http://goo.gl/CdsyZ                
Algumas opções    ● Puma    ● Thin    ● Passenger    ● Unicorn                   
Algumas opções    ● Puma            config.threadsafe!    ● Thin    ● Passenger    ● Unicorn                   
Unicorn    worker_processes 4    preload_app true    timeout 30    Listen 3000    after_fork do |server, worker|     Activ...
Resultadoab -n 400 -c 20 http://localhost:3000/politicos/pioresTime taken for tests: 109.909 secondsRequests per second: 3...
Evolução            
Indo mais fundo       Ferramentas para       encontrar gargalos                 
newrelicgem newrelic_rpm                      
ruby-prof    gem ruby-prof    > rails generate performance_test politicos    > rake test:profile    def test_piores     EN...
ruby-prof             
Ruby 1.9.3 e ruby-prof     http://goo.gl/u0hMd               
Patching Garbage Collectorhttps://github.com/skaes/rvm-patchsets> rvm install 1.9.3-p125    --patch railsexpress    --name...
Patched Garbage CollectorGC.enable_statsENV[RAILS_ENV] = production4.times { get /politicos/piores }ENV[RAILS_ENV] = testp...
4 acessos geram...allocated: 46409K total in 65930 allocations,GC calls: 6,GC time: 262 msec                           
Gargalo nos ERBs              
Transformando ERBs em Strdef botoes_para_avaliar(politico, avaliacao)  html = <<-RETORNO    <div class="avaliar">     #{te...
O que o GC nos dizAntes:allocated: 46409K total in 65930 allocations,GC calls: 6,GC time: 262 msec                        ...
E o Apache Benchmarkab -n 400 -c 20 http://localhost:3000/politicos/pioresTime taken for tests: 82.318 secondsRequests per...
Evolução            
Ministério da segurança adverte      Trocar ERB por String pode      tornar seu código vulnerável          html e script i...
Trocando bibliotecas      gem memcache-client      gem dalli                  
Benchmark    Benchmark.measure do       1000.times do |i|         Rails.cache.write("a#{i}",i)         Rails.cache.read("a...
Resultadoab -n 400 -c 20 http://localhost:3000/politicos/pioresTime taken for tests: 80.309 secondsRequests per second: 4....
Evolução            
O que o ruby-prof nos diz...             41% do seu           processamento               antes                    
Rack Middlewares    > RAILS_ENV=production rake middleware    use Rack::Cache    use #<ActiveSupport::Cache::Strategy::Loc...
Removendo alguns...config.middleware.delete(ActionDispatch::RemoteIp)config.middleware.delete(Rack::Sendfile)config.middle...
Efeitos colaterais...    404 antes       404 depois                 
O que o GC nos diz...    Antes:    allocated: 45466K total in 63078 allocations,    GC calls: 6,    GC time: 261 msec     ...
E o Apache Benchmarkab -n 400 -c 20 http://localhost:3000/politicos/pioresTime taken for tests: 75.588 secondsRequests per...
Evolução            
Mais ruby-prof                   15% para                 Gerar os filtros                   Da lateral              
Mais cache    def html_de_filtros      Rails.cache.fetch(filtros) do        html = <<-RETORNO         <div class="filtros"...
Resultadoab -n 400 -c 20 http://localhost:3000/politicos/pioresTime taken for tests: 73.719 secondsRequests per second: 5....
Evolução            
Requests por segundo             
Ferramentas    ● Apache Benchmark    ● Newrelic    ● Ruby-prof    ● Benckmark.mesure    ● Patched GC                  
Problemas/soluções comuns    ● N+1 problem    ● Cache    ● Paralelização    ● ERB    ● Middlewares                       
Outras técnicas    ●   Separar site cacheável         http://goo.gl/SyTnB    ●   Utilizando o Nginx para escalar         h...
Entre em contato    @timotta    timotta@gmail.com    http://programandosemcafeina.blogspot.com                             
Upcoming SlideShare
Loading in …5
×

Otimizando seu projeto Rails

1,030 views

Published on

Slides da palestra que apresentei no Rock and Rails de Vila Velha em outubro de 2012

Published in: Technology
4 Comments
5 Likes
Statistics
Notes
No Downloads
Views
Total views
1,030
On SlideShare
0
From Embeds
0
Number of Embeds
9
Actions
Shares
0
Downloads
11
Comments
4
Likes
5
Embeds 0
No embeds

No notes for slide

Otimizando seu projeto Rails

  1. 1. Otimizando seu projeto Rails @timotta   
  2. 2. Otimização prematura Premature optimization is the root of all evil -- DonaldKnuth   
  3. 3. Ruby é... Lenta   
  4. 4. Rails é... Lento   
  5. 5. O argumento...   
  6. 6. Porém... Enfileiramento De requisições   
  7. 7.    
  8. 8. Apache Benchmark ab -n 1 -c 1 http://localhost:3000/politicos/piores   
  9. 9. Resultado de 1 requisiçãoab -n 1 -c 1 http://localhost:3000/politicos/pioresTime taken for tests: 1.239 secondsRequests per second: 0.81 [#/sec]Time per request: 1239.403 [ms]   
  10. 10. Resultado de 400 por 20ab -n 400 -c 20 http://localhost:3000/politicos/pioresTime taken for tests: 595.433 secondsRequests per second: 0.67 [#/sec]Time per request: 1488.583 [ms]   
  11. 11. Sempre a primeira supeita Queries   
  12. 12. Verificando log > rm log/production.log > GET http://localhost:3000/politicos/piores > grep -c select log/production.log 216   
  13. 13. N+1 problem   
  14. 14. Usando include: Politico.piores.include(:cargo,:partido)   
  15. 15. QueriesSELECT `cargos`.* FROM `cargos`WHERE `cargos`.`id` IN (4, 5, 1, 2, 8, 6, 3, 9)SELECT `partidos`.* FROM `partidos`WHERE `partidos`.`id` IN (3, 9, 12, 11, 6, 4, 1)> rm log/production.log> GET http://localhost:3000/politicos/piores> grep -c Load log/production.log191   
  16. 16. N+1 problem   
  17. 17. Agrupando no controller@query = Avaliacao. where(politico_id: @politicos, eleitor_id: @eleitor)@avaliacoes = @query.reduce({}) do |grupo, avaliacao| grupo[avaliacao.politico_id] = avaliacao grupoend   
  18. 18. Usando na view Antes: @politico.avaliacoes.do_eleitor(@eleitor) Agora: @avaliacoes[@politico.id]   
  19. 19. QueriesSELECT `avaliacoes`.* FROM `avaliacoes`WHERE `avaliacoes`.`politico_id` IN (640, 620, 639, 683, ...)AND `avaliacoes`.`eleitor_id` = 6275> rm log/production.log> GET http://localhost:3000/politicos/piores> grep -c select log/production.log7   
  20. 20. Apache Benchmarkab -n 1 -c 1 http://localhost:3000/politicos/pioresTime taken for tests: 0.658 secondsRequests per second: 1.52 [#/sec]Time per request: 658.123 [ms]ab -n 400 -c 20 http://localhost:3000/politicos/pioresTime taken for tests: 298.956 secondsRequests per second: 1.34 [#/sec]Time per request: 747.390 [ms]   
  21. 21. Evolução   
  22. 22. Vamos analisar mais...   
  23. 23. Cache dos filtrosdef cargos_for_select Rails.cache.fetch(cargos_for_select) do Cargo.all.collect do |cargo| [ cargo.no_plural, cargo.slug ] end endend<%= options_for_select(cargos_for_select) %>   
  24. 24. Memcached Gemfile: gem memcache-client environments: config.cache_store = :mem_cache_store, [localhost:11211], namespace: Rails.env a   
  25. 25. Resultadoab -n 400 -c 20 http://localhost:3000/politicos/pioresTime taken for tests: 300.668 secondsRequests per second: 1.33 [#/sec]Time per request: 751.671 [ms]   
  26. 26. Evolução   
  27. 27. () sobre memcached Rails 1 Memcache 1 Key ”ABC” Rails 2 Memcache 2 Key ”XYZ” Rails 3 Memcache 3 Key ”123”   
  28. 28. Vamos aproveitar os CORES Mas e o GIL? http://goo.gl/CdsyZ   
  29. 29. Algumas opções ● Puma ● Thin ● Passenger ● Unicorn   
  30. 30. Algumas opções ● Puma config.threadsafe! ● Thin ● Passenger ● Unicorn   
  31. 31. Unicorn worker_processes 4 preload_app true timeout 30 Listen 3000 after_fork do |server, worker| ActiveRecord::Base.establish_connection end   
  32. 32. Resultadoab -n 400 -c 20 http://localhost:3000/politicos/pioresTime taken for tests: 109.909 secondsRequests per second: 3.64 [#/sec]Time per request: 274.773 [ms]   
  33. 33. Evolução   
  34. 34. Indo mais fundo Ferramentas para encontrar gargalos   
  35. 35. newrelicgem newrelic_rpm   
  36. 36. ruby-prof gem ruby-prof > rails generate performance_test politicos > rake test:profile def test_piores ENV[RAILS_ENV] = production get /politicos/piores ENV[RAILS_ENV] = test end   
  37. 37. ruby-prof   
  38. 38. Ruby 1.9.3 e ruby-prof http://goo.gl/u0hMd   
  39. 39. Patching Garbage Collectorhttps://github.com/skaes/rvm-patchsets> rvm install 1.9.3-p125 --patch railsexpress --name railsexpress> rvm use 1.9.3-p125-railsexpress@webdemocracia   
  40. 40. Patched Garbage CollectorGC.enable_statsENV[RAILS_ENV] = production4.times { get /politicos/piores }ENV[RAILS_ENV] = testputs "allocated: #{GC.allocated_size/1024}K total” + "in #{GC.num_allocations} allocations, "puts "GC calls: #{GC.collections}, "puts "GC time: #{GC.time / 1000} msec"   
  41. 41. 4 acessos geram...allocated: 46409K total in 65930 allocations,GC calls: 6,GC time: 262 msec   
  42. 42. Gargalo nos ERBs   
  43. 43. Transformando ERBs em Strdef botoes_para_avaliar(politico, avaliacao) html = <<-RETORNO <div class="avaliar"> #{texto_de_avaliacao avaliacao}:<br/> #{html_de_avaliacao_negativa politico, avaliacao} #{html_de_avaliacao_positiva politico, avaliacao} </div> RETORNO html.html_safeend    
  44. 44. O que o GC nos dizAntes:allocated: 46409K total in 65930 allocations,GC calls: 6,GC time: 262 msec 2852 a menosDepois:allocated: 45466K total in 63078 allocations,GC calls: 6,GC time: 261 msec   
  45. 45. E o Apache Benchmarkab -n 400 -c 20 http://localhost:3000/politicos/pioresTime taken for tests: 82.318 secondsRequests per second: 4.86 [#/sec]Time per request: 205.796 [ms]   
  46. 46. Evolução   
  47. 47. Ministério da segurança adverte Trocar ERB por String pode tornar seu código vulnerável html e script injection   
  48. 48. Trocando bibliotecas gem memcache-client gem dalli   
  49. 49. Benchmark Benchmark.measure do 1000.times do |i| Rails.cache.write("a#{i}",i) Rails.cache.read("a#{i}") end end Memcache-client: => 0.860000 0.020000 0.880000 ( 0.880819) Dalli: => 0.270000 0.020000 0.290000 ( 0.302308)   
  50. 50. Resultadoab -n 400 -c 20 http://localhost:3000/politicos/pioresTime taken for tests: 80.309 secondsRequests per second: 4.98 [#/sec]Time per request: 200.773 [ms]   
  51. 51. Evolução   
  52. 52. O que o ruby-prof nos diz... 41% do seu processamento antes   
  53. 53. Rack Middlewares > RAILS_ENV=production rake middleware use Rack::Cache use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x0000000293e930> use Rack::Runtime use Rack::MethodOverride use ActionDispatch::RequestId use Rails::Rack::Logger use ActionDispatch::ShowExceptions use ActionDispatch::DebugExceptions use ActionDispatch::RemoteIp use Rack::Sendfile use ActionDispatch::Callbacks use ActiveRecord::ConnectionAdapters::ConnectionManagement use ActiveRecord::QueryCache use ActionDispatch::Cookies use ActionDispatch::Session::CookieStore use ActionDispatch::Flash use ActionDispatch::ParamsParser use ActionDispatch::Head use Rack::ConditionalGet use Rack::ETag use ActionDispatch::BestStandardsSupport use ExceptionNotifier use OmniAuth::Builder run Webcracia::Application.routes   
  54. 54. Removendo alguns...config.middleware.delete(ActionDispatch::RemoteIp)config.middleware.delete(Rack::Sendfile)config.middleware.delete(ActionDispatch::RequestId)config.middleware.delete(Rack::Cache)config.middleware.delete(ActionDispatch::Callbacks)config.middleware.delete(Rack::ConditionalGet)config.middleware.delete(Rack::ETag)config.middleware.delete(ActionDispatch::BestStandardsSupport)config.middleware.delete(ActiveSupport::Cache::Strategy::LocalCache)config.middleware.delete(ActionDispatch::DebugExceptions)config.middleware.delete(ActionDispatch::ShowExceptions)config.middleware.delete(ActionDispatch::Head)    
  55. 55. Efeitos colaterais... 404 antes 404 depois   
  56. 56. O que o GC nos diz... Antes: allocated: 45466K total in 63078 allocations, GC calls: 6, GC time: 261 msec 1111 a menos Antes: allocated: 45329K total in 61967 allocations, GC calls: 6, GC time: 250 msec   
  57. 57. E o Apache Benchmarkab -n 400 -c 20 http://localhost:3000/politicos/pioresTime taken for tests: 75.588 secondsRequests per second: 5.29 [#/sec]Time per request: 188.970 [ms]   
  58. 58. Evolução   
  59. 59. Mais ruby-prof 15% para Gerar os filtros Da lateral   
  60. 60. Mais cache def html_de_filtros Rails.cache.fetch(filtros) do html = <<-RETORNO <div class="filtros">Filtrar por:<br/> #{select_de_cargos} #{select_de_partidos} #{select_de_estados} </div>" RETORNO html.html_safe end  end  
  61. 61. Resultadoab -n 400 -c 20 http://localhost:3000/politicos/pioresTime taken for tests: 73.719 secondsRequests per second: 5.43 [#/sec]Time per request: 184.296 [ms]   
  62. 62. Evolução   
  63. 63. Requests por segundo   
  64. 64. Ferramentas ● Apache Benchmark ● Newrelic ● Ruby-prof ● Benckmark.mesure ● Patched GC   
  65. 65. Problemas/soluções comuns ● N+1 problem ● Cache ● Paralelização ● ERB ● Middlewares   
  66. 66. Outras técnicas ● Separar site cacheável http://goo.gl/SyTnB ● Utilizando o Nginx para escalar http://goo.gl/k9H7D   
  67. 67. Entre em contato @timotta timotta@gmail.com http://programandosemcafeina.blogspot.com   

×