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

Ruby Objects, A Walkabout

hone
March 05, 2015

Ruby Objects, A Walkabout

In Ruby, it's easy to create classes, methods, and objects. For instance, did you know that a hello world sinatra app uses 43 classes, 155 methods, and dispatches 548 methods for a single request. In this talk, we're going use TracePoint API to look at how you can find out this information among other things. Additionally, now that we have all these objects from our Ruby app, the evolving ObjectSpace API we can glean information about rough object size in memory. Let's take a walk through objects in Ruby.

hone

March 05, 2015
Tweet

More Decks by hone

Other Decks in Programming

Transcript

  1. enable trace set_trace_func proc {|event, file, line, id, binding, klass|

    printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, klass }
  2. code to trace set_trace_func proc {|event, file, line, id, binding,

    klass| printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, klass } class Foo def bar "baz" end end Foo.new.bar
  3. disable trace set_trace_func proc {|event, file, line, id, binding, klass|

    printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, klass } class Foo def bar "baz" end end Foo.new.bar set_trace_func(nil) Foo.new
  4. c-return trace.rb:1 set_trace_func Kernel line trace.rb:5 c-call trace.rb:5 inherited Class

    c-return trace.rb:5 inherited Class class trace.rb:5 line trace.rb:6 c-call trace.rb:6 method_added Module c-return trace.rb:6 method_added Module end trace.rb:9 line trace.rb:11 c-call trace.rb:11 new Class c-call trace.rb:11 initialize BasicObject c-return trace.rb:11 initialize BasicObject c-return trace.rb:11 new Class call trace.rb:6 bar Foo line trace.rb:7 bar Foo return trace.rb:8 bar Foo line trace.rb:13 c-call trace.rb:13 set_trace_func Kernel
  5. c-return trace.rb:1 set_trace_func Kernel line trace.rb:5 c-call trace.rb:5 inherited Class

    c-return trace.rb:5 inherited Class class trace.rb:5 line trace.rb:6 c-call trace.rb:6 method_added Module c-return trace.rb:6 method_added Module end trace.rb:9 line trace.rb:11 c-call trace.rb:11 new Class c-call trace.rb:11 initialize BasicObject c-return trace.rb:11 initialize BasicObject c-return trace.rb:11 new Class call trace.rb:6 bar Foo line trace.rb:7 bar Foo return trace.rb:8 bar Foo line trace.rb:13 c-call trace.rb:13 set_trace_func Kernel
  6. set_trace_func proc {|event, file, line, id, binding, klass| printf "%8s

    %s:%-2d %10s %8s\n", event, file, line, id, klass } class Foo def bar "baz" end end Foo.new.bar set_trace_func(nil) Foo.new
  7. c-return trace.rb:1 set_trace_func Kernel line trace.rb:5 c-call trace.rb:5 inherited Class

    c-return trace.rb:5 inherited Class class trace.rb:5 line trace.rb:6 c-call trace.rb:6 method_added Module c-return trace.rb:6 method_added Module end trace.rb:9 line trace.rb:11 c-call trace.rb:11 new Class c-call trace.rb:11 initialize BasicObject c-return trace.rb:11 initialize BasicObject c-return trace.rb:11 new Class call trace.rb:6 bar Foo line trace.rb:7 bar Foo return trace.rb:8 bar Foo line trace.rb:13 c-call trace.rb:13 set_trace_func Kernel
  8. set_trace_func proc {|event, file, line, id, binding, klass| printf "%8s

    %s:%-2d %10s %8s\n", event, file, line, id, klass } class Foo def bar "baz" end end Foo.new.bar set_trace_func(nil) Foo.new
  9. c-return trace.rb:1 set_trace_func Kernel line trace.rb:5 c-call trace.rb:5 inherited Class

    c-return trace.rb:5 inherited Class class trace.rb:5 line trace.rb:6 c-call trace.rb:6 method_added Module c-return trace.rb:6 method_added Module end trace.rb:9 line trace.rb:11 c-call trace.rb:11 new Class c-call trace.rb:11 initialize BasicObject c-return trace.rb:11 initialize BasicObject c-return trace.rb:11 new Class call trace.rb:6 bar Foo line trace.rb:7 bar Foo return trace.rb:8 bar Foo line trace.rb:13 c-call trace.rb:13 set_trace_func Kernel
  10. set_trace_func proc {|event, file, line, id, binding, klass| printf "%8s

    %s:%-2d %10s %8s\n", event, file, line, id, klass } class Foo def bar "baz" end end Foo.new.bar set_trace_func(nil) Foo.new
  11. c-return trace.rb:1 set_trace_func Kernel line trace.rb:5 c-call trace.rb:5 inherited Class

    c-return trace.rb:5 inherited Class class trace.rb:5 line trace.rb:6 c-call trace.rb:6 method_added Module c-return trace.rb:6 method_added Module end trace.rb:9 line trace.rb:11 c-call trace.rb:11 new Class c-call trace.rb:11 initialize BasicObject c-return trace.rb:11 initialize BasicObject c-return trace.rb:11 new Class call trace.rb:6 bar Foo line trace.rb:7 bar Foo return trace.rb:8 bar Foo line trace.rb:13 c-call trace.rb:13 set_trace_func Kernel
  12. c-return trace.rb:1 set_trace_func Kernel line trace.rb:5 c-call trace.rb:5 inherited Class

    c-return trace.rb:5 inherited Class class trace.rb:5 line trace.rb:6 c-call trace.rb:6 method_added Module c-return trace.rb:6 method_added Module end trace.rb:9 line trace.rb:11 c-call trace.rb:11 new Class c-call trace.rb:11 initialize BasicObject c-return trace.rb:11 initialize BasicObject c-return trace.rb:11 new Class call trace.rb:6 bar Foo line trace.rb:7 bar Foo return trace.rb:8 bar Foo line trace.rb:13 c-call trace.rb:13 set_trace_func Kernel
  13. set_trace_func proc {|event, file, line, id, binding, klass| printf "%8s

    %s:%-2d %10s %8s\n", event, file, line, id, klass } class Foo def bar "baz" end end Foo.new.bar set_trace_func(nil) Foo.new
  14. c-return trace.rb:1 set_trace_func Kernel line trace.rb:5 c-call trace.rb:5 inherited Class

    c-return trace.rb:5 inherited Class class trace.rb:5 line trace.rb:6 c-call trace.rb:6 method_added Module c-return trace.rb:6 method_added Module end trace.rb:9 line trace.rb:11 c-call trace.rb:11 new Class c-call trace.rb:11 initialize BasicObject c-return trace.rb:11 initialize BasicObject c-return trace.rb:11 new Class call trace.rb:6 bar Foo line trace.rb:7 bar Foo return trace.rb:8 bar Foo line trace.rb:13 c-call trace.rb:13 set_trace_func Kernel
  15. set_trace_func proc {|event, file, line, id, binding, klass| printf "%8s

    %s:%-2d %10s %8s\n", event, file, line, id, klass } class Foo def bar "baz" end end Foo.new.bar set_trace_func(nil) Foo.new
  16. c-return trace.rb:1 set_trace_func Kernel line trace.rb:5 c-call trace.rb:5 inherited Class

    c-return trace.rb:5 inherited Class class trace.rb:5 line trace.rb:6 c-call trace.rb:6 method_added Module c-return trace.rb:6 method_added Module end trace.rb:9 line trace.rb:11 c-call trace.rb:11 new Class c-call trace.rb:11 initialize BasicObject c-return trace.rb:11 initialize BasicObject c-return trace.rb:11 new Class call trace.rb:6 bar Foo line trace.rb:7 bar Foo return trace.rb:8 bar Foo line trace.rb:13 c-call trace.rb:13 set_trace_func Kernel
  17. set_trace_func proc {|event, file, line, id, binding, klass| printf "%8s

    %s:%-2d %10s %8s\n", event, file, line, id, klass } class Foo def bar "baz" end end Foo.new.bar set_trace_func(nil) Foo.new
  18. tracepoint = TracePoint.new do |tp| printf "%8s %s:%-2d %10s %8s\n",

    tp.event, tp.path, tp.lineno, tp.method_id, tp.defined_class end tracepoint.enable do class Foo def bar "baz" end end Foo.new.bar end Foo.new
  19. tracepoint = TracePoint.new do |tp| printf "%8s %s:%-2d %10s %8s\n",

    tp.event, tp.path, tp.lineno, tp.method_id, tp.defined_class end tracepoint.enable do class Foo def bar "baz" end end Foo.new.bar end Foo.new
  20. tracepoint = TracePoint.new do |tp| printf "%8s %s:%-2d %10s %8s\n",

    tp.event, tp.path, tp.lineno, tp.method_id, tp.defined_class end tracepoint.enable do class Foo def bar "baz" end end Foo.new.bar end Foo.new
  21. b_call tracepoint.rb:5 line tracepoint.rb:6 c_call tracepoint.rb:6 inherited Class c_return tracepoint.rb:6

    inherited Class class tracepoint.rb:6 line tracepoint.rb:7 c_call tracepoint.rb:7 method_added Module c_return tracepoint.rb:7 method_added Module end tracepoint.rb:10 line tracepoint.rb:12 c_call tracepoint.rb:12 new Class c_call tracepoint.rb:12 initialize BasicObject c_return tracepoint.rb:12 initialize BasicObject c_return tracepoint.rb:12 new Class call tracepoint.rb:7 bar Foo line tracepoint.rb:8 bar Foo return tracepoint.rb:9 bar Foo b_return tracepoint.rb:13
  22. TracePoint Middleware def call(env) stats = {} response = nil

    trace = TracePoint.new(:call) do |tp| stats[tp.defined_class] ||= {} stats[tp.defined_class][tp.method_id] ||= 0 stats[tp.defined_class][tp.method_id] += 1 end trace.enable { response = @app.call(env) } ... end * https://gist.github.com/mattetti/5097206
  23. TracePoint Middleware def call(env) ... puts env['PATH_INFO'] puts "#{stats.keys.size} classes

    used" puts "#{stats.map{|k,v| v.keys}.flatten.size} methods used" puts "#{stats.map{|k,v| v.values}.flatten.inject(0) {|sum, num| sum + num }} methods dispatched" File.open("tmp/#{env['PATH_INFO'].gsub('/', '_')}_req_stats. json", "w"){|f| f << stats.to_json } puts "" response end * https://gist.github.com/mattetti/5097206
  24. rack require 'rack/tracepoint' class RubyVersion def self.version `ruby -v` end

    end use Rack::TracePoint::Middleware run Proc.new {|env| [200, {"Content-Type" => "text/plain"}, [RubyVersion.version] ] }
  25. rails # app/controllers/welcome_controller.rb class WelcomeController < ApplicationController def index @ruby_version

    = `ruby -v` end end # app/views/welcome/index.html.erb <%= @ruby_version %>
  26. { "Sinatra::Base": { "initialize": 1, "call": 1, "call!": 1, "indifferent_params":

    1, "indifferent_hash": 1, "settings": 9, "force_encoding": 1, "invoke": 3, "dispatch!": 1, "filter!": 4, "route!": 1, "process_route": 1, "route_eval": 1, "error_block!": 1 } }
  27. { "ERB::Compiler::Buffer#push": 133, "ERB::Compiler#add_put_cmd": 66, "ERB::Compiler#content_dump": 66, "ERB::Compiler#add_insert_cmd": 34, "Rack::Builder#use":

    13, "#<Class:Sinatra::Base>#settings": 10, "Sinatra::Base#settings": 9, "Rack::Utils::HeaderHash#[]": 9, "#<Class:Sinatra::Application>#app_file": 8, "Rack::Utils::HeaderHash#[]=": 7, "Rack::Protection::Base#initialize": 6, "Rack::Protection::Base#default_options": 6, "#<Class:Sinatra::Base>#environment": 4, "#<Class:Sinatra::Base>#root": 4, "Sinatra::Base#filter!": 4, "Sinatra::Base#invoke": 3 ...
  28. ObjectSpace.count_objects { "TOTAL": 30161, "FREE": 677, "T_OBJECT": 70, "T_CLASS": 616,

    "T_MODULE": 37, "T_FLOAT": 7, "T_STRING": 10950, "T_REGEXP": 77, "T_ARRAY": 1629, "T_HASH": 63, "T_STRUCT": 9, "T_BIGNUM": 2, "T_FILE": 29, "T_DATA": 983, "T_MATCH": 26, ... }
  29. { "String": 8199, "RubyVM::InstructionSequence": 750, "Array": 511, "Class": 288, "Encoding":

    100, "Regexp": 77, "Hash": 44, "Module": 37, "MatchData": 20, "File": 18, "Gem::Requirement": 15, "Gem::Version": 15, "Gem::StubSpecification": 11, "Proc": 10, "Range": 9, "Gem::StubSpecification::StubLine": 8, "Float": 7
  30. Object Introspection require 'objspace' os = ObjectSpace o = nil

    os.trace_object_allocations do class Foo def bar "baz" end end o = Foo.new.bar end printf "%s:%-2d %10s %8s\n", os.allocation_sourcefile(o), os. allocation_sourceline(o), os.allocation_method_id(o), os. allocation_class_path(o)
  31. require 'objspace' os = ObjectSpace o = nil os.trace_object_allocations do

    class Foo def bar "baz" end end o = Foo.new.bar end printf "%s:%-2d %10s %8s\n", os.allocation_sourcefile(o), os. allocation_sourceline(o), os.allocation_method_id(o), os. allocation_class_path(o)
  32. require 'objspace' os = ObjectSpace o = nil os.trace_object_allocations do

    class Foo def bar "baz" end end o = Foo.new.bar end printf "%s:%-2d %10s %8s\n", os.allocation_sourcefile(o), os. allocation_sourceline(o), os.allocation_method_id(o), os. allocation_class_path(o)
  33. require 'objspace' os = ObjectSpace o = nil os.trace_object_allocations do

    class Foo def bar "baz" end end o = Foo.new.bar end printf "%s:%-2d %10s %8s\n", os.allocation_sourcefile(o), os. allocation_sourceline(o), os.allocation_method_id(o), os. allocation_class_path(o)
  34. Object Size Counting require 'objspace' Metadata = Struct.new(:class, :sourcefile, :sourceline,

    : class_path, :method_id, :memsize) objects = [] result = {} def walk_objects(result) ObjectSpace.each_object do |o| result[o.object_id] = Metadata.new( ObjectSpace.allocation_class_path(o), ObjectSpace.allocation_sourcefile(o), ObjectSpace.allocation_sourceline(o), ObjectSpace.allocation_class_path(o), ObjectSpace.allocation_method_id(o), ObjectSpace.memsize_of(o) ) end end
  35. Object Size Counting ObjectSpace.trace_object_allocations do a = Foo.new.bar b =

    Baz.new.foo walk_objects(result) end result.to_a.sort {|a,b| b[1].memsize <=> a[1].memsize }. to_h.each do |object_id, s| next if s.class_path.nil? printf "%s:%-2d %5s#%s %d\n", s.sourcefile, s.sourceline, s. class_path, s.method_id, s.memsize end
  36. Made Up Algorithm def swap_elements(a) pivot = a.length/2 (0..(pivot-1)).each do

    |i| a[i], a[i + pivot] = a[i + pivot], a[i] end end N = 10000; arr = N.times.map{|x| (N-x).to_s } swap_elements(arr)
  37. ObjectSpace::AllocationTracer.trace def swap_elements(a) pivot = a.length/2 (0..(pivot-1)).each do |i| a[i],

    a[i + pivot] = a[i + pivot], a[i] end end pp ObjectSpace::AllocationTracer.trace { N = 10000; arr = N.times.map{|x| (N-x).to_s } swap_elements(arr) }
  38. {["algorithm.rb", 15]=>[10004, 0, 0, 0, 0, 0], ["algorithm.rb", 6]=>[1, 0,

    0, 0, 0, 0], ["algorithm.rb", 7]=>[5000, 0, 0, 0, 0, 0]}
  39. {["algorithm.rb", 15]=>[10004, 0, 0, 0, 0, 0], ["algorithm.rb", 6]=>[1, 0,

    0, 0, 0, 0], ["algorithm.rb", 7]=>[5000, 0, 0, 0, 0, 0]}
  40. def swap_elements(a) pivot = a.length/2 (0..(pivot-1)).each do |i| a[i], a[i

    + pivot] = a[i + pivot], a[i] end end pp ObjectSpace::AllocationTracer.trace { N = 10000; arr = N.times.map{|x| (N-x).to_s } swap_elements(arr) }
  41. {["algorithm.rb", 15]=>[10004, 0, 0, 0, 0, 0], ["algorithm.rb", 6]=>[1, 0,

    0, 0, 0, 0], ["algorithm.rb", 7]=>[5000, 0, 0, 0, 0, 0]}
  42. def swap_elements(a) pivot = a.length/2 (0..(pivot-1)).each do |i| a[i], a[i

    + pivot] = a[i + pivot], a[i] end end pp ObjectSpace::AllocationTracer.trace { N = 10000; arr = N.times.map{|x| (N-x).to_s } swap_elements(arr) }
  43. def swap_elements(a) pivot = a.length/2 (0..(pivot-1)).each do |i| a[i], a[i

    + pivot] = a[i + pivot], a[i] end end ObjectSpace::AllocationTracer.setup(%i{path line type}) pp ObjectSpace::AllocationTracer.trace { N = 10000; arr = N.times.map{|x| (N-x).to_s } swap_elements(arr) }
  44. {["algorithm.rb", 15, :T_NODE]=>[2, 0, 2, 1, 1, 40], ["algorithm.rb", 7,

    :T_ARRAY]=>[5000, 0, 2511, 0, 1, 45560], ["algorithm.rb", 15, :T_DATA]=>[1, 0, 1, 1, 1, 0], ["algorithm.rb", 15, :T_ARRAY]=>[1, 0, 1, 1, 1, 0], ["algorithm.rb", 15, :T_STRING]=>[10000, 0, 10000, 1, 1, 0], ["algorithm.rb", 6, :T_STRUCT]=>[1, 0, 1, 1, 1, 0]}
  45. {["algorithm.rb", 15, :T_NODE]=>[2, 0, 2, 1, 1, 40], ["algorithm.rb", 7,

    :T_ARRAY]=>[5000, 0, 2511, 0, 1, 45560], ["algorithm.rb", 15, :T_DATA]=>[1, 0, 1, 1, 1, 0], ["algorithm.rb", 15, :T_ARRAY]=>[1, 0, 1, 1, 1, 0], ["algorithm.rb", 15, :T_STRING]=>[10000, 0, 10000, 1, 1, 0], ["algorithm.rb", 6, :T_STRUCT]=>[1, 0, 1, 1, 1, 0]}
  46. {["algorithm.rb", 15, :T_NODE]=>[2, 0, 2, 1, 1, 40], ["algorithm.rb", 7,

    :T_ARRAY]=>[5000, 0, 2511, 0, 1, 45560], ["algorithm.rb", 15, :T_DATA]=>[1, 0, 1, 1, 1, 0], ["algorithm.rb", 15, :T_ARRAY]=>[1, 0, 1, 1, 1, 0], ["algorithm.rb", 15, :T_STRING]=>[10000, 0, 10000, 1, 1, 0], ["algorithm.rb", 6, :T_STRUCT]=>[1, 0, 1, 1, 1, 0]}
  47. def swap_elements(a) pivot = a.length/2 (0..(pivot-1)).each do |i| a[i], a[i

    + pivot] = a[i + pivot], a[i] end end pp ObjectSpace::AllocationTracer.trace { N = 10000; arr = N.times.map{|x| (N-x).to_s } swap_elements(arr) }
  48. def swap_elements(a) pivot = a.length/2 (0..(pivot-1)).each do |i| x, y

    = a[i + pivot], a[i] a[i] = x a[i + pivot] = y end end pp ObjectSpace::AllocationTracer.trace { N = 10000; arr = N.times.map{|x| (N-x).to_s } swap_elements(arr) }