Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
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,081 views

Published on

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

Published in: Technology

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   

×