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.

Davy Stevenson

August 12, 2014
Tweet

More Decks by Davy Stevenson

Other Decks in Programming

Transcript

  1. 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)
  2. 1 2 3 4 5 6 7 8 9 Plejaden

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

    M45 by Carsten Frenzi
  4. 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
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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
  10. $ 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)
  11. $ 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)
  12. 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
  13. 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
  14. 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
  15. $ 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
  16. $ 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
  17. $ 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
  18. $ 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
  19. $ 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
  20. $ 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. 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
  26. 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
  27. $ 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
  28. Terraformer Ruby • github.com/esripdx/terraformer-ruby • A geometric toolkit for dealing

    with geometry, geography, formats • Can calculate Convex Hull
  29. 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
  30. 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
  31. 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
  32. 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
  33. 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
  34. 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
  35. 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
  36. 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
  37. 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
  38. 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
  39. 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
  40. Benchmark Terraformer • github.com/davy/benchmark-terraformer • Helper code • Benchmark code

    • Map Viewer • Example charts, geojson & raw data • Array benchmark code
  41. Fix

  42. 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
  43. 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
  44. 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 ! !