RubyInlineWithoutZenTest 3.12.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/ruby -w
2
+
3
+ # We started out with some code that averaged a ton of numbers in a
4
+ # bunch of arrays. Once we finally lost our patience with the average
5
+ # running time of the code, we decided to profile and optimize it
6
+ # using RubyInline.
7
+
8
+ class Array
9
+
10
+ def average
11
+ result = 0
12
+ self.each { |x| result += x }
13
+ result / self.size.to_f
14
+ end
15
+
16
+ end
17
+
18
+ max_loop = (ARGV.shift || 5).to_i
19
+ max_size = (ARGV.shift || 100_000).to_i
20
+ a = (1..max_size).to_a
21
+
22
+ 1.upto(max_loop) do
23
+ avg = a.average
24
+ $stderr.print "."
25
+ end
26
+ $stderr.puts ""
27
+
28
+ # The first step to profiling is to get a simple run of the code using
29
+ # 'time' and a large dataset. This is because a profile run should
30
+ # only be used for figuring out where your bottlenecks are, but the
31
+ # runtime of a profile run should be considered invalid. This is
32
+ # because set_trace_func, the main mechanism used by profile is a very
33
+ # costly function.
34
+
35
+ # & time ruby ./example1.rb 5 100000
36
+ # .....
37
+ #
38
+ # real 0m4.580s
39
+ # user 0m3.310s
40
+ # sys 0m0.090s
41
+ # (user+sys = 3.400s)
42
+
43
+ # This gives us a tangible goal, to reduce the runtime of 4.58 seconds
44
+ # as much as possible. The next step is to run with a smaller dataset
45
+ # (because profiling is VERY SLOW) while including the profile module.
46
+
47
+ # & ruby -rprofile ./example1.rb 3 10000
48
+ # ...
49
+ # % cumulative self self total
50
+ # time seconds seconds calls ms/call ms/call name
51
+ # 69.78 4.78 4.78 3 1593.33 2273.33 Array#each
52
+ # 29.78 6.82 2.04 30000 0.07 0.07 Fixnum#+
53
+ # 0.15 6.83 0.01 3 3.33 2276.67 Array#average
54
+ # 0.15 6.84 0.01 1 10.00 10.00 Range#each
55
+ # 0.00 6.84 0.00 1 0.00 10.00 Enumerable.to_a
56
+ # -- CUT ALL FOLLOWING LINES WHERE %time == 0.00
57
+
58
+ # This says that Array#each and Fixnum#+ are the only two things we
59
+ # should focus on at all. The rest of the time is statistically
60
+ # insignificant. So, since average itself is a rather uncomplicated
61
+ # method, we decided to convert the entire method rather than just try
62
+ # to speed up the math or the loop separately. See example2.rb for the
63
+ # continuation of this example.
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/ruby -w -I..
2
+
3
+ require 'inline'
4
+
5
+ class Array
6
+
7
+ inline do |builder|
8
+ builder.c_raw "
9
+ static VALUE average(int argc, VALUE *argv, VALUE self) {
10
+ double result = 0;
11
+ long i, len;
12
+ VALUE *arr = RARRAY_PTR(self);
13
+ len = RARRAY_LEN(self);
14
+
15
+ for(i=0; i<len; i++) {
16
+ result += NUM2DBL(arr[i]);
17
+ }
18
+
19
+ return rb_float_new(result/(double)len);
20
+ }
21
+ "
22
+ end
23
+ end
24
+
25
+ max_loop = (ARGV.shift || 5).to_i
26
+ max_size = (ARGV.shift || 100_000).to_i
27
+ a = (1..max_size).to_a
28
+
29
+ 1.upto(max_loop) do
30
+ avg = a.average
31
+ $stderr.print "."
32
+ end
33
+ $stderr.puts ""
34
+
35
+ # ruby -rprofile ./example2.rb 3 10000
36
+ # ...
37
+ # % cumulative self self total
38
+ # time seconds seconds calls ms/call ms/call name
39
+ # 23.53 0.08 0.08 4 20.00 122.50 Kernel.require
40
+ # 14.71 0.13 0.05 123 0.41 0.98 Config.expand
41
+ # 11.76 0.17 0.04 135 0.30 0.59 String#gsub!
42
+ # 11.76 0.21 0.04 1 40.00 40.00 Hash#each
43
+ # 8.82 0.24 0.03 1 30.00 110.00 Hash#each_value
44
+ # 5.88 0.26 0.02 3 6.67 6.67 Mod_Array_average.average
45
+ # 2.94 0.27 0.01 10 1.00 1.00 Kernel.singleton_method_added
46
+ # 2.94 0.28 0.01 1 10.00 30.00 Fixnum#upto
47
+ # 2.94 0.29 0.01 2 5.00 10.00 Module#parse_signature
48
+ # 2.94 0.30 0.01 2 5.00 5.00 File#stat
49
+ # 2.94 0.31 0.01 1 10.00 10.00 Range#each
50
+ # 2.94 0.32 0.01 1 10.00 50.00 Module#inline_c_real
51
+ # 2.94 0.33 0.01 182 0.05 0.05 Hash#[]=
52
+ # 2.94 0.34 0.01 1 10.00 20.00 Module#inline_c_gen
53
+ # 0.00 0.34 0.00 1 0.00 0.00 Module#include
54
+ # -- CUT ALL FOLLOWING LINES WHERE %time == 0.00
55
+
56
+ # The first example's cumulative time for Array#average was 6.83
57
+ # seconds (wallclock) and the second example's average
58
+ # (Mod_Array_average#average) was .26 seconds (a 26x speedup). The
59
+ # rest of the time was spent dealing with RubyInline's compile of the
60
+ # code. Subsequent runs of the code skip most of RubyInline's work
61
+ # because the code has already been compiled and it hasn't changed.
62
+ # Looking at the profile, there was really nothing more that we wanted
63
+ # to speed up. If there was, then we would have done a few more
64
+ # iterations of using RubyInline to extract slower ruby code into
65
+ # faster C code and profiling again.
66
+ #
67
+ # At this point, we were satisfied with the time of the code and
68
+ # decided to stop profiling. All that was left to do was to run with
69
+ # 'time' again and our larger dataset:
70
+
71
+ # & time ruby ./example2.rb 5 100000
72
+ # .....
73
+ #
74
+ # real 0m1.403s
75
+ # user 0m1.120s
76
+ # sys 0m0.070s
77
+ # (user+sys = 1.190s)
78
+
79
+ # We've reduced the running time of the program from 3.40s of CPU time
80
+ # to 1.19s of CPU. This is a speed-up of 2.85. Not too shabby...
81
+
82
+ # You don't want to compare the runtime of the profiled code because
83
+ # the cost of running with set_trace_func is so great that it skews
84
+ # the results heavily. Looking at the ratio between the normal run
85
+ # versus the profiled runs between the pure ruby and the inlined
86
+ # versions shows this skew quite clearly:
87
+
88
+ # norm prof
89
+ # ruby 3.40 6.83 (1:2 roughly)
90
+ # C 1.19 0.26 (5:1 roughly)
91
+
92
+ # This happens simply because our call to Mod_Array_average.average
93
+ # causes 30000-1 less method calls than the pure ruby version. This
94
+ # translates directly into a multiplier per method call when using
95
+ # set_trace_func (which we estimate to be about 200us per call (6.83 /
96
+ # 30000) on my machine.
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: RubyInlineWithoutZenTest
3
+ version: !ruby/object:Gem::Version
4
+ hash: 51
5
+ prerelease:
6
+ segments:
7
+ - 3
8
+ - 12
9
+ - 2
10
+ version: 3.12.2
11
+ platform: ruby
12
+ authors:
13
+ - Paul Kmiec
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2013-07-24 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: minitest
22
+ prerelease: false
23
+ version_requirements: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 21
29
+ segments:
30
+ - 4
31
+ - 7
32
+ version: "4.7"
33
+ requirement: *id001
34
+ type: :development
35
+ - !ruby/object:Gem::Dependency
36
+ name: rdoc
37
+ prerelease: false
38
+ version_requirements: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ hash: 27
44
+ segments:
45
+ - 4
46
+ - 0
47
+ version: "4.0"
48
+ requirement: *id002
49
+ type: :development
50
+ - !ruby/object:Gem::Dependency
51
+ name: rake
52
+ prerelease: false
53
+ version_requirements: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ hash: 3
59
+ segments:
60
+ - 0
61
+ version: "0"
62
+ requirement: *id003
63
+ type: :development
64
+ description: See https://github.com/seattlerb/zentest/issues/28. Tired of wasting time with ZenTest.
65
+ email:
66
+ - paul.kmiec@appfolio.com
67
+ executables: []
68
+
69
+ extensions: []
70
+
71
+ extra_rdoc_files: []
72
+
73
+ files:
74
+ - .gitignore
75
+ - Gemfile
76
+ - History.txt
77
+ - LICENSE.txt
78
+ - Manifest.txt
79
+ - README.md
80
+ - README.txt
81
+ - Rakefile
82
+ - demo/fastmath.rb
83
+ - demo/hello.rb
84
+ - example.rb
85
+ - example2.rb
86
+ - lib/inline.rb
87
+ - lib/inline/mapping.rb
88
+ - lib/inline/version.rb
89
+ - rubyinlinewithoutzentest.gemspec
90
+ - test/test_inline.rb
91
+ - tutorial/example1.rb
92
+ - tutorial/example2.rb
93
+ homepage: ""
94
+ licenses:
95
+ - MIT
96
+ post_install_message:
97
+ rdoc_options: []
98
+
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ none: false
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ hash: 3
107
+ segments:
108
+ - 0
109
+ version: "0"
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ hash: 3
116
+ segments:
117
+ - 0
118
+ version: "0"
119
+ requirements:
120
+ - A POSIX environment and a compiler for your language.
121
+ rubyforge_project:
122
+ rubygems_version: 1.8.5
123
+ signing_key:
124
+ specification_version: 3
125
+ summary: RubyInline without the ZenTest dependency
126
+ test_files:
127
+ - test/test_inline.rb