whysoslow 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,6 @@
1
+ pkg/*
2
+ .bundle
3
+ *.gem
4
+ *.log
5
+ .rvmrc
6
+ .rbenv-version
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify dependencies in whysoslow.gemspec
4
+ gemspec
5
+
6
+ gem 'rake', '~>0.9.2'
@@ -0,0 +1,25 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ whysoslow (0.0.1)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ ansi (1.4.2)
10
+ assert (0.7.3)
11
+ assert-view (~> 0.5)
12
+ assert-view (0.5.0)
13
+ ansi (~> 1.3)
14
+ undies (~> 2.0)
15
+ rake (0.9.2.2)
16
+ undies (2.2.0)
17
+
18
+ PLATFORMS
19
+ ruby
20
+
21
+ DEPENDENCIES
22
+ assert
23
+ bundler
24
+ rake (~> 0.9.2)
25
+ whysoslow!
@@ -0,0 +1,59 @@
1
+ = Whysoslow
2
+
3
+ == Description
4
+
5
+ Whysoslow is a little runner/printer I wrote to benchmark my Ruby code. It runs a block of code, collects run time measurements, and can snapshot memory usage information at various points during execution.
6
+
7
+ It has no tests, shockingly sparse docs, and is more of an experiment at this point. It comes as is for now so use if you see fit.
8
+
9
+ I will happily accept any pull requests for documentation, tests, extensions, and improvements.
10
+
11
+ == Installation
12
+
13
+ gem install whysoslow
14
+
15
+ == Usage
16
+
17
+ require 'whysoslow'
18
+
19
+ printer = Whysoslow::DefaultPrinter.new({
20
+ :title => "Bench Report Title",
21
+ :verbose => true
22
+ })
23
+
24
+ runner = Whysoslow::Runner.new(@printer)
25
+
26
+ runner.run do
27
+ # ... some long running script or loop
28
+ runner.snapshot("mem usage during run")
29
+ # ... some more long running script or loop
30
+ end
31
+
32
+ == Output: DefaultPrinter
33
+
34
+ The DefaultPrinter outputs to an io stream ($stdout by default). It uses Ansi to output columned memory usage snapshot data and measurement data.
35
+
36
+ == License
37
+
38
+ Copyright (c) 2012 Kelly Redding
39
+
40
+ Permission is hereby granted, free of charge, to any person
41
+ obtaining a copy of this software and associated documentation
42
+ files (the "Software"), to deal in the Software without
43
+ restriction, including without limitation the rights to use,
44
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
45
+ copies of the Software, and to permit persons to whom the
46
+ Software is furnished to do so, subject to the following
47
+ conditions:
48
+
49
+ The above copyright notice and this permission notice shall be
50
+ included in all copies or substantial portions of the Software.
51
+
52
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
53
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
54
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
55
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
56
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
57
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
58
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
59
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,7 @@
1
+ require 'assert/rake_tasks'
2
+ Assert::RakeTasks.for(:test)
3
+
4
+ require 'bundler'
5
+ Bundler::GemHelper.install_tasks
6
+
7
+ task :default => :build
@@ -0,0 +1,4 @@
1
+ module Whysoslow; end
2
+
3
+ require 'whysoslow/runner'
4
+ require 'whysoslow/default_printer'
@@ -0,0 +1,91 @@
1
+ require 'ansi'
2
+
3
+ module Whysoslow
4
+ class DefaultPrinter
5
+
6
+ def initialize(*args)
7
+ @opts, @ios = [
8
+ args.last.kind_of?(::Hash) ? args.pop : {},
9
+ args.pop || $stdout
10
+ ]
11
+ @ios.sync = true
12
+ @title = @opts[:title]
13
+ @verbose = @opts[:verbose]
14
+ end
15
+
16
+ def print(thing)
17
+ case thing
18
+ when :title
19
+ if @title
20
+ @ios.puts @title.to_s
21
+ @ios.puts '-'*(@title.to_s.length)
22
+ end
23
+ when :run_start
24
+ @ios.print "whysoslow? " if @verbose
25
+ when :snapshot
26
+ @ios.print '.' if @verbose
27
+ when :run_end
28
+ @ios.print "\n\n" if @verbose
29
+ when Whysoslow::Results
30
+ @ios << snapshots_table(thing)
31
+ @ios.puts
32
+ @ios << measurements_table(thing)
33
+ else
34
+ end
35
+ end
36
+
37
+ protected
38
+
39
+ def snapshots_table(results)
40
+ units = results.snapshot_units
41
+ tdata = [
42
+ results.snapshot_labels.collect { |l| "mem @ #{l}" },
43
+ results.snapshot_usages.collect { |u| "#{memory_s(u)} #{units}" },
44
+ results.snapshot_diffs.collect do |diff_perc|
45
+ diff = diff_perc.first
46
+ perc = diff_perc.last
47
+ if diff == '??'
48
+ "??"
49
+ elsif diff > 0
50
+ ANSI.red do
51
+ "+#{memory_s(diff.abs)} #{units}, #{perc_s(perc)}%"
52
+ end
53
+ else
54
+ ANSI.green do
55
+ "-#{memory_s(diff.abs)} #{units}, #{perc_s(perc)}%"
56
+ end
57
+ end
58
+ end
59
+ ].flatten
60
+ ANSI::Columns.new(tdata, {
61
+ :columns => 3,
62
+ :align => :left,
63
+ :padding => 1
64
+ }).to_s
65
+ end
66
+
67
+ def measurements_table(results)
68
+ units = results.measurement_units
69
+ tdata = [
70
+ ['', 'time'],
71
+ results.measurements.collect do |l_t|
72
+ [l_t.first, "#{l_t.last} #{units}"]
73
+ end
74
+ ].flatten
75
+ ANSI::Columns.new(tdata, :columns => 5, :align=>:left).to_s
76
+ end
77
+
78
+ def snapshots_label_col
79
+ results.snapshots.collect{|r| "mem @ #{r.label}" }
80
+ end
81
+
82
+ def memory_s(amount)
83
+ amount.round.to_s.rjust(4)
84
+ end
85
+
86
+ def perc_s(perc)
87
+ (perc*100).round.abs.to_s.rjust(3)
88
+ end
89
+
90
+ end
91
+ end
@@ -0,0 +1,34 @@
1
+ require 'benchmark'
2
+
3
+ module Whysoslow
4
+ class Measurement
5
+
6
+ MEASUREMENTS = [:user, :system, :total, :real]
7
+ attr_reader :units, :multiplier
8
+ attr_reader *MEASUREMENTS
9
+
10
+ def initialize(units='ms')
11
+ @user, @system, @total, @real = 0
12
+ @units, @multiplier = if units == 's'
13
+ ['s', 1]
14
+ else # MB
15
+ ['ms', 1000]
16
+ end
17
+ end
18
+
19
+ def benchmark(&block)
20
+ Benchmark.measure(&block).tap do |values|
21
+ measurments = values.to_s.strip.gsub(/[^\s|0-9|\.]/, '').split(/\s+/)
22
+ self.user, self.system, self.total, self.real = measurments
23
+ end
24
+ end
25
+
26
+ protected
27
+
28
+ def user=(value_in_secs); @user = value_in_secs.to_f * @multiplier; end
29
+ def system=(value_in_secs); @system = value_in_secs.to_f * @multiplier; end
30
+ def total=(value_in_secs); @total = value_in_secs.to_f * @multiplier; end
31
+ def real=(value_in_secs); @real = value_in_secs.to_f * @multiplier; end
32
+
33
+ end
34
+ end
@@ -0,0 +1,46 @@
1
+ module Whysoslow
2
+
3
+ class MemoryProfile
4
+
5
+ class Snapshot; end
6
+
7
+ attr_reader :snapshots, :divider
8
+ attr_accessor :units
9
+
10
+ def initialize(units='MB')
11
+ @snapshots = []
12
+ self.units = units
13
+ end
14
+
15
+ def units=(value)
16
+ @units, @divider = if value == 'KB'
17
+ ['KB', 1]
18
+ else # MB
19
+ ['MB', 1000]
20
+ end
21
+ end
22
+
23
+ def snapshot(label)
24
+ Snapshot.new(label, @divider).tap { |snap| @snapshots.push(snap) }
25
+ end
26
+
27
+ end
28
+
29
+ class MemoryProfile::Snapshot
30
+
31
+ attr_reader :label, :memory
32
+
33
+ def initialize(label, divider)
34
+ @label = label
35
+ @memory = capture_memory_usage(divider)
36
+ end
37
+
38
+ protected
39
+
40
+ def capture_memory_usage(divider)
41
+ ((`ps -o rss= -p #{$$}`.to_i) / divider.to_f)
42
+ end
43
+
44
+ end
45
+
46
+ end
@@ -0,0 +1,67 @@
1
+ require 'whysoslow/measurement'
2
+ require 'whysoslow/memory_profile'
3
+
4
+ module Whysoslow
5
+ class Results
6
+
7
+ attr_reader :desc
8
+
9
+ def initialize(desc, time_unit, memory_unit)
10
+ @desc = desc
11
+ @measurement = Measurement.new(time_unit)
12
+ @memory_profile = MemoryProfile.new(memory_unit)
13
+ end
14
+
15
+ def benchmark(*args, &block)
16
+ @measurement.benchmark(*args, &block)
17
+ end
18
+
19
+ def snapshot(*args)
20
+ @memory_profile.snapshot(*args)
21
+ end
22
+
23
+ def snapshots
24
+ @snapshots ||= @memory_profile.snapshots
25
+ end
26
+
27
+ def snapshot_units
28
+ @memory_profile.units
29
+ end
30
+
31
+ def snapshot_labels
32
+ snapshots.collect{|s| s.label }
33
+ end
34
+
35
+ def snapshot_usages
36
+ snapshots.collect{|s| s.memory }
37
+ end
38
+
39
+ def snapshot_diffs
40
+ prev = nil
41
+ snapshots.inject([]) do |diffs, snapshot|
42
+ if prev.nil? || prev.memory == 0
43
+ diffs << ['??', '??']
44
+ else
45
+ diff = snapshot.memory - prev.memory
46
+ diffs << [
47
+ diff,
48
+ diff.to_f / prev.memory.to_f
49
+ ]
50
+ end
51
+ prev = snapshot
52
+ diffs
53
+ end
54
+ end
55
+
56
+ def measurements
57
+ @measurements ||= Measurement::MEASUREMENTS.collect do |m|
58
+ [m, @measurement.send(m)]
59
+ end
60
+ end
61
+
62
+ def measurement_units
63
+ @measurement.units
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,37 @@
1
+ require 'whysoslow/results'
2
+
3
+ module Whysoslow
4
+
5
+ class Runner
6
+
7
+ attr_accessor :desc, :verbose, :time_unit, :memory_unit
8
+
9
+ def initialize(printer, opts={})
10
+ @printer = printer
11
+ @time_unit = opts[:time_unit]
12
+ @memory_unit = opts[:memory_unit]
13
+ @results = nil
14
+ end
15
+
16
+ def run(&block)
17
+ @printer.print :title
18
+ @printer.print(Results.new(@desc, @time_unit, @memory_unit).tap do |results|
19
+ @results = results
20
+ @printer.print :run_start
21
+ self.snapshot('start')
22
+ @results.benchmark(&block)
23
+ self.snapshot('finish')
24
+ @printer.print :run_end
25
+ @results = nil
26
+ end)
27
+ end
28
+
29
+ def snapshot(*args)
30
+ raise RuntimeError, "no active results being gathered - be sure and call snapshot during a run session" if @results.nil?
31
+ @results.snapshot(*args)
32
+ @printer.print :snapshot
33
+ end
34
+
35
+ end
36
+ end
37
+
@@ -0,0 +1,3 @@
1
+ module Whysoslow
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,5 @@
1
+ # this file is automatically required in when you require 'assert' in your tests
2
+ # put test helpers here
3
+
4
+ # add root dir to the load path
5
+ $LOAD_PATH.unshift(File.expand_path("../..", __FILE__))
@@ -0,0 +1,9 @@
1
+ require 'assert/setup'
2
+
3
+ # this file is required in when the 'irb' rake test is run.
4
+ # b/c 'assert/setup' is required above, the test helper will be
5
+ # required in as well.
6
+
7
+ # put any IRB setup code here
8
+
9
+ require 'whysoslow'
@@ -0,0 +1,4 @@
1
+ require "assert"
2
+
3
+ class WhysoslowTest < Assert::Context
4
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "whysoslow/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "whysoslow"
7
+ s.version = Whysoslow::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Kelly Redding"]
10
+ s.email = ["kelly@kellyredding.com"]
11
+ s.homepage = "http://github.com/kellyredding/whysoslow"
12
+ s.summary = %q{A little runner/printer to benchmark Ruby code blocks}
13
+ s.description = %q{A little runner/printer to benchmark Ruby code blocks}
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ s.add_development_dependency("bundler")
21
+ s.add_development_dependency("assert")
22
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: whysoslow
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Kelly Redding
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-02-20 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ type: :development
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ version_requirements: *id001
33
+ name: bundler
34
+ - !ruby/object:Gem::Dependency
35
+ type: :development
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ version_requirements: *id002
47
+ name: assert
48
+ description: A little runner/printer to benchmark Ruby code blocks
49
+ email:
50
+ - kelly@kellyredding.com
51
+ executables: []
52
+
53
+ extensions: []
54
+
55
+ extra_rdoc_files: []
56
+
57
+ files:
58
+ - .gitignore
59
+ - Gemfile
60
+ - Gemfile.lock
61
+ - README.rdoc
62
+ - Rakefile
63
+ - lib/whysoslow.rb
64
+ - lib/whysoslow/default_printer.rb
65
+ - lib/whysoslow/measurement.rb
66
+ - lib/whysoslow/memory_profile.rb
67
+ - lib/whysoslow/results.rb
68
+ - lib/whysoslow/runner.rb
69
+ - lib/whysoslow/version.rb
70
+ - test/helper.rb
71
+ - test/irb.rb
72
+ - test/whysoslow_test.rb
73
+ - whysoslow.gemspec
74
+ homepage: http://github.com/kellyredding/whysoslow
75
+ licenses: []
76
+
77
+ post_install_message:
78
+ rdoc_options: []
79
+
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ hash: 3
88
+ segments:
89
+ - 0
90
+ version: "0"
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ hash: 3
97
+ segments:
98
+ - 0
99
+ version: "0"
100
+ requirements: []
101
+
102
+ rubyforge_project:
103
+ rubygems_version: 1.8.11
104
+ signing_key:
105
+ specification_version: 3
106
+ summary: A little runner/printer to benchmark Ruby code blocks
107
+ test_files:
108
+ - test/helper.rb
109
+ - test/irb.rb
110
+ - test/whysoslow_test.rb