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

The Science of Success - Cascadia Ruby 2014

The Science of Success - Cascadia Ruby 2014

Software is approached mainly from the angle of engineering. Let's step back and take a look at software as science. How can we increase the quality of our code, tune our minds to efficiently solve problems, and correctly reapply known solutions to new problems? Learn a few new tricks about how to easily benchmark your code and how to analyze code complexity.

Framed by the story of my own path from would-be Physicist to happy Software Engineer, let's look at using the scientific method as a guide for software development.

5f557a41fc286ddcc1ed6b869c6d04c3?s=128

Davy Stevenson

August 12, 2014
Tweet

Transcript

  1. The Science of Success Davy Stevenson @davystevenson Cascadia Ruby 2014

  2. The Columbia River Gorge, Home by Paul Kline

  3. Astronaut by Alan

  4. Williams College by Ethan Kan

  5. White, Mt Greylock by kumsval

  6. In Search of the Abominable Snowman by Peter Lee

  7. Fish Bowl Embroidery by Jenny S

  8. light brain by Mario D’Amore

  9. Cao Xueqin, Dream of the Red Chamber by Ivan Walsh

  10. Modern Art in Prague by BrunoDelzant

  11. Greek Philosopher Mosaic by Holly Hayes

  12. Hacker by the Preiser Project

  13. Glass of Heart by Rahim Packir Saibo

  14. 100 Planetary Nebulas by Judy Schmidt

  15. Professor John Frink, Jr. vs. Professor Hubert J. Farnsworth by

    JD Hancock
  16. Complexity

  17. Brockhaus and Efron Encyclopedic Dictionary

  18. Complexity O(n) O(log n) O(n log n) O(n2) O(2n) O(n!)

  19. Complexity O(log n) - Logarithmic - Sorted Data

  20. Complexity O(n) - Linear - Analyze each point

  21. Complexity O(n log n) - Loglinear Best Sorting Algorithms

  22. Complexity O(n2) - Quadratic Compare pairs of points

  23. Complexity O(2n) - Exponential - Try and avoid

  24. Complexity O(n!) - Factorial You’re gonna have a bad time

  25. Algorithms

  26. Silver and Gold - Charles Babbage’s Difference Engine by odditty

  27. jar by hillary.hc.chang

  28. Convex Hull by fdecomite

  29. Stonebalancing in Greece by t.klick

  30. Jarvis March

  31. Plejaden M45 by Carsten Frenzi

  32. Plejaden M45 by Carsten Frenzi

  33. Plejaden M45 by Carsten Frenzi

  34. Plejaden M45 by Carsten Frenzi

  35. Plejaden M45 by Carsten Frenzi

  36. Plejaden M45 by Carsten Frenzi

  37. Plejaden M45 by Carsten Frenzi

  38. Plejaden M45 by Carsten Frenzi

  39. Plejaden M45 by Carsten Frenzi

  40. Plejaden M45 by Carsten Frenzi

  41. Plejaden M45 by Carsten Frenzi

  42. Jarvis March • Find one edge: O(n) • Find right-most

    point: O(n) • Repeat for each point on hull: O(h) • O(n + n * h) => O(n h) • Worst case: O(n2)
  43. Monotone Chain

  44. Plejaden M45 by Carsten Frenzi

  45. 1 2 3 4 5 6 7 8 9 Plejaden

    M45 by Carsten Frenzi
  46. 1 2 3 4 5 6 7 8 9 Plejaden

    M45 by Carsten Frenzi
  47. 4 5 6 7 8 9 Plejaden M45 by Carsten

    Frenzi
  48. 5 6 7 8 9 Plejaden M45 by Carsten Frenzi

  49. 5 6 7 8 9 Plejaden M45 by Carsten Frenzi

  50. 5 6 7 8 9 Plejaden M45 by Carsten Frenzi

  51. 6 7 8 9 Plejaden M45 by Carsten Frenzi

  52. 7 8 9 Plejaden M45 by Carsten Frenzi

  53. 7 8 9 Plejaden M45 by Carsten Frenzi

  54. 7 8 9 Plejaden M45 by Carsten Frenzi

  55. 7 8 9 Plejaden M45 by Carsten Frenzi

  56. 8 9 Plejaden M45 by Carsten Frenzi

  57. 9 Plejaden M45 by Carsten Frenzi

  58. 9 Plejaden M45 by Carsten Frenzi

  59. Plejaden M45 by Carsten Frenzi

  60. Plejaden M45 by Carsten Frenzi

  61. Plejaden M45 by Carsten Frenzi

  62. Plejaden M45 by Carsten Frenzi

  63. Plejaden M45 by Carsten Frenzi

  64. Monotone Chain • Sort points: O(n log n) • Construct

    lower hull: O(n) • Construct upper hull: O(n) • O(n log n + (n + n)) => O(n log n) • O(n) if already sorted
  65. Pushpins in a map over the USA by Marc Levin

  66. Benchmark

  67. Fast by photophilde

  68. require 'benchmark' n = 100_000 size = 10000 array =

    (0...size).to_a.shuffle ! Benchmark.bm do |x| x.report("#at") { n.times { array.at rand(size)} } x.report("#index") { n.times { array.index rand(size) } } x.report("#index-miss") { n.times { array.index (size + rand(size)) } } end
  69. require 'benchmark' n = 100_000 size = 10000 array =

    (0...size).to_a.shuffle ! Benchmark.bm do |x| x.report("#at") { n.times { array.at rand(size)} } x.report("#index") { n.times { array.index rand(size) } } x.report("#index-miss") { n.times { array.index (size + rand(size)) } } end
  70. require 'benchmark' n = 100_000 size = 10000 array =

    (0...size).to_a.shuffle ! Benchmark.bm do |x| x.report("#at") { n.times { array.at rand(size)} } x.report("#index") { n.times { array.index rand(size) } } x.report("#index-miss") { n.times { array.index (size + rand(size)) } } end
  71. require 'benchmark' n = 100_000 size = 10000 array =

    (0...size).to_a.shuffle ! Benchmark.bm do |x| x.report("#at") { n.times { array.at rand(size)} } x.report("#index") { n.times { array.index rand(size) } } x.report("#index-miss") { n.times { array.index (size + rand(size)) } } end
  72. require 'benchmark' n = 100_000 size = 10000 array =

    (0...size).to_a.shuffle ! Benchmark.bm do |x| x.report("#at") { n.times { array.at rand(size)} } x.report("#index") { n.times { array.index rand(size) } } x.report("#index-miss") { n.times { array.index (size + rand(size)) } } end
  73. $ ruby examples/bm.rb user system total real #at 0.020000 0.000000

    0.020000 ( 0.023444) #index 2.380000 0.000000 2.380000 ( 2.391208) #index-miss 4.770000 0.000000 4.770000 ( 4.786147)
  74. $ ruby examples/bm.rb user system total real #at 0.020000 0.000000

    0.020000 ( 0.023444) #index 2.380000 0.000000 2.380000 ( 2.391208) #index-miss 4.770000 0.000000 4.770000 ( 4.786147)
  75. require 'benchmark' n = 100_000 size = 10000 array =

    (0...size).to_a.shuffle ! Benchmark.bm do |x| x.report("#at") { n.times { array.at rand(size)} } x.report("#index") { n.times { array.index rand(size) } } x.report("#index-miss") { n.times { array.index (size + rand(size)) } } end
  76. require 'benchmark/ips' ! size = 10000 array = (0...size).to_a.shuffle !

    Benchmark.ips do |x| x.report("#at") { array.at rand(size) } x.report("#index") { array.index rand(size) } x.report("#index-miss") { array.index (size + rand(size)) } x.compare! end
  77. require 'benchmark/ips' ! size = 10000 array = (0...size).to_a.shuffle !

    Benchmark.ips do |x| x.report("#at") { array.at rand(size) } x.report("#index") { array.index rand(size) } x.report("#index-miss") { array.index (size + rand(size)) } x.compare! end
  78. $ ruby examples/ips.rb ! ! ! Calculating ------------------------------------- #at 79155

    i/100ms #index 4039 i/100ms #index-miss 2055 i/100ms ------------------------------------------------- #at 3165995.2 (±10.7%) i/s - 15593535 in 5.006746s #index 41873.8 (±6.3%) i/s - 210028 in 5.040799s #index-miss 20592.7 (±7.5%) i/s - 102750 in 5.022168s ! Comparison: #at: 3165995.2 i/s #index: 41873.8 i/s - 75.61x slower #index-miss: 20592.7 i/s - 153.74x slower
  79. $ ruby examples/ips.rb ! ! ! Calculating ------------------------------------- #at 79155

    i/100ms #index 4039 i/100ms #index-miss 2055 i/100ms ------------------------------------------------- #at 3165995.2 (±10.7%) i/s - 15593535 in 5.006746s #index 41873.8 (±6.3%) i/s - 210028 in 5.040799s #index-miss 20592.7 (±7.5%) i/s - 102750 in 5.022168s ! Comparison: #at: 3165995.2 i/s #index: 41873.8 i/s - 75.61x slower #index-miss: 20592.7 i/s - 153.74x slower
  80. $ ruby examples/ips.rb ! ! ! Calculating ------------------------------------- #at 79155

    i/100ms #index 4039 i/100ms #index-miss 2055 i/100ms ------------------------------------------------- #at 3165995.2 (±10.7%) i/s - 15593535 in 5.006746s #index 41873.8 (±6.3%) i/s - 210028 in 5.040799s #index-miss 20592.7 (±7.5%) i/s - 102750 in 5.022168s ! Comparison: #at: 3165995.2 i/s #index: 41873.8 i/s - 75.61x slower #index-miss: 20592.7 i/s - 153.74x slower
  81. $ ruby examples/ips.rb ! ! ! Calculating ------------------------------------- #at 79155

    i/100ms #index 4039 i/100ms #index-miss 2055 i/100ms ------------------------------------------------- #at 3165995.2 (±10.7%) i/s - 15593535 in 5.006746s #index 41873.8 (±6.3%) i/s - 210028 in 5.040799s #index-miss 20592.7 (±7.5%) i/s - 102750 in 5.022168s ! Comparison: #at: 3165995.2 i/s #index: 41873.8 i/s - 75.61x slower #index-miss: 20592.7 i/s - 153.74x slower
  82. $ ruby examples/ips.rb ! ! ! Calculating ------------------------------------- #at 79155

    i/100ms #index 4039 i/100ms #index-miss 2055 i/100ms ------------------------------------------------- #at 3165995.2 (±10.7%) i/s - 15593535 in 5.006746s #index 41873.8 (±6.3%) i/s - 210028 in 5.040799s #index-miss 20592.7 (±7.5%) i/s - 102750 in 5.022168s ! Comparison: #at: 3165995.2 i/s #index: 41873.8 i/s - 75.61x slower #index-miss: 20592.7 i/s - 153.74x slower
  83. $ ruby examples/ips.rb ! ! ! Calculating ------------------------------------- #at 79155

    i/100ms #index 4039 i/100ms #index-miss 2055 i/100ms ------------------------------------------------- #at 3165995.2 (±10.7%) i/s - 15593535 in 5.006746s #index 41873.8 (±6.3%) i/s - 210028 in 5.040799s #index-miss 20592.7 (±7.5%) i/s - 102750 in 5.022168s ! Comparison: #at: 3165995.2 i/s #index: 41873.8 i/s - 75.61x slower #index-miss: 20592.7 i/s - 153.74x slower
  84. None
  85. Benchmark BigO

  86. Benchmark Big O • github.com/davy/benchmark-bigo • Provides Big O notation

    benchmarking for Ruby
  87. require 'benchmark/ips' ! size = 10000 array = (0...size).to_a.shuffle !

    Benchmark.ips do |x| x.report("#at") { array.at rand(size) } x.report("#index") { array.index rand(size) } x.report("#index-miss") { array.index (size + rand(size)) } x.compare! end
  88. require 'benchmark/bigo' ! Benchmark.bigo do |x| x.generate :array x.linear 2000

    ! x.report("#at") {|array, size| array.at rand(size) } x.report("#index") {|array, size| array.index rand(size) } x.report("#index-miss") {|array, size| array.index (size + rand(size)) } x.chart! 'array.html' end
  89. require 'benchmark/bigo' ! Benchmark.bigo do |x| x.generate :array x.linear 2000

    ! x.report("#at") {|array, size| array.at rand(size) } x.report("#index") {|array, size| array.index rand(size) } x.report("#index-miss") {|array, size| array.index (size + rand(size)) } x.chart! 'array.html' end
  90. require 'benchmark/bigo' ! Benchmark.bigo do |x| x.generate :array x.exponential !

    x.report("#at") {|array, size| array.at rand(size) } x.report("#index") {|array, size| array.index rand(size) } x.report("#index-miss") {|array, size| array.index (size + rand(size)) } x.chart! 'array.html' end
  91. require 'benchmark/bigo' ! Benchmark.bigo do |x| x.generate :array x.exponential !

    x.report("#at") {|array, size| array.at rand(size) } x.report("#index") {|array, size| array.index rand(size) } x.report("#index-miss") {|array, size| array.index (size + rand(size)) } x.chart! 'array.html' end
  92. require 'benchmark/bigo' ! Benchmark.bigo do |x| x.generate :array x.exponential !

    x.report("#at") {|array, size| array.at rand(size) } x.report("#index") {|array, size| array.index rand(size) } x.report("#index-miss") {|array, size| array.index (size + rand(size)) } x.chart! 'array.html' end
  93. $ ruby examples/bigo-exp.rb ! Calculating ------------------------------------- #at 1 68448 i/100ms

    #at 10 63310 i/100ms #at 100 67388 i/100ms #at 1000 65912 i/100ms #at 10000 65518 i/100ms #index 1 66714 i/100ms #index 10 63736 i/100ms #index 100 50055 i/100ms #index 1000 18838 i/100ms #index 10000 3752 i/100ms #index-miss 1 61960 i/100ms #index-miss 10 58333 i/100ms #index-miss 100 45989 i/100ms #index-miss 1000 14698 i/100ms #index-miss 10000 1898 i/100ms ------------------------------------------------- #at 1 2084640.3 (±9.5%) i/s - 10335648 in 5.029076s #at 10 2006228.2 (±8.3%) i/s - 9939670 in 5.003949s #at 100 1871633.1 (±18.1%) i/s - 8962604 in 5.025712s #at 1000 2055675.6 (±8.7%) i/s - 10216360 in 5.029822s #at 10000 2000566.4 (±8.6%) i/s - 9893218 in 4.999732s #index 1 1992529.4 (±8.2%) i/s - 9940386 in 5.035758s #index 10 1810344.6 (±8.3%) i/s - 8986776 in 5.012351s #index 100 1304666.0 (±9.2%) i/s - 6457095 in 5.010804s #index 1000 347986.2 (±6.0%) i/s - 1733096 in 5.002854s #index 10000 40728.2 (±10.7%) i/s - 202608 in 5.059052s #index-miss 1 1984727.2 (±9.4%) i/s - 9851640 in 5.028868s #index-miss 10 1762957.2 (±7.6%) i/s - 8749950 in 5.004258s #index-miss 100 986260.8 (±10.2%) i/s - 4874834 in 5.010465s #index-miss 1000 187159.1 (±7.9%) i/s - 940672 in 5.063904s #index-miss 10000 19330.9 (±12.8%) i/s - 94900 in 5.001322s
  94. None
  95. Map of Node coverage with Convex Hulls by Dario Villanueva

  96. Terraformer

  97. Terraformer Ruby • github.com/esripdx/terraformer-ruby • A geometric toolkit for dealing

    with geometry, geography, formats • Can calculate Convex Hull
  98. Jarvis Tortoise by Alexander Montuschi

  99. Run, bunny, run! by shadow planet Monotone

  100. None
  101. None
  102. module Terraformer::Benchmark # portland LON = -122.6764 LAT = 45.5165

    ! # calculate convex hull with given implementation def self.convex_hull obj, impl Terraformer::ConvexHull.impl = impl obj.convex_hull end end ! class Float def perm self + rand(-100..100) / 10000.0 end end
  103. module Terraformer::Benchmark # portland LON = -122.6764 LAT = 45.5165

    ! # calculate convex hull with given implementation def self.convex_hull obj, impl Terraformer::ConvexHull.impl = impl obj.convex_hull end end ! class Float def perm self + rand(-100..100) / 10000.0 end end
  104. module Terraformer::Benchmark # portland LON = -122.6764 LAT = 45.5165

    ! # calculate convex hull with given implementation def self.convex_hull obj, impl Terraformer::ConvexHull.impl = impl obj.convex_hull end end ! class Float def perm self + rand(-100..100) / 10000.0 end end
  105. module Terraformer::Benchmark ! # generates a Line String representing a

    random walk def self.random_walk size walk = [[LON, LAT]] # start in pdx ! size.times do walk << walk.last.map{|i| i.perm } end ! # create line string from the random walk ls = Terraformer::LineString.new(walk) ! # convert to feature ls.to_feature end end
  106. module Terraformer::Benchmark ! # generates a Line String representing a

    random walk def self.random_walk size walk = [[LON, LAT]] # start in pdx ! size.times do walk << walk.last.map{|i| i.perm } end ! # create line string from the random walk ls = Terraformer::LineString.new(walk) ! # convert to feature ls.to_feature end end
  107. None
  108. Benchmark.bigo do |x| x.generator {|size| Terraformer::Benchmark.random_walk size } ! x.time

    = 20 # sample each point for 20 seconds x.linear 200 x.increments = 5 # 200..1000 ! x.report("#rand-jarvis") {|f, _| Terraformer::Benchmark.convex_hull f, :jarvis_march } ! x.report("#rand-monotone") {|f, _| Terraformer::Benchmark.convex_hull f, :monotone} ! x.chart! 'data/chart_random_walk.html' x.compare! end
  109. Benchmark.bigo do |x| x.generator {|size| Terraformer::Benchmark.random_walk size } ! x.time

    = 20 # sample each point for 20 seconds x.linear 200 x.increments = 5 # 200..1000 ! x.report("#rand-jarvis") {|f, _| Terraformer::Benchmark.convex_hull f, :jarvis_march } ! x.report("#rand-monotone") {|f, _| Terraformer::Benchmark.convex_hull f, :monotone} ! x.chart! 'data/chart_random_walk.html' x.compare! end
  110. Benchmark.bigo do |x| x.generator {|size| Terraformer::Benchmark.random_walk size } ! x.time

    = 20 # sample each point for 20 seconds x.linear 200 x.increments = 5 # 200..1000 ! x.report("#rand-jarvis") {|f, _| Terraformer::Benchmark.convex_hull f, :jarvis_march } ! x.report("#rand-monotone") {|f, _| Terraformer::Benchmark.convex_hull f, :monotone} ! x.chart! 'data/chart_random_walk.html' x.compare! end
  111. None
  112. None
  113. None
  114. Water Balloon by Amyn Kassam

  115. Water Balloon by Amyn Kassam

  116. module Terraformer::Benchmark ! # generates a circle feature with size

    points def self.circle size ! # minimum size is three size = 3 if size < 3 ! # expand the diameter as size increases diam = [100, size].max ! # generate circle c = Terraformer::Circle.new([LON, LAT], diam, size) ! # convert to feature c.to_feature end end
  117. module Terraformer::Benchmark ! # generates a circle feature with size

    points def self.circle size ! # minimum size is three size = 3 if size < 3 ! # expand the diameter as size increases diam = [100, size].max ! # generate circle c = Terraformer::Circle.new([LON, LAT], diam, size) ! # convert to feature c.to_feature end end
  118. None
  119. Benchmark.bigo do |x| x.generator {|size| Terraformer::Benchmark.circle size } ! x.time

    = 20 # sample each point for 20 seconds x.linear 200 x.increments = 5 # 200..1000 ! x.report("#circ-jarvis") {|f, _| Terraformer::Benchmark.convex_hull f, :jarvis_march } ! x.report("#circ-monotone") {|f, _| Terraformer::Benchmark.convex_hull f, :monotone} ! x.chart! 'data/chart_circle.html' x.compare! end
  120. None
  121. None
  122. None
  123. None
  124. None
  125. Benchmark Terraformer • github.com/davy/benchmark-terraformer • Helper code • Benchmark code

    • Map Viewer • Example charts, geojson & raw data • Array benchmark code
  126. My grandfather's microscope by Juan Eduardo Donoso

  127. Peer Review!

  128. Line Fitting

  129. Prettier Graphs

  130. Verify Math

  131. None
  132. Science Driven Development

  133. QUESTION

  134. why is ____________ slow?

  135. why is ____________ broken?

  136. is ____________ a success?

  137. HYPOTHESIS

  138. it is slow because ___________

  139. it is broken because ___________

  140. it is successful because ___________

  141. PREDICTION

  142. if __________ is why it is slow then ____________

  143. if __________ is why it is broken then ____________

  144. if __________ is true then it is a success

  145. TESTING

  146. test the speed of ____________

  147. test the logic of ____________

  148. test if ____________ is true

  149. ANALYSIS

  150. what does the data say?

  151. Fix

  152. Sleeping Cat by Terence Lee

  153. Scientist Minifig by Maia Weinstock

  154. Thanks! Questions? Davy Stevenson @davystevenson Cascadia Ruby 2014

  155. Resources Benchmark IPS - github.com/evanphx/benchmark-ips Benchmark BigO - github.com/davy/benchmark-bigo Terraformer.rb

    - github.com/esripdx/terraformer-ruby ! Benchmark Terraformer Results- github.com/davy/ benchmark-terraformer ! Maps by Leaflet - github.com/Leaflet/Leaflet extended with Esri Leaflet - github.com/Esri/esri-leaflet and Leaflet Ajax - github.com/calvinmetcalf/leaflet-ajax
  156. Attributions From the Noun Project: ! “Erlenmeyer Flask” by Emily

    van den Heever “Stopwatch” by Irit Barzily “Diamond” by Ryan Beck “Question” by Brennan Novak “Check Mark” by Brennan Novak “Arrow” by José Campos
  157. Attributions Backgrounds: Clouds by Joshua Rappeneker First Stars by Paul

    Chaloner grassy by cometwendy Drops on Bright Orange Flower by A Guy Taking Pictures Lightning by Stuart Williams Beach Palm Trees by Grand Velas Riviera Maya ! !