Upgrade to Pro — share decks privately, control downloads, hide ads and more …

RailsConf 2015 Keynote

RailsConf 2015 Keynote

F29327647a9cff5c69618bae420792ea?s=128

Aaron Patterson

May 13, 2015
Tweet

Transcript

  1. None
  2. None
  3. Click to add title Click to add subtitle

  4. Why Windows?

  5. Kent Beck!!!

  6. W IN DO W S

  7. No emoji

  8. !"#$

  9. Welcome To Rails Conf!!!

  10. Aaron Patterson

  11. @tenderlove

  12. None
  13. Thanks!!!

  14. None
  15. None
  16. Ruby Central

  17. @eileencodes

  18. @_ko1

  19. Thank You!!! <3

  20. Cat photos

  21. None
  22. None
  23. None
  24. Extreme Programming

  25. None
  26. Last Year: Thought Leader in Training

  27. I Graduated!!

  28. Degree in Thought Leadering from Online University

  29. Ruby

  30. Rails

  31. Technoligy

  32. Rails

  33. Computers

  34. Rails

  35. Synergy

  36. Rails

  37. Innovation

  38. Rails

  39. Future

  40. Rails

  41. Words

  42. Trolling DHH

  43. "TDD is dead"

  44. SOA is dead

  45. Turbo Links 3: RJS

  46. I should announce something.

  47. Backpack

  48. Lightweight

  49. Action Fanny Pack

  50. Pink

  51. Performance! vroom!!!

  52. Rails 5?

  53. Adequate Development

  54. Automatic Parallel Testing

  55. Data Base Test Process Test Process Test Process

  56. Data Base Test Process Test Process Test Process Data Base

    Data Base Data Base
  57. Automatic

  58. Transparent

  59. Caching Compiled Views

  60. Today: lazy compile

  61. Get lock Template Compiled? Compile Save Unlock Writes to memory

  62. Threaded Servers: lock

  63. Forking servers: waste memory

  64. Challenges

  65. Too many templates!

  66. Rendering with Locals

  67. Template source eval(" local_variable = variables_hash[:local_variable] # rest of the

    template ")
  68. Integration Tests

  69. Controller Test class ControllerTest < ActionController::TestCase test "index" do get

    :index assert_equal 200, response.status end end
  70. Integration Test class IntegrationTest < ActionDispatch::IntegrationTest test "index" do get

    '/documents' assert_equal 200, response.status end end
  71. Why do we write controller tests?

  72. Because integration tests are slow?

  73. If tests are slow, is the website slow?

  74. Rails 5?: Fast Tests

  75. Controller / Integration: What’s the difference?

  76. Controller: no middleware

  77. Integration: has middleware

  78. Rails 5?: Delete controller tests *We won’t actually delete them

  79. Why So Slow?

  80. How slow?

  81. Benchmark Benchmark.ips do |bm| bm.report 'INDEX: Integration Test' do Minitest.run_one_method(IntegrationTest,

    'test_index') end bm.report 'INDEX: Functional Test' do Minitest.run_one_method(ControllerTest, 'test_index') end bm.compare! end Controller Integration Compare
  82. Result Calculating ------------------------------------- INDEX: Integration Test 15.000 i/100ms INDEX: Functional

    Test 39.000 i/100ms ------------------------------------------------- INDEX: Integration Test 158.019 (± 8.2%) i/s - 795.000 INDEX: Functional Test 390.853 (± 9.0%) i/s - 1.950k Comparison: INDEX: Functional Test: 390.9 i/s INDEX: Integration Test: 158.0 i/s - 2.47x slower Comparison
  83. What is slow?

  84. stackprof

  85. Controller Bottleneck Minitest.run_one_method(ControllerTest, 'test_index') StackProf.run(mode: :cpu, out: 'stackprof.dump') do 3000.times

    do Minitest.run_one_method(ControllerTest, 'test_index') end end
  86. CPU time vs Wall time

  87. `sleep` is slow, but doesn’t use CPU

  88. View Stack $ stackprof stackprof.dump

  89. Stack ================================== Mode: cpu(1000) Samples: 3280 (0.00% miss rate) GC:

    134 (4.09%) ================================== TOTAL (pct) SAMPLES (pct) FRAME 3146 (95.9%) 1740 (53.0%) Minitest::Runnable.on_signal 483 (14.7%) 483 (14.7%) Dependencies#search_for_file 207 (6.3%) 191 (5.8%) SQLite3::Database#prepare 95 (2.9%) 95 (2.9%) Inflector#underscore 47 (1.4%) 46 (1.4%) TestRequest#initialize
  90. 53.0% in Minitest::Runnable.on_signal

  91. on_signal def self.on_signal name, action # :nodoc: supported = SIGNALS[name]

    old_trap = trap name do old_trap.call if old_trap.respond_to? :call action.call end if supported yield ensure trap name, old_trap if supported end
  92. JUST DELETE IT

  93. on_signal def self.on_signal name, action yield end

  94. Results ================================== Mode: cpu(1000) Samples: 4246 (0.00% miss rate) GC:

    386 (9.09%) ================================== TOTAL (pct) SAMPLES (pct) FRAME 220 (5.2%) 185 (4.4%) SQLite3::Database#prepare 148 (3.5%) 131 (3.1%) TestRequest#initialize 113 (2.7%) 113 (2.7%) Event#initialize 105 (2.5%) 105 (2.5%) PerThreadRegistry#instance
  95. 50% faster!

  96. Time the process $ time ruby benchmark.rb real 0m12.358s user

    0m11.732s sys 0m0.498s $ time ruby benchmark.rb real 0m12.055s user 0m11.462s sys 0m0.475s Before After
  97. &

  98. CPU bound

  99. Wall should be similar to CPU

  100. Stack (CPU) ================================== Mode: cpu(1000) Samples: 3280 (0.00% miss rate)

    GC: 134 (4.09%) ================================== TOTAL (pct) SAMPLES (pct) FRAME 3146 (95.9%) 1740 (53.0%) Minitest::Runnable.on_signal 483 (14.7%) 483 (14.7%) Dependencies#search_for_file 207 (6.3%) 191 (5.8%) SQLite3::Database#prepare 95 (2.9%) 95 (2.9%) Inflector#underscore 47 (1.4%) 46 (1.4%) TestRequest#initialize
  101. Stack (WALL) ================================== Mode: wall(1000) Samples: 8797 (0.11% miss rate)

    GC: 535 (6.08%) ================================== TOTAL (pct) SAMPLES (pct) FRAME 286 (3.3%) 286 (3.3%) PerThreadRegistry#instance 233 (2.6%) 233 (2.6%) Formatter#non_recursive 336 (3.8%) 227 (2.6%) Class#class_attribute 209 (2.4%) 209 (2.4%) NonConcurrentCacheBackend#[] 198 (2.3%) 198 (2.3%) ActiveRecord::Base.logger
  102. '

  103. Even profilers have bugs!

  104. Something useful

  105. Integration test profiles.

  106. Stack (WALL) ================================== Mode: wall(1000) Samples: 20168 (2.95% miss rate)

    GC: 1247 (6.18%) ================================== TOTAL (pct) SAMPLES (pct) FRAME 6401 (31.7%) 5622 (27.9%) block in Module#delegate 495 (2.5%) 495 (2.5%) PerThreadRegistry#instance 341 (1.7%) 341 (1.7%) NonConcurrentCacheBackend#[] 317 (1.6%) 317 (1.6%) Set#include?
  107. Calling `delegate` is slow.

  108. Patch - delegate :url_for, :optimize_routes_generation?, to: '@_routes' + def url_for(options)

    + @_routes.url_for(options) + end + + def optimize_routes_generation? + @_routes.optimize_routes_generation? + end +
  109. Check our progress

  110. 2.47x slower

  111. Result Calculating ------------------------------------- INDEX: Integration Test 23.000 i/100ms INDEX: Functional

    Test 37.000 i/100ms ------------------------------------------------- INDEX: Integration Test 251.487 (±12.7%) i/s - 1.242k INDEX: Functional Test 365.112 (±11.0%) i/s - 1.813k Comparison: INDEX: Functional Test: 365.1 i/s INDEX: Integration Test: 251.5 i/s - 1.45x slower Comparison
  112. 2.47x to 1.45x

  113. Verify $ time ruby test.rb real 0m26.821s user 0m25.277s sys

    0m0.903s $ time ruby test.rb real 0m16.632s user 0m15.709s sys 0m0.638s Before After
  114. Even profilers have bugs

  115. Always measure

  116. Verify results

  117. Today: 10% slower

  118. But faster than controller tests in previous versions

  119. Soup two Nuts Performance

  120. bundler

  121. Binstubs

  122. bin stub source code #!/Users/aaron/.rbenv/versions/ruby-trunk/bin/ruby # # This file was

    generated by RubyGems. # # The application 'bundler' is installed as part of a gem, and # this file is here to facilitate running it. # # some unimportant stuff happens here! gem 'bundler', version load Gem.bin_path('bundler', 'bundle', version)
  123. gem

  124. require

  125. What does `gem` do?

  126. Finds a gemspec

  127. Puts a path on $LOAD_PATH

  128. Load Path Manipulation > x = $LOAD_PATH.dup; nil => nil

    > gem 'bundler' => true > $LOAD_PATH - x => ["/Users/aaron/.rbenv/versions/ruby-trunk/lib/ruby/gems/2.3.0/gems/ bundler-1.9.2/lib"]
  129. What Happens When File is Required?

  130. Is File in $LOAD_PATH? Load it! Is file in Gemspec?

    Add gemspec to $LOAD_PATH
  131. RubyGems performance

  132. Test Methodology

  133. Require One File And Increase Gems

  134. Activate one Gem And Increase Gems

  135. time to require one file Time in seconds 0 0.08

    0.16 0.24 0.32 Number of Gems on the system 0 100 200 300 400 500 600 700 800 900 1000 best worst
  136. allocations to require one file Number of allocations 0 22500

    45000 67500 90000 Number of Gems on the system 0 100 200 300 400 500 600 700 800 900 1000 best worst
  137. time to activate one gem Time in seconds 0 0.06

    0.12 0.18 0.24 Number of Gems on the system 0 100 200 300 400 500 600 700 800 900 1000 best worst
  138. allocations to activate one gem Number of allocations 0 10000

    20000 30000 40000 Number of Gems on the system 0 100 200 300 400 500 600 700 800 900 1000
  139. Bundler Performance

  140. Test Method

  141. One Gem in Gemfile Increase Gems on System

  142. Increase Gems in Gemfile Constant Gems on System

  143. *NO DEPENDENCIES

  144. *ALL LOCAL

  145. Vary System Gems

  146. `bundle update` with one gem Time in Seconds 0 0.2

    0.4 0.6 0.8 Number of Gems on the system 100 200 300 400 500 600 700 800 900 1000
  147. `bundle exec` with one gem Time in Seconds 0 0.2

    0.4 0.6 0.8 Number of Gems on the system 100 200 300 400 500 600 700 800 900 1000
  148. Vary Gems in Gemfile

  149. `bundle exec` time Time in seconds 1.44 1.45 1.46 1.47

    1.48 Number of gems in the gemfile 0 100 200 300 400 500 600 700 800 900 1000 y = 0.0008x + 1.4603 R² = 0.1323
  150. Bundle Exec Time Breakdown (ms) Other? 750 Spec Load 710

  151. `bundle update` time Time in Seconds 0 17.5 35 52.5

    70 Number of Gems in the Gemfile 0 100 200 300 400 500 600 700 800 900 1000 y = 0.6984x2 - 2.9466x + 13.856 R² = 0.999
  152. Thanks Excel

  153. None
  154. None
  155. None
  156. R Code - Fit curve > bundler <- read.csv(file="~/bundle_test/ run_bundle_updats.csv",head=TRUE,sep=",")

    > fit2 <- lm(bundler$time ~ poly(bundler$gems, 2, raw = TRUE)) > xx <- seq(0, 1000, length=11) > plot(bundler$gems, bundler$time) > lines(xx, predict(fit2, data.frame(x = xx)), col="red")
  157. None
  158. R Code - Coefficients > coefficients(fit2) (Intercept) poly(bundler$gems, 2, raw

    = TRUE)1 poly(bundler$gems, 2, raw = TRUE)2 1.160827e+01 -1.549806e-02 6.984136e-05 > summary(fit2)$r.squared [1] 0.9989536 >
  159. None
  160. Make Predictions > second_order <- function(newdist, model) { + coefs

    <- coef(model) + res <- coefs[1] + (coefs[2] * newdist) + (coefs[3] * newdist^2) + return(res) + } > second_order(0, fit2) (Intercept) 11.60827 > second_order(10000, fit2) (Intercept) 6840.763 empty gemfile 10k gemfile
  161. How to Fix?

  162. What do we know?

  163. bin stub source code #!/Users/aaron/.rbenv/versions/ruby-trunk/bin/ruby # # This file was

    generated by RubyGems. # # The application 'bundler' is installed as part of a gem, and # this file is here to facilitate running it. # # some unimportant stuff happens here! gem 'bundler', version load Gem.bin_path('bundler', 'bundle', version) gem name
  164. Gemfile.lock GEM specs: pkg0 (1) PLATFORMS ruby DEPENDENCIES pkg0 gem

    name and version!
  165. Spec loading code today def get_spec name spec_cache = Dir[File.join(dir,

    "*.gemspec")] spec_cache.find { |spec| spec.name == name } end
  166. bundle -v against 100k gems [aaron@TC rubygems (master)]$ time GEM_HOME=`pwd`/100k_gems

    GEM_PATH=`pwd`/100k_gems ruby -I lib 100k_gems/bin/bundle -v Bundler version 1.9.4 real 0m50.045s user 0m15.815s sys 0m11.967s
  167. Spec loading code I want def get_spec name spec_cache =

    Dir[File.join(dir, "#{name}-*.gemspec")] spec_cache.find { |spec| spec.name == name } end
  168. bundle -v against 100k gems [aaron@TC rubygems (specs)]$ time GEM_HOME=`pwd`/100k_gems

    GEM_PATH=`pwd`/100k_gems ruby -I lib 100k_gems/bin/bundle -v Bundler version 1.9.4 real 0m0.632s user 0m0.479s sys 0m0.124s
  169. Still O(n)

  170. I think we can make it O(1)

  171. Future Work: Make `require` O(1)

  172. Don’t recalculate static data

  173. Take measurements

  174. Question intuitions

  175. I have stickers!

  176. THANK YOU!!!