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

The (Ruby) Sims

Ba4b0b302231bffb0f685bbef25db0d6?s=47 Bryan Liles
December 09, 2013

The (Ruby) Sims

Ever thought about writing a simulator in Ruby?

Ba4b0b302231bffb0f685bbef25db0d6?s=128

Bryan Liles

December 09, 2013
Tweet

More Decks by Bryan Liles

Other Decks in Technology

Transcript

  1. The (Ruby) Sims (part 1)

  2. Introduction

  3. @bryanl

  4. None
  5. None
  6. None
  7. None
  8. None
  9. None
  10. None
  11. CootieSim2012

  12. Why RUBY?

  13. Stochastic vs Deterministic

  14. Define inputs Compute Analyze output

  15. SIMULATION_LENGTH = 100 PEOPLE_COUNT = 100 def create_people(people_count); end def

    report; end def simulate_to(count) 1.upto(count) {|i| yield i } end people = create_people(PEOPLE_COUNT) simulate_to(SIMULATION_LENGTH) do |iteration| # mish and mash people end report
  16. class Simulator def initialize(length, count) # ... end end s

    = Simulator.new 3650, 100 Define inputs
  17. class Simulator def simulate(iteration) create_pairs(iteration) transmit(iteration) progress_infections(iteration) end end Compute

  18. s = Simulator.new 3650, 100 s.run s.infected_people.each do |p| i

    = p.infections.to_a d = p.diseases.to_a puts "#{p} #{i} #{d}" end Analyze output
  19. Breakdown

  20. What’s in a day?

  21. Create new pair

  22. def create_pairs(iteration) @pairs += people_not_in_pairs(iteration) .shuffle .each_slice(2) .map{|x| Pair.new iteration,

    x} end
  23. Transmit cooties

  24. class Simulation def transmit(iteration) current_pairs(iteration).each{|pair| pair.contact(iteration)} end end class Pair

    def contact(iteration) @alpha.infect(iteration, @beta) @beta.infect(iteration, @alpha) end end class Person def infect(iteration, person); end def acquire(infection); end end
  25. Track infection progress

  26. class Person def progress_infections(iteration) @infections.each do |infection| if rand <=

    infection.chance_to_acquire_disease unless has_disease? infection.disease @diseases << infection.disease.new(iteration) end end end end end
  27. ... This isn’t going to be fast

  28. 28.23s user 0.10s system 99% cpu 28.333 total

  29. but why?

  30. %self total self wait child calls name 30.32 386.26 117.53

    0.00 268.72 21904 Array#each 28.08 180.05 108.88 0.00 71.18 78738195 Comparable#between? 21.74 84.29 84.29 0.00 0.00 78738195 Pair#duration 18.36 71.18 71.18 0.00 0.00 157476390 Fixnum#<=> 0.29 2.11 1.11 0.00 1.06 414957 Set#each 0.13 1.15 0.50 0.00 0.65 365100 Person#infected? 0.13 2.36 0.50 0.00 1.86 365000 Person#infect 0.12 0.65 0.48 0.00 0.17 365100 Set#empty?
  31. O(n)

  32. module Enumerable # create a thread and execute in parallel

    def parallel_each(&block) # create thread yield # cleanup end end
  33. gem install peach (YMMV)

  34. None
  35. 100 people

  36. 3650 iterations

  37. 365,000 “actions”

  38. 3,650,000 @ 1,000 36,500,000 @ 10,000 365,000,000 @ 100,000 3,650,000,000

    @ 1,000,000
  39. How do we make it faster?

  40. You can’t!!!

  41. 1. Run less code 2. Do more things at once

  42. MRI 1.9

  43. None
  44. Thread.new

  45. None
  46. JRuby

  47. None
  48. Thread.new

  49. None
  50. JRuby’s sweetspot is Hotspot

  51. 11.34s user 0.37s system 116% cpu 10.064 total

  52. Doing more than one thing at once with MRI

  53. Queues

  54. redis backed

  55. resque

  56. sidekiq

  57. memory backed

  58. girl_friday

  59. memory backed

  60. rabbitmq

  61. db backed

  62. rabbitmq

  63. socket based concurrency

  64. Ømq

  65. dcell (part 2)

  66. statistics

  67. mean median variance standard deviation σ

  68. How do i randomly pick an integer from 1-6?

  69. gem install vose

  70. v = Vose::AliasMethod.new [1,1,1,1,1,1] (1..10000).map{v.next}.inject(Hash.new{0}){ |m,n| m[n]+=1; m} (1..10000).map{rand(1..6)} .inject(Hash.new{0}){|m,n|

    m[n]+=1; m} VS
  71. What if the die is weighted?

  72. v = Vose::AliasMethod.new [1.2,1,1,1,1,1] (1..10000).map{v.next}.inject(Hash.new{0}){ |m,n| m[n]+=1; m} ????? VS

  73. graphing

  74. Trends

  75. None
  76. None
  77. library(ggplot2) runtime.csv <- read.csv("runtime.csv", header=T) mygg<- qplot(iteration, time, data=runtime.csv); mygg<-

    mygg + stat_smooth(method="lm") ggsave("runtime.png") dev.off() R
  78. Visualization

  79. None
  80. digraph g { "person59" -> "person5"; "person23" -> "person18"; "person72"

    -> "person23"; "person78" -> "person44"; "person78" -> "person59"; "person59" -> "person72"; "person1" -> "person78"; "person83" -> "person81"; "person59" -> "person83"; "person59" -> "person92"; }; Graphviz
  81. Building a simulator

  82. None
  83. None
  84. None
  85. ?!

  86. Shared Cooties?

  87. Making it fast

  88. Be kind to MRI’s GC

  89. Run more than one process

  90. Look outside of MRI

  91. irb(main):003:0> rand 1..7 TypeError: can't convert Range into Integer from

    org/jruby/RubyKernel.java:1580:in `rand' from (irb):3:in `evaluate' from org/jruby/RubyKernel.java:1070:in `eval' from org/jruby/RubyKernel.java:1395:in `loop' from org/jruby/RubyKernel.java:1178:in `catch' from org/jruby/RubyKernel.java:1178:in `catch' from /Users/bryan/.rbenv/versions/jruby-1.7.0-preview2/bin/irb:13:in `(root)' JRuby 1.7 pre2
  92. irb(main):001:0> rand 1..7 => 3 MRI 1.9.3

  93. other languages?

  94. The Ruby compromise

  95. Exploring CootieSim2012

  96. Thanks! @thunderboltlabs @bryanl