RubyInlineWithoutZenTest 3.12.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/History.txt +470 -0
- data/LICENSE.txt +22 -0
- data/Manifest.txt +12 -0
- data/README.md +29 -0
- data/README.txt +138 -0
- data/Rakefile +17 -0
- data/demo/fastmath.rb +27 -0
- data/demo/hello.rb +13 -0
- data/example.rb +86 -0
- data/example2.rb +33 -0
- data/lib/inline/mapping.rb +119 -0
- data/lib/inline/version.rb +3 -0
- data/lib/inline.rb +902 -0
- data/rubyinlinewithoutzentest.gemspec +39 -0
- data/test/test_inline.rb +1059 -0
- data/tutorial/example1.rb +63 -0
- data/tutorial/example2.rb +96 -0
- metadata +127 -0
@@ -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
|