RubyInline 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|