[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. 5.
  2. 8.
  3. 12.
  4. 13.
  5. 18.
  6. 19.
  7. 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]
  8. 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
  9. 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
  10. 23.
  11. 24.
  12. 25.
  13. 28.
  14. 32.

    2.5

  15. 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
  16. 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)
  17. 39.
  18. 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
  19. 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)
  20. 43.
  21. 47.
  22. 53.
  23. 55.
  24. 56.
  25. 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); .... }
  26. 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.
  27. 60.
  28. 62.

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

    end private attr_reader :buckets end Separate Chaining
  29. 63.

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

    end private attr_reader :buckets end Separate Chaining
  30. 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
  31. 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
  32. 69.

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

    is likely that nearby memory locations will be referenced in the near future. Wikipedia
  33. 71.

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

    end private attr_reader :buckets end Open Addressing
  34. 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
  35. 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
  36. 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
  37. 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
  38. 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
  39. 84.
  40. 85.
  41. 87.
  42. 88.
  43. 94.