Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

Ruby Objects A Walkabout

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Terence Lee @hone02

Slide 5

Slide 5 text

Austin, TX

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

@schneems

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

zzak (@_zzak)

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

● TracePoint ● ObjectSpace ● Allocation Tracer Gem

Slide 17

Slide 17 text

TracePoint

Slide 18

Slide 18 text

Kernel#set_func_trace

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

events ● c-call ● c-return ● call ● return ● class ● end ● line ● raise

Slide 37

Slide 37 text

TracePoint

Slide 38

Slide 38 text

events ● b_call ● b_return ● thread_begin ● thread_end

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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] ] }

Slide 46

Slide 46 text

1 classes used 1 methods used 1 methods dispatched

Slide 47

Slide 47 text

sinatra require 'sinatra' require 'rack/tracepoint' use Rack::Tracepoint::Middleware get "/" do `ruby -v` end run Sinatra::Application

Slide 48

Slide 48 text

43 classes used 155 methods used 548 methods dispatched

Slide 49

Slide 49 text

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 %>

Slide 50

Slide 50 text

361 classes used 1193 methods used 8621 methods dispatched

Slide 51

Slide 51 text

{ "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 } }

Slide 52

Slide 52 text

{ "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, "##settings": 10, "Sinatra::Base#settings": 9, "Rack::Utils::HeaderHash#[]": 9, "##app_file": 8, "Rack::Utils::HeaderHash#[]=": 7, "Rack::Protection::Base#initialize": 6, "Rack::Protection::Base#default_options": 6, "##environment": 4, "##root": 4, "Sinatra::Base#filter!": 4, "Sinatra::Base#invoke": 3 ...

Slide 53

Slide 53 text

ObjectSpace

Slide 54

Slide 54 text

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, ... }

Slide 55

Slide 55 text

ObjectSpace#each_object require 'json' types_count = Hash.new(0) ObjectSpace.each_object do |o| types_count[o.class] += 1 end puts types_count.to_a.sort {|a, b| b[1] <=> a[1] }.to_h.to_json

Slide 56

Slide 56 text

{ "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

Slide 57

Slide 57 text

objspace.so

Slide 58

Slide 58 text

allocation tracer

Slide 59

Slide 59 text

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)

Slide 60

Slide 60 text

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)

Slide 61

Slide 61 text

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)

Slide 62

Slide 62 text

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)

Slide 63

Slide 63 text

objectspace.rb:8 bar Foo

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

memory_profiler.rb:28 Baz#foo 40 memory_profiler.rb:22 Foo#bar 40

Slide 67

Slide 67 text

$ gem install memory_profiler

Slide 68

Slide 68 text

Allocation Tracer Gem

Slide 69

Slide 69 text

$ gem install allocation_tracer

Slide 70

Slide 70 text

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)

Slide 71

Slide 71 text

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) }

Slide 72

Slide 72 text

{["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]}

Slide 73

Slide 73 text

File Info ["algorithm.rb", 15]=>[10004, 0, 0, 0, 0, 0]

Slide 74

Slide 74 text

Objects Created ["algorithm.rb", 15]=>[10004, 0, 0, 0, 0, 0]

Slide 75

Slide 75 text

Old Objects ["algorithm.rb", 15]=>[10004, 0, 0, 0, 0, 0]

Slide 76

Slide 76 text

Total Age ["algorithm.rb", 15]=>[10004, 0, 0, 0, 0, 0]

Slide 77

Slide 77 text

Min Age ["algorithm.rb", 15]=>[10004, 0, 0, 0, 0, 0]

Slide 78

Slide 78 text

Max Age ["algorithm.rb", 15]=>[10004, 0, 0, 0, 0, 0]

Slide 79

Slide 79 text

Memsize without RVALUE ["algorithm.rb", 15]=>[10004, 0, 0, 0, 0, 0]

Slide 80

Slide 80 text

{["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]}

Slide 81

Slide 81 text

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) }

Slide 82

Slide 82 text

{["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]}

Slide 83

Slide 83 text

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) }

Slide 84

Slide 84 text

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) }

Slide 85

Slide 85 text

{["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]}

Slide 86

Slide 86 text

{["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]}

Slide 87

Slide 87 text

{["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]}

Slide 88

Slide 88 text

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) }

Slide 89

Slide 89 text

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) }

Slide 90

Slide 90 text

{["algorithm.rb", 15]=>[10004, 0, 0, 0, 0, 0], ["algorithm.rb", 6]=>[1, 0, 0, 0, 0, 0],

Slide 91

Slide 91 text

Conclusion

Slide 92

Slide 92 text

Thank You