Slide 1

Slide 1 text

THE future of ruby performance tooling @aq / GoRuCo 2014

Slide 2

Slide 2 text

Thanks for coming. Good Morning!

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Well, lets start with the philosophy at least. today’s agenda: More Practical, Less Philosophical

Slide 5

Slide 5 text

User Experience, but also $$$ Performance matters.

Slide 6

Slide 6 text

+ PERFORMANCE

Slide 7

Slide 7 text

Connecting Ruby Server HTML DL CSS DL JS DL Images JS onLoad() Done 0-10ms 10-30,000ms 30ms 150ms 100-50,000ms

Slide 8

Slide 8 text

Connecting Ruby Server HTML DL CSS DL JS DL Images Sorted by how much control we have to improve

Slide 9

Slide 9 text

Ruby Server Ruby/Stdlib Rails DB/Cache YOUR APPLICATION

Slide 10

Slide 10 text

So where do we start?

Slide 11

Slide 11 text

The Past of RUBY-CORE: Developers > Operators

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

THE FUTURE OF RUBY-CORE: INTROSPECTABILITY

Slide 14

Slide 14 text

Heres to the future cuz we got through the past

Slide 15

Slide 15 text

Multitudinous The future of ruby performance tooling

Slide 16

Slide 16 text

There is no BEST tool, either. Reminder: THERE IS NO ONE TOOL

Slide 17

Slide 17 text

Ruby PerformancE Character Profiles

Slide 18

Slide 18 text

• Specificity (Sp) • Impact (Im) • Difficulty of Operator Use (Op) • Readability (Rd) • Realtimedness (Rl) • Special Abilities (SA)

Slide 19

Slide 19 text

The Elves AS::Notifications + Friends

Slide 20

Slide 20 text

ActiveSupport::Notifications.publish("log_action.request_subscriber", data)

Slide 21

Slide 21 text

class StatsdSubscriber < ActiveSupport::LogSubscriber attr_reader :event ! def log_action(event) payload = event.payload[:controller_data] prefix = “controller.#{payload[:method]}.#{payload[:controller].to_s.gsub(‘::’,'_')}." %w{view db total}.each do |metric| value = payload["#{metric}_runtime".to_sym] Paperless::Statsd.timing("#{prefix}.#{metric}", value) if value Paperless::Statsd.timing("controller.#{metric}", value) if value end end ! end

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

AS:Notify Sp: 5-10 Im: 5-10 Op: 7 Rd: 2-10 Rl: 9 • Special Abilities: Graphs! • Good for: p90s, what _layers_ are slow? Trending data. • Not Good for: Giving you details about anything. Easy to misinterpret data.

Slide 24

Slide 24 text

The Warrior RblineproF

Slide 25

Slide 25 text

require 'rblineprof' def lineprof_block(options = {}, &block) profile = lineprof(/app/) do ret = yield end profile_text(profile, options) end

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

RblineproF Sp: 9 Im: 9 Op: 8 Rd: 7 Rl: 3 • Special Abilities: Amazing visibility into dev hotspots • Good for: Diving deep into slow actions. • Not Good for: Discovery or fixing of systematic issues. Real world numbers.

Slide 28

Slide 28 text

You have obtained a special weapon

Slide 29

Slide 29 text

Sword of Repeatability PPprofiler

Slide 30

Slide 30 text

$ ./script/ppprofiler ‘Event.last.as_json(:expand => true)’

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

PPprofiler +2 Op +2 Rd • Special Abilities: Tracks local improvements. • Good for: Golf. Sharing improvements with team. • Not Good for: Production. This is about local.

Slide 33

Slide 33 text

The Mage StackProF

Slide 34

Slide 34 text

Ruby Process (Unicorn) AC::Dispatch MyController::Create Template::Render Ar::Find

Slide 35

Slide 35 text

Ruby Process (Unicorn) StackProf.start rb_profile_frames() rb_profile_frames() rb_profile_frames() rb_profile_frames() StackProf.stop StackProf.dump

Slide 36

Slide 36 text

$ stackprof ~/Downloads/stackprof-cpu-1402024056.dump ================================== Mode: cpu(1000) Samples: 562 (0.35% miss rate) GC: 71 (12.63%) ================================== TOTAL (pct) SAMPLES (pct) FRAME 33 (5.9%) 33 (5.9%) ActiveRecord::Base.scoped_methods 30 (5.3%) 30 (5.3%) ActiveRecord::ConnectionAdapters::PostgreSQLAdapter#… 26 (4.6%) 26 (4.6%) ActiveSupport::CoreExtensions::Hash::Keys#assert_valid_keys 24 (4.3%) 24 (4.3%) block in ActiveRecord::ConnectionAdapters::..#execute 14 (2.5%) 14 (2.5%) block in ActiveSupport::Inflector#singularize 13 (2.3%) 12 (2.1%) block in ActiveSupport::Notifications::Fanout#listeners_for 12 (2.1%) 12 (2.1%) ActiveRecord::Reflection::AssociationReflection#klass 19 (3.4%) 12 (2.1%) ActiveRecord::ConnectionAdapters::ConnectionHandler#… 11 (2.0%) 11 (2.0%) block in ActiveRecord::Base.with_scope 797 (141.8%) 11 (2.0%) ActiveRecord::Base.with_scope 18 (3.2%) 8 (1.4%) ActiveRecord::Base.quote_bound_value 7 (1.2%) 7 (1.2%) Haml::Helpers#preserve 79 (14.1%) 7 (1.2%) block in ActiveRecord::Base.with_scope 6 (1.1%) 6 (1.1%) block (2 levels) in ActiveRecord::Base.connection_handler= 72 (12.8%) 6 (1.1%) block (2 levels) in ActiveRecord::Base.with_scope

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

StackProF Sp: 4 Im: 2 Op: 3 Rd: 4-6 Rl: 9 • Special Abilities: FLAMEGRAPHS • Good for: Finding systemic issues in production. • Not Good for: Detailed info about whats slow in YOUR code.

Slide 39

Slide 39 text

You have obtained a special weapon

Slide 40

Slide 40 text

Axe of Usability StackProF-RemotE

Slide 41

Slide 41 text

require 'stackprof/remote/middleware' ! enabled = proc do |env| magic_method || Rails.env.development? end ActionController::Dispatcher.middleware.use StackProf::Remote::Middleware, enabled: enabled, logger: Rails.logger

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 text

SP::Remote +4 Op +2 Rd +1 Rl • Special Abilities: Interactive StackProf • Good for: Easily introspecting a production server. • Not Good for: Development.

Slide 44

Slide 44 text

The Giant ObjecTSpacE

Slide 45

Slide 45 text

$ ./bin/rbtrace -p 8963 -e 'GC.start(full_mark: true); require “objspace”;\ ObjectSpace.dump_all(output: File.open(“heap.json","w"))' ! $ ls -lh heap.json -rw-r--r-- 1 aaronquint staff 87M Jun 16 21:52 heap.json

Slide 46

Slide 46 text

{"address":"0xf583b8", "type":"STRING", "class":"0xf11620", "frozen":true, "embedded":true, "bytesize":13, "value":"output_format", "encoding":"UTF-8", "flags":{"wb_protected":true, "old":true, "marked":true}} {"address":"0xf583e0", "type":"OBJECT", "class":"0x2471268", "frozen":true, "ivars":3, "references":["0xf58480"], "flags":{"wb_protected":true, "old":true, "marked":true}} {"address":"0xf58458", "type":"STRING", "class":"0xf11620", "frozen":true, "embedded":true, "bytesize":10, "value":"transforms", "encoding":"UTF-8", "flags":{"wb_protected":true, "old":true, "marked":true}} {"address":"0xf58480", "type":"STRING", "class":"0xf11620", "embedded":true, "bytesize":1, "value":"/", "encoding":"UTF-8", "flags":{"wb_protected":true, "old":true, "marked":true}} {"address":"0xf584d0", "type":"STRING", "class":"0xf11620", "frozen":true, "embedded":true, "bytesize":6, "value":"output", "encoding":"UTF-8", "flags":{"wb_protected":true, "old":true, "marked":true}} {"address":"0xf58570", "type":"STRING", "class":"0xf11620", "frozen":true, "embedded":true, "bytesize":11, "value":"progressive", "encoding":"UTF-8", "flags":{"wb_protected":true, "old":true, "marked":true}} {"address":"0xf58598", "type":"OBJECT", "class":"0x2470b10", "frozen":true, "ivars":3, "flags":{"wb_protected":true, "old":true, "marked":true}} {"address":"0xf58610", "type":"OBJECT", "class":"0x24714e8", "frozen":true, "ivars":3, "references":["0xf58638"], "flags":{"wb_protected":true, "old":true, "marked":true}} {"address":"0xf58638", "type":"STRING", "class":"0xf11620", "embedded":true, "bytesize":3, "value":"api", "encoding":"UTF-8", "flags":{"wb_protected":true, "old":true, "marked":true}} {"address":"0xf58700", "type":"OBJECT", "class":"0x24714e8", "frozen":true, "ivars":3, "references":["0xf58750"], "flags":{"wb_protected":true, "old":true, "marked":true}} {"address":"0xf58728", "type":"STRING", "class":"0xf11620", "frozen":true, "embedded":true, "fstring":true, "bytesize" 8, "value":"ASTERISK", "encoding":"US-ASCII", "flags":{"wb_protected":true, "old":true, "marked":true}} {"address":"0xf58750", "type":"STRING", "class":"0xf11620", "shared":true, "encoding":"UTF-8", "references": ["0xf5f1b8"], "flags":{"wb_protected":true, "old":true, "marked":true}} {"address":"0xf587a0", "type":"STRING", "class":"0xf11620", "frozen":true, "bytesize":24, "capacity":48, "value":"ActionController::CgiExt", "encoding":"US-ASCII", "memsize":49, "flags":{"wb_protected":true, "old":true, "marked":true}} {"address":"0xf587c8", "type":"STRING", "class":"0xf11620", "frozen":true, "embedded":true, "bytesize":5, "value":"strip", "encoding":"UTF-8", "flags":{"wb_protected":true, "old":true, "marked":true}} {"address":"0xf587f0", "type":"STRING", "class":"0xf11620", "frozen":true, "embedded":true, "fstring":true, "bytesize" 9, "value":"SEV_LABEL", "encoding":"US-ASCII", "flags":{"wb_protected":true, "old":true, "marked":true}} {"address":"0xf58840", "type":"OBJECT", "class":"0x2471268", "frozen":true, "ivars":3, "references":["0xf588e0"], "flags":{"wb_protected":true, "old":true, "marked":true}} {"address":"0xf588b8", "type":"STRING", "class":"0xf11620", "frozen":true, "embedded":true, "fstring":true, "bytesize" 6, "value":"DOLLAR", "encoding":"US-ASCII", "flags":{"wb_protected":true, "old":true, "marked":true}} {"address":"0xf588e0", "type":"STRING", "class":"0xf11620", "embedded":true, "bytesize":1, "value":"/", "encoding":"UTF-8", "flags":{"wb_protected":true, "old":true, "marked":true}} {"address":"0xf58930", "type":"STRING", "class":"0xf11620", "frozen":true, "embedded":true, "bytesize":12, "value":"version_name", "encoding":"UTF-8", "flags"

Slide 47

Slide 47 text

ObjecTSpacE Sp: 10 Im: 10 Op: 3 Rd:3 Rl: 2 • Special Abilities: Every. Object. • Good for: Potential for diagnosing memory issues. • Not Good for: Hard to fetch from running systems.

Slide 48

Slide 48 text

A WIP Palantir ObjspacEViewer

Slide 49

Slide 49 text

$ ./scripts/import heap.json Time: 00:12:22 |==============================================>> | 93% Progress

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

ObSViewer +2 Rd • Total WIP • The data is there, we just need better tools to gain analysis

Slide 52

Slide 52 text

No content

Slide 53

Slide 53 text

— The Strategy Use low impact tools to collect data from production and high impact tools to measure the relative improvements of iterative changes in development and staging.

Slide 54

Slide 54 text

THANKS! ! twitter: @aq code: github.com/quirkey ! rblineprof: github.com/tmm1/rblineprof stackprof: github.com/tmm1/stackprof ppprofiler: dev.paperlesspost.com stackprof-remote: github.com/quirkey/stackprof-remote objspace_viewer: github.com/quirkey/objspace_viewer