rubyperf 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,39 @@
1
+ #
2
+ # Copyright (c) 2012 Lorenzo Pasqualis - DreamBox Learning, Inc
3
+ # https://github.com/lpasqualis/rubyperf
4
+ #
5
+
6
+ module Perf
7
+ #
8
+ # This class can be used in substitution to a Perf::Measure class to avoid overhead when performance measurments is not
9
+ # required. It needs to maintain the same API as Perf::Measure.
10
+ #
11
+ class NoopMeasure
12
+
13
+ def measurements
14
+ {}
15
+ end
16
+
17
+ def current_stack
18
+ {}
19
+ end
20
+
21
+ def initialize(logger = nil)
22
+ end
23
+
24
+ def clear
25
+ end
26
+
27
+ #############################################################################################################
28
+
29
+ def measure(what,type=nil)
30
+ yield
31
+ end
32
+
33
+ def count_value(what_to_count)
34
+ nil
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,130 @@
1
+ #
2
+ # Copyright (c) 2012 Lorenzo Pasqualis - DreamBox Learning, Inc
3
+ # https://github.com/lpasqualis/rubyperf
4
+ #
5
+
6
+ module Perf
7
+
8
+ #
9
+ # Base class for all the standard formatting options for a Perf::Meter. Provies a simple interface to create
10
+ # custom views of the performance report.
11
+ #
12
+ class ReportFormat
13
+
14
+ MIN_TOTAL_TIME = 1.0e-10
15
+
16
+ # Format takes a Perf::Meter plus a hash of options and converts it into a header, followed by a series
17
+ # of entries in a hash format that can be easily converted in any other format such as Text, HTML, XML, etc.
18
+ #
19
+ # You call this method every time that you want to generate a report from a Perf::Meter object.
20
+ #
21
+ # ==== Options
22
+ #
23
+ # * +:max_count_len+ : Maximum expected length of a block/espresson/method count.
24
+ #
25
+
26
+ def format(perf,options={})
27
+ options[:max_count_len] ||= 6
28
+ rep=[]
29
+ percents={}
30
+
31
+ max_count=options[:max_count_len]
32
+ max_title=0
33
+ keys_in_order=perf.measurements.keys.sort
34
+ total = Benchmark::Tms.new
35
+
36
+ perf.measurements.each_pair do |what,m|
37
+ title_len=format_title(what,options).length
38
+ path = what.split("\\")
39
+
40
+ max_title = title_len if title_len>max_title
41
+ max_count = m[:count].to_s.length if m[:count].to_s.length>max_count
42
+
43
+ total += m[:time] if path.size==2 # This calculates the max of the level-1 entries needed for the root entry.
44
+ end
45
+
46
+ totals=[total.real]
47
+ depth=1
48
+ keys_in_order.each do |what|
49
+ m=perf.measurements[what]
50
+ path = what.split("\\")
51
+ if path.size-1 != depth
52
+ if path.size-1 > depth
53
+ totals.push 0
54
+ else
55
+ totals.pop(depth-(path.size-1))
56
+ end
57
+ depth=path.size-1
58
+ end
59
+ totals[totals.size-1] = m[:time].real
60
+ totals[totals.size-1] = MIN_TOTAL_TIME if totals[totals.size-1]<MIN_TOTAL_TIME
61
+ percents[what]=(m[:time].real*100.0)/totals[totals.size-2]
62
+ end
63
+
64
+ # Header
65
+ rep << format_header(:title => "measure path", :max_title => max_title,
66
+ :percent => "percent",
67
+ :count => "count", :max_count => max_count,
68
+ :time => Benchmark::Tms::CAPTION,
69
+ :options => options)
70
+
71
+ # Root
72
+
73
+ # Split of keys
74
+ keys_in_order.each do |what|
75
+ m=perf.measurements[what]
76
+ title = format_title(what,options)
77
+ rep << format_measure(:title => title, :max_title => max_title,
78
+ :percent => percents[what]||0.0,
79
+ :count => m[:count], :max_count => max_count,
80
+ :time => m[:time],
81
+ :options => options)
82
+ end
83
+
84
+ rep << format_footer(options)
85
+ rep
86
+ end
87
+
88
+ # Override to format the output of a single measure. Returns the measure in whatever format the output needs to be.
89
+ #
90
+ # ==== Options
91
+ #
92
+ # * +v+ : Hash containing the following keys:
93
+ # +title+ : Title - or path - of the block/method/expression (\root\a\b\c\d\something))
94
+ # +max_title+ : Longest title in the report
95
+ # +percent+ : Percentage of time spent in this measure compared to the containing block.
96
+ # +count+ : How many times the block/method/exression was executed
97
+ # +time+ : Execution time expressed as a Benchmark::Tms value; For titles this is Benchmark::Tms::CAPTION
98
+ # +options+ : Formatting options, as passed by the framework or the user.
99
+ #
100
+
101
+ def format_measure(v)
102
+ v
103
+ end
104
+
105
+ # Override to format the output of the header of the data.
106
+ #
107
+ # See format_measure
108
+
109
+ def format_header(v)
110
+ format_measure(v)
111
+ end
112
+
113
+ # Override to format the output of the header of the data.
114
+ #
115
+ # See format_measure
116
+
117
+ def format_title(what,options)
118
+ what
119
+ end
120
+
121
+ # Override to format the output of the footer of the data.
122
+ #
123
+ # See format_measure
124
+
125
+ def format_footer(options)
126
+ ""
127
+ end
128
+
129
+ end
130
+ end
@@ -0,0 +1,34 @@
1
+ #
2
+ # Copyright (c) 2012 Lorenzo Pasqualis - DreamBox Learning, Inc
3
+ # https://github.com/lpasqualis/rubyperf
4
+ #
5
+
6
+ require 'rubyperf'
7
+ require 'cgi'
8
+
9
+ module Perf
10
+ class ReportFormatHtml < ReportFormat
11
+
12
+ PERCENT_FORMAT = "%.3f"
13
+ INDENT = "&nbsp;"*3
14
+
15
+ def format_header(v)
16
+ "<table class='rubyperf_report_format_html_table'><tr><th>#{v[:title]}</th><th>%</th><th>count</th><th>user</th><th>system</th><th>total</th><th>real</th></tr>"
17
+ end
18
+
19
+ def format_measure(v)
20
+ percent= v[:percent].is_a?(String) ? v[:percent] : (PERCENT_FORMAT%v[:percent])
21
+ "<tr><td>#{v[:title]}</td><td>#{percent}</td><td>#{v[:count]}</td><td>#{v[:time].utime}</td><td>#{v[:time].stime}</td><td>#{v[:time].total}</td><td>#{v[:time].real}</td></td>"
22
+ end
23
+
24
+ def format_footer(v)
25
+ "</table>"
26
+ end
27
+
28
+ def format_title(what,options)
29
+ path=what.split("\\")
30
+ "#{(path.size-2) ? INDENT*(path.size-2) : ""}\\#{CGI.escapeHTML(path.last)}"
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,67 @@
1
+ #
2
+ # Copyright (c) 2012 Lorenzo Pasqualis - DreamBox Learning, Inc
3
+ # https://github.com/lpasqualis/rubyperf
4
+ #
5
+
6
+ require 'rubyperf'
7
+
8
+ module Perf
9
+ # Formats the report this way:
10
+ #
11
+ # measure path : percent% count user system total real
12
+ # \ : 100.000% 1 2.540000 0.030000 2.570000 ( 4.602187)
13
+ # \emtpy_loop : 0.042% 1 0.000000 0.000000 0.000000 ( 0.001946)
14
+ # \measure_overhead_x10000 : 5.893% 1 0.260000 0.010000 0.270000 ( 0.271223)
15
+ # \nothing1 : 90.286% 1000 0.250000 0.010000 0.260000 ( 0.244876)
16
+ # \blah2 : 2.685% 1000 0.010000 0.000000 0.010000 ( 0.006574)
17
+ # \nothing2 : 74.104% 1000 0.120000 0.010000 0.130000 ( 0.181462)
18
+ # \blah3 : 4.020% 1000 0.010000 0.000000 0.010000 ( 0.007294)
19
+ # \nothing3 : 3.679% 1000 0.070000 0.000000 0.070000 ( 0.006676)
20
+ # \zzzzblah3 : 3.716% 1000 0.000000 0.000000 0.000000 ( 0.006744)
21
+ # \something : 11.144% 1 0.000000 0.000000 0.000000 ( 0.512861)
22
+ # \something1 : 39.077% 1 0.000000 0.000000 0.000000 ( 0.200411)
23
+ # \something2 : 60.864% 2 0.000000 0.000000 0.000000 ( 0.312149)
24
+ # \string_operations : 20.097% 2 0.910000 0.000000 0.910000 ( 0.924894)
25
+ # \ciao1000 : 50.085% 1 0.460000 0.000000 0.460000 ( 0.463233)
26
+ # \help1000 : 49.893% 1 0.450000 0.000000 0.450000 ( 0.461461)
27
+ # \test = "1" : 21.737% 1 0.000000 0.000000 0.000000 ( 1.000368)
28
+ # \test = "false" : 0.000% 2 0.000000 0.000000 0.000000 ( 0.000015)
29
+ #
30
+ #
31
+ # Where:
32
+ #
33
+ # percent% : is the percentage of the real time of the real time of its containing path.
34
+ # For example blah2 is 2.685% of the real time of nothing1.The sum of the percentages at the same depth
35
+ # should always be < 100% (for example depth2 1 is 0.042+5.893+11.144+20.097+21.737 = 58.913). The rest
36
+ # is unmeasured time spent.
37
+ # count : is the number of time the measure was taken. All the measures are the cumulative elapsed time for all
38
+ # the measures. For example blah3 numbers refer to the cumulative time spent in 1000 execution of that block.
39
+ # user : User CPU time
40
+ # system : System CPU time
41
+ # total : user+system
42
+ # real : Real time
43
+ #
44
+
45
+ class ReportFormatSimple < ReportFormat
46
+
47
+ DEFAULT_INDENT = " "
48
+ PERCENT_FORMAT = "%.3f"
49
+ EXTRA_SPACES_AFTER_TITLE = 2
50
+
51
+ def format(perf,options={})
52
+ options[:indent] ||= DEFAULT_INDENT
53
+ super perf,options
54
+ end
55
+
56
+ def format_measure(v)
57
+ percent= v[:percent].is_a?(String) ? v[:percent] : (PERCENT_FORMAT%v[:percent])
58
+ "#{v[:title].ljust(v[:max_title]+EXTRA_SPACES_AFTER_TITLE," ")}: #{percent.rjust(7," ")}% #{v[:count].to_s.rjust(v[:max_count]," ")} #{v[:time].to_s.gsub(/\n/,'')}\n"
59
+ end
60
+
61
+ def format_title(what,options)
62
+ path=what.split("\\")
63
+ "#{(path.size-2) ? options[:indent]*(path.size-2) : ""}\\#{path.last}"
64
+ end
65
+
66
+ end
67
+ end
data/lib/rubyperf.rb ADDED
@@ -0,0 +1,154 @@
1
+ #
2
+ # Copyright (c) 2012 Lorenzo Pasqualis - DreamBox Learning, Inc
3
+ #
4
+ # Project home-page git repository:
5
+ # https://github.com/lpasqualis/rubyperf
6
+ #
7
+ # Author can be contacted at:
8
+ # lpasqualis@gmail.com
9
+ #
10
+ # Author's Blog:
11
+ # http://www.lorenzopasqualis.com
12
+ #
13
+ # License:
14
+ #
15
+ # Permission is hereby granted, free of charge, to any person obtaining
16
+ # a copy of this software and associated documentation files (the
17
+ # "Software"), to deal in the Software without restriction, including
18
+ # without limitation the rights to use, copy, modify, merge, publish,
19
+ # distribute, sublicense, and/or sell copies of the Software, and to
20
+ # permit persons to whom the Software is furnished to do so, subject to
21
+ # the following conditions:
22
+ #
23
+ # The above copyright notice and this permission notice shall be
24
+ # included in all copies or substantial portions of the Software.
25
+ #
26
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33
+ #
34
+ #############################################################################
35
+ #
36
+ # This gem measures the execution time of blocks of code, methods and expressions.
37
+ # It generates detailed reports in various formats showing the nested structure
38
+ # of the measures.
39
+ #
40
+ # Designed to give tools to drill in the performance of hot code and identify
41
+ # bottlenecks.
42
+ #
43
+ # Currently available output formats for the report: text, html.
44
+ #
45
+ # Example of use:
46
+ #
47
+ # require 'rubyperf'
48
+ #
49
+ # def example
50
+ # m=Perf::Meter.new
51
+ # m.measure(:string_operations) do
52
+ # m.measure(:ciao) do
53
+ # 1000.times do; "CIAO"*100; end
54
+ # end
55
+ # end
56
+ # m.measure(:string_operations) do
57
+ # m.measure(:help) do
58
+ # 1000.times do; "HELP"*100; end
59
+ # end
60
+ # end
61
+ # m.measure(:emtpy_loop) do
62
+ # 50000.times do; end;
63
+ # end
64
+ # m.measure(:rough_overhead_x10000) do
65
+ # 1000.times do
66
+ # m.measure(:block_1) do
67
+ # m.measure(:block_1_1) do
68
+ # end
69
+ # m.measure(:block_1_2) do
70
+ # m.measure(:block_1_2_1) do
71
+ # end
72
+ # m.measure(:block_1_2_2) do
73
+ # end
74
+ # m.measure(:block_1_2_3) do
75
+ # m.measure_result(:bool_exp_1_2_3) { false }
76
+ # m.measure_result(:bool_exp_1_2_3) { true }
77
+ # end
78
+ # end
79
+ # end
80
+ # end
81
+ # end
82
+ #
83
+ # m.measure(:sleep1) do
84
+ # m.measure(:sleep1_1) do
85
+ # sleep(0.01)
86
+ # end
87
+ # m.measure(:sleep_1_2) do
88
+ # sleep(0.02)
89
+ # end
90
+ # m.measure(:sleep_1_3) do
91
+ # sleep(0.03)
92
+ # end
93
+ # end
94
+ # m.measure(:empty) do
95
+ # end
96
+ # m.measure_result("test") { sleep(1) }
97
+ # m.measure_result("test") { false }
98
+ # m.measure_result("test") { false }
99
+ #
100
+ # m.method_meters(Array,[:sort,:reverse],[:new]) do
101
+ # Array.new(1000000,"abc").reverse.sort
102
+ # end
103
+ #
104
+ # puts m.report_simple
105
+ # end
106
+ #
107
+ # This sample outputs the following report (see notes)).
108
+ #
109
+ # measure path : percent% count user system total real
110
+ #
111
+ # \blocks : 100.000% 8014 1.040000 0.060000 1.100000 ( 2.167488)
112
+ # \empty : 0.000% 1 0.000000 0.000000 0.000000 ( 0.000005)
113
+ # \emtpy_loop : 0.130% 1 0.010000 0.000000 0.010000 ( 0.002825)
114
+ # \rough_overhead_x10000 : 49.354% 1 1.000000 0.060000 1.060000 ( 1.069733)
115
+ # \block_1 : 92.013% 1000 0.890000 0.050000 0.940000 ( 0.984297)
116
+ # \block_1_1 : 0.503% 1000 0.020000 0.000000 0.020000 ( 0.004951)
117
+ # \block_1_2 : 77.414% 1000 0.670000 0.040000 0.710000 ( 0.761979)
118
+ # \block_1_2_1 : 0.640% 1000 0.000000 0.010000 0.010000 ( 0.004880)
119
+ # \block_1_2_2 : 0.644% 1000 0.010000 0.000000 0.010000 ( 0.004904)
120
+ # \block_1_2_3 : 48.027% 1000 0.370000 0.020000 0.390000 ( 0.365958)
121
+ # \bool_exp_1_2_3 = "false" : 1.354% 1000 0.000000 0.000000 0.000000 ( 0.004956)
122
+ # \bool_exp_1_2_3 = "true" : 1.369% 1000 0.020000 0.000000 0.020000 ( 0.005012)
123
+ # \sleep1 : 2.868% 1 0.000000 0.000000 0.000000 ( 0.062157)
124
+ # \sleep1_1 : 17.808% 1 0.000000 0.000000 0.000000 ( 0.011069)
125
+ # \sleep_1_2 : 32.524% 1 0.000000 0.000000 0.000000 ( 0.020216)
126
+ # \sleep_1_3 : 49.090% 1 0.000000 0.000000 0.000000 ( 0.030513)
127
+ # \string_operations : 1.468% 2 0.030000 0.000000 0.030000 ( 0.031817)
128
+ # \ciao : 95.276% 1 0.030000 0.000000 0.030000 ( 0.030314)
129
+ # \help : 4.111% 1 0.000000 0.000000 0.000000 ( 0.001308)
130
+ # \test = "1" : 46.160% 1 0.000000 0.000000 0.000000 ( 1.000502)
131
+ # \test = "false" : 0.000% 2 0.000000 0.000000 0.000000 ( 0.000008)
132
+ # \methods : 100.000% 3 0.140000 0.010000 0.150000 ( 0.154880)
133
+ # \#<Class:Array> : 21.416% 1 0.030000 0.010000 0.040000 ( 0.033169)
134
+ # \new : 99.907% 1 0.030000 0.010000 0.040000 ( 0.033138)
135
+ # \Array : 78.489% 2 0.110000 0.000000 0.110000 ( 0.121563)
136
+ # \reverse : 38.671% 1 0.040000 0.000000 0.040000 ( 0.047010)
137
+ # \sort : 61.287% 1 0.070000 0.000000 0.070000 ( 0.074502)
138
+ #
139
+ #
140
+ # Note: the percentage in each line is calculated on the real time of the containing block. For example in the example
141
+ # -> block_1_1 takes 0.503% of the real time of block_1
142
+ # -> block_1_2 takes 77.414% of the real time of block
143
+ #
144
+ # TODO:
145
+ #
146
+ # * Eliminate the overhead of the perf meter operations from the computation of times.
147
+ #
148
+
149
+ require 'perf/meter'
150
+ require 'perf/meter_factory'
151
+ require 'perf/no_op_meter'
152
+ require 'perf/report_format'
153
+ require 'perf/report_format_simple'
154
+ require 'perf/report_format_html'
data/test/helper.rb ADDED
@@ -0,0 +1,24 @@
1
+ #
2
+ # Copyright (c) 2012 Lorenzo Pasqualis - DreamBox Learning, Inc
3
+ # https://github.com/lpasqualis/rubyperf
4
+ #
5
+
6
+ require 'rubygems'
7
+ require 'bundler'
8
+ begin
9
+ Bundler.setup(:default, :development)
10
+ rescue Bundler::BundlerError => e
11
+ $stderr.puts e.message
12
+ $stderr.puts "Run `bundle install` to install missing gems"
13
+ exit e.status_code
14
+ end
15
+ require 'test/unit'
16
+ require 'shoulda'
17
+
18
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
19
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
20
+ require 'rubyperf'
21
+
22
+ class Test::Unit::TestCase
23
+ end
24
+
@@ -0,0 +1,19 @@
1
+ #
2
+ # Copyright (c) 2012 Lorenzo Pasqualis - DreamBox Learning, Inc
3
+ # https://github.com/lpasqualis/rubyperf
4
+ #
5
+
6
+ class PerfTestExample
7
+ def test(a,b,c)
8
+ (0..100000).to_a.reverse.reverse.reverse # Do something heavy
9
+ end
10
+
11
+ def test_np
12
+ (0..300000).to_a.reverse.reverse.reverse # Do something heavy
13
+ end
14
+
15
+ def self.static_method
16
+ (0..300000).to_a.reverse.reverse.reverse # Do something heavy
17
+ end
18
+ end
19
+
@@ -0,0 +1,43 @@
1
+ #
2
+ # Copyright (c) 2012 Lorenzo Pasqualis - DreamBox Learning, Inc
3
+ # https://github.com/lpasqualis/rubyperf
4
+ #
5
+
6
+ require 'rubyperf'
7
+ require 'perf_test_example'
8
+
9
+ class RubyperfTestHelpers
10
+ def self.get_measure
11
+ m=Perf::Meter.new
12
+
13
+ a=PerfTestExample.new
14
+ m.measure(:measure_test) { a.test(1,2,3) }
15
+ m.measure(:measure_test_np) { a.test_np }
16
+ m.measure(:some_expressions) do
17
+ m.measure_result(:expression1) { 1234+12345 }
18
+ m.measure_result(:expression1) { 1234-123 }
19
+ m.measure_result(:expression2) { "string" }
20
+ end
21
+ # Then use the instance method
22
+ m.method_meters(PerfTestExample,[:test,:test_np],[:static_method]) do
23
+ a=PerfTestExample.new
24
+ a.test(1,2,3)
25
+ a.test_np
26
+ PerfTestExample.static_method
27
+ end
28
+ m
29
+ end
30
+
31
+ def self.verify_report(m,expected_paths)
32
+ rf=Perf::ReportFormat.new
33
+ r=rf.format(m)
34
+ cnt=0
35
+ expected_paths.each do |ep|
36
+ r.each do |l|
37
+ cnt+=1 if l[:title]==ep
38
+ end
39
+ end
40
+ cnt==expected_paths.size
41
+ end
42
+
43
+ end