RubyInline 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +120 -0
- data/Makefile +37 -0
- data/Manifest.txt +12 -0
- data/README.txt +118 -0
- data/example.rb +75 -0
- data/example2.rb +29 -0
- data/inline-compat.rb +44 -0
- data/inline.gemspec +33 -0
- data/inline.rb +427 -0
- data/test_inline.rb +481 -0
- data/tutorial/example1.rb +63 -0
- data/tutorial/example2.rb +96 -0
- metadata +52 -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;
|
11
|
+
long i, len;
|
12
|
+
VALUE *arr = RARRAY(self)->ptr;
|
13
|
+
len = RARRAY(self)->len;
|
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,52 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.1
|
3
|
+
specification_version: 1
|
4
|
+
name: RubyInline
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 3.1.0
|
7
|
+
date: 2004-11-19
|
8
|
+
summary: Multi-language extension coding within ruby.
|
9
|
+
require_paths:
|
10
|
+
- "."
|
11
|
+
author: Ryan Davis
|
12
|
+
email: ryand-ruby@zenspider.com
|
13
|
+
homepage: http://www.zenspider.com/ZSS/Products/RubyInline/
|
14
|
+
rubyforge_project: rubyinline
|
15
|
+
description: "Ruby Inline is my quick attempt to create an analog to Perl's Inline::C. It
|
16
|
+
allows you to embed C or C++ external module code in your ruby script directly.
|
17
|
+
The code is compiled and run on the fly when needed. The ruby version isn't near
|
18
|
+
as feature-full as the perl version, but it is neat!"
|
19
|
+
autorequire: inline
|
20
|
+
default_executable:
|
21
|
+
bindir: bin
|
22
|
+
has_rdoc: false
|
23
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
24
|
+
requirements:
|
25
|
+
-
|
26
|
+
- ">"
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: 0.0.0
|
29
|
+
version:
|
30
|
+
platform: ruby
|
31
|
+
files:
|
32
|
+
- History.txt
|
33
|
+
- Makefile
|
34
|
+
- Manifest.txt
|
35
|
+
- README.txt
|
36
|
+
- example.rb
|
37
|
+
- example2.rb
|
38
|
+
- inline.gemspec
|
39
|
+
- inline.rb
|
40
|
+
- inline-compat.rb
|
41
|
+
- test_inline.rb
|
42
|
+
- tutorial/example1.rb
|
43
|
+
- tutorial/example2.rb
|
44
|
+
test_files:
|
45
|
+
- test_inline.rb
|
46
|
+
rdoc_options: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
executables: []
|
49
|
+
extensions: []
|
50
|
+
requirements:
|
51
|
+
- A POSIX environment and a compiler for your language.
|
52
|
+
dependencies: []
|