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

[RubyConf] Digesting MRI by Studying Alternative Ruby Implementations

[RubyConf] Digesting MRI by Studying Alternative Ruby Implementations

Pointers, managing memory and static typing - writing C code is hard! However, most programming languages, including Matz's Ruby Interpreter (MRI), are implemented in a low level programming language. So you think without knowing these concepts, you can not contribute to Ruby? Wrong! Although MRI is implemented in C, fortunately there are Ruby's in Java, Go and even Ruby itself.

If you ever wanted to learn about Ruby internals without being a C expert, this talk is for you. Join me on my journey of re-implementing hash maps in JRuby, breaking bundler and actually learn to write (some) C code.

Efa305e8e1b4eca01dd9f5c8bea4294b?s=128

Christian Bruckmayer

November 20, 2019
Tweet

Transcript

  1. https://www.flickr.com/photos/altus Story time

  2. What defines a “Senior Developer”?

  3. Experience

  4. Leadership

  5. Mentor

  6. “Masters” Programming

  7. “Masters” Ruby

  8. How?

  9. Ruby is a computer program

  10. Digesting MRI by Studying Alternative Ruby Implementations

  11. Hello! I am 
 Christian Bruckmayer
 
 @bruckmayer
 bruckmayer.net

  12. Bristol

  13. None
  14. MRI Rubinius / Opal JRuby

  15. MRI / CRuby 1993 Programming language: C Known for: Reference

    implementation, Matz
  16. “Masters” Ruby

  17. Ruby is a computer program

  18. None
  19. None
  20. array = [1,2,3,4,5,6,7,8,9] # => [1, 2, 3, 4, 5,

    6, 7, 8, 9] puts array.minmax # => [1, 9] puts [array.min, array.max] # => [1, 9]
  21. require 'benchmark-driver' Benchmark.driver do |x| x.prelude <<~RUBY arr = (1..1000).map

    { rand } RUBY x.report %{ arr.minmax } x.report %{ [arr.min, arr.max] } end
  22. Warming up -------------------------------------- arr.minmax 36.970k i/s - 40.436k times in

    1.093742s (27.05μs/i) [arr.min, arr.max] 67.366k i/s - 72.435k times in 1.075249s (14.84μs/i) Calculating ------------------------------------- arr.minmax 36.784k i/s - 110.910k times in 3.015196s (27.19μs/i) [arr.min, arr.max] 67.324k i/s - 202.097k times in 3.001869s (14.85μs/i) Comparison: [arr.min, arr.max] : 67323.7 i/s arr.minmax : 36783.7 i/s - 1.83x slower
  23. None
  24. None
  25. None
  26. Embrace Failure

  27. Challenged

  28. Inspired

  29. MRI Rubinius / Opal JRuby

  30. Rubinius 2006 Programming language: Ruby, C, C++ Known for: Implemented

    as much as possible in Ruby
  31. Opal 2010 Programming language: Java Script, Ruby Known for: Ruby

    to Java Script, Ruby in the Browser
  32. 2.5

  33. puts "Hello RubyConf”.delete_prefix("Hello ") # RubyConf

  34. def delete_prefix(prefix) prefix = Rubinius::Type.check_convert_type prefix, String, :to_str return self[prefix.size..-1]

    if self.start_with?(prefix) dup end Rubinius
  35. def delete_prefix(prefix) %x{ if (!prefix.$$is_string) { #{prefix = Opal.coerce_to(prefix, String,

    :to_str)} } if (self.slice(0, prefix.length) === prefix) { return self.$$cast(self.slice(prefix.length)); } else { return self; } } end Opal
  36. “Hello RubyConf”.delete_prefix(:Hello) # TypeError (no implicit conversion of Symbol into

    String)
  37. def delete_prefix(prefix) prefix = Rubinius::Type.check_convert_type prefix, String, :to_str return self[prefix.size..-1]

    if self.start_with?(prefix) dup end Rubinius
  38. “Hello RubyConf”.delete_prefix(:Hello) # TypeError (no implicit conversion of Symbol into

    String) :Hello.to_s # “Hello" :Hello.to_str # NoMethodError (undefined method `to_str' for :foo:Symbol)
  39. None
  40. vs prefix = :Hello.to_s “Hello RubyConf”. delete_prefix(prefix) class Prefix def

    to_str “Hello" end end “Hello RubyConf”. delete_prefix(Prefix.new) Explicit Implicit
  41. class Path def to_str "/Christian" end end puts "home" +

    Path.new # home/Christian
  42. class Path def to_s “/Christian" end end puts "home" +

    Path.new # Traceback (most recent call last): # `+': no implicit conversion of Path into String (TypeError)
  43. None
  44. def delete_prefix(prefix) prefix = Rubinius::Type.check_convert_type prefix, String, :to_str return self[prefix.size..-1]

    if self.start_with?(prefix) dup end Rubinius
  45. Artichoke 2019 Programming language: Rust, Ruby Known for: Ruby runtime

    in Rust
  46. def delete_prefix(prefix) return self[prefix.length..-1] if start_with?(prefix) self end Artichoke

  47. None
  48. string = "Hello" => "Hello" string.delete("a").equal?(string) => false string.chomp.equal?(string) =>

    false string.gsub(/[a]/, 'a').equal?(string) => false
  49. Three Lines of Code

  50. MRI Rubinius / Opal JRuby

  51. JRuby 2001 Programming language: Java Known for: JVM, Concurrency, Fast

  52. It aims to be a complete, correct and fast implementation

    of Ruby
  53. None
  54. ~40 % Faster

  55. None
  56. None
  57. Rubinius 2006 Programming language: Ruby, C, C++ Known for: Implemented

    as much as possible in Ruby
  58. /* Find an entry with KEY in table TAB. Return

    non-zero if we found it. Set up *RESULT to the found table entry key. */ int st_get_key(st_table *tab, st_data_t key, st_data_t *result) { st_index_t bin; st_hash_t hash = do_hash(key, tab); rb_p(key); .... }
  59. make: *** [encdb.h] Error 1 make: *** Waiting for unfinished

    jobs.... ../ruby/tool/transform_mjit_header.rb:5: [BUG] Segmentation fault at 0x0000000000000093 ruby 2.7.0dev (2019-09-03T19:17:53Z implement-ary-minmax ebebc4b80d) [x86_64-darwin18] -- Crash Report log information -------------------------------------------- See Crash Report log file under the one of following: * ~/Library/Logs/DiagnosticReports * /Library/Logs/DiagnosticReports for more details. Don't forget to include the above Crash Report log file in bug reports.
  60. None
  61. hash = {} hash[:key] = "value" hash[:key] # "value"

  62. class Hash def initialize @buckets = Array.new(7) { [] }

    end private attr_reader :buckets end Separate Chaining
  63. class Hash def initialize @buckets = Array.new(7) { [] }

    end private attr_reader :buckets end Separate Chaining
  64. def []=(key, value) if find(key) find(key).value = value else bucket(key)

    << Entry.new(key, value) end end class Entry < Struct.new(:key, :value) end Separate Chaining
  65. def []=(key, value) if find(key) find(key).value = value else bucket(key)

    << Entry.new(key, value) end end class Entry < Struct.new(:key, :value) end Separate Chaining
  66. def find(key) bucket(key).find do |entry| entry.key == key end end

    Separate Chaining
  67. def bucket(key) buckets[index(key)] end def index(key) key.hash % buckets.length end

    Separate Chaining
  68. Cache Locality

  69. If a particular storage location is referenced […], then it

    is likely that nearby memory locations will be referenced in the near future. Wikipedia
  70. [1,2,3,4,5,6,7,8,9].each do |i| puts i end

  71. class Hash def initialize @buckets = Array.new(7) { [] }

    end private attr_reader :buckets end Open Addressing
  72. def []=(key, value) if find(key) find(key).value = value else bucket

    = bucket(key) buckets[bucket] = Entry.new(key, value) end end Open Addressing
  73. def []=(key, value) if find(key) find(key).value = value else bucket

    = bucket(key) buckets[bucket] = Entry.new(key, value) end end Open Addressing
  74. def find(key) buckets[bucket(key)] end Open Addressing

  75. def bucket(key) index = index(key) entry = buckets[index] while entry

    != nil && entry.key != key index += next_index(index) entry = buckets[index] end index end Open Addressing
  76. def next_index(index) index + 1 end Open Addressing

  77. Refactoring

  78. The Pragmatic Programmer “Different languages solve the same problems in

    different ways”
  79. Release Early, Release Often

  80. Jessica Kerr “Ideas become bigger when you share them”

  81. def []=(key, value) if find(key) find(key).value = value else bucket

    = bucket(key) buckets[bucket] = Entry.new(key, value) end end Open Addressing
  82. def []=(key, value) if find(key) find(key).value = value else bucket

    = bucket(key) buckets[bucket] = key buckets[bucket + 1] = value end end Open Addressing
  83. Akira Matsuda “Performance optimisation is a game in which you

    want to achieve a highscore”
  84. None
  85. 2 months

  86. +682 LOC - 426 LOC

  87. None
  88. Prototype

  89. Ask for Help

  90. MRI Rubinius / Opal JRuby

  91. What defines a “Senior Developer”?

  92. “Masters” Ruby

  93. Never stop learning

  94. Community

  95. Thank You!
 
 @bruckmayer
 bruckmayer.net