ZenHacks 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,149 @@
1
+ require 'inline'
2
+ require 'singleton'
3
+ require 'ruby_to_c'
4
+ require 'pp'
5
+
6
+ module Inline
7
+ class Ruby < Inline::C
8
+ def optimize(meth)
9
+ src = RubyToC.translate(@mod, meth)
10
+ if $DEBUG then
11
+ STDERR.puts
12
+ STDERR.puts src
13
+ STDERR.puts
14
+ end
15
+ @mod.class_eval "alias :#{meth}_slow :#{meth}"
16
+ @mod.class_eval "remove_method :#{meth}"
17
+ c src
18
+ end
19
+ end
20
+ end
21
+
22
+ class ZenOptimizer
23
+
24
+ include Singleton
25
+
26
+ @@threshold = 500
27
+ @@sacred = {
28
+ Sexp => true,
29
+ }
30
+ @@skip = Hash.new(false)
31
+ @@data = Hash.new(0)
32
+
33
+ def self.start_optimizing
34
+ self.instance.add_event_hook
35
+ end
36
+
37
+ def self.stop_optimizing
38
+ self.instance.remove_event_hook
39
+ if $DEBUG then
40
+ STDERR.puts @@skip.inspect
41
+ STDERR.puts @@data.sort_by{|x,y| y}.reverse[0..4].inspect
42
+ end
43
+ end
44
+
45
+ def self.optimize(signature)
46
+ klass, meth = *signature
47
+
48
+ unless @@sacred.include? klass then
49
+ STDERR.puts "*** Optimizer threshold tripped!! Optimizing #{klass}.#{meth}"
50
+
51
+ begin
52
+ klass.module_eval "inline(:Ruby) { |b| b.optimize(#{meth.inspect}) }"
53
+ rescue Exception => e
54
+ STDERR.puts "Failed to optimize #{klass}.#{meth}"
55
+ STDERR.puts "Exception = #{e.class}, message = #{e.message}"
56
+ end
57
+ end
58
+
59
+ @@skip[signature] = true
60
+ end
61
+
62
+ ############################################################
63
+ # Inlined Methods:
64
+
65
+ inline(:C) do |builder|
66
+
67
+ builder.add_type_converter("rb_event_t", '', '')
68
+ builder.add_type_converter("ID", '', '')
69
+
70
+ builder.include '"ruby.h"'
71
+ builder.include '"node.h"'
72
+
73
+ builder.prefix "static VALUE optimizer_klass = Qnil;
74
+ static VALUE data = Qnil;
75
+ static VALUE skip = Qnil;
76
+ static unsigned long threshold = 0;"
77
+
78
+ builder.c_raw <<-'EOF'
79
+ static void
80
+ prof_event_hook(rb_event_t event, NODE *node,
81
+ VALUE self, ID mid, VALUE klass) {
82
+
83
+ if (NIL_P(optimizer_klass))
84
+ optimizer_klass = rb_path2class("ZenOptimizer");
85
+ if (NIL_P(data))
86
+ data = rb_cv_get(optimizer_klass, "@@data");
87
+ if (NIL_P(skip))
88
+ skip = rb_cv_get(optimizer_klass, "@@skip");
89
+ if (threshold == 0)
90
+ threshold = NUM2ULONG(rb_cv_get(optimizer_klass, "@@threshold"));
91
+
92
+ switch (event) {
93
+ case RUBY_EVENT_CALL:
94
+ {
95
+ VALUE signature;
96
+
97
+ #if 0
98
+ VALUE mod_name = rb_mod_name(klass);
99
+ if (NIL_P(mod_name))
100
+ signature = rb_str_new2("Unknown");
101
+ else
102
+ signature = mod_name;
103
+
104
+ rb_str_cat(signature, ".", 1); // TODO: # or .
105
+
106
+ char * meth = rb_id2name(mid);
107
+ if (meth) {
108
+ size_t len = strlen(meth);
109
+ rb_str_cat(signature, meth, len);
110
+ } else {
111
+ rb_str_cat(signature, "unknown", 7);
112
+ }
113
+ #else
114
+ signature = rb_ary_new2(2);
115
+ rb_ary_store(signature, 0, klass);
116
+ rb_ary_store(signature, 1, ID2SYM(mid));
117
+ #endif
118
+
119
+ unsigned long count = NUM2ULONG(rb_hash_aref(data, signature)) + 1;
120
+
121
+ if (count > threshold) {
122
+ if (! RTEST(rb_hash_aref(skip, signature))) {
123
+ rb_funcall(optimizer_klass, rb_intern("optimize"), 1, signature);
124
+ }
125
+ }
126
+
127
+ rb_hash_aset(data, signature, ULONG2NUM(count));
128
+ }
129
+ break;
130
+ }
131
+ }
132
+ EOF
133
+
134
+ builder.c <<-'EOF'
135
+ void add_event_hook() {
136
+ rb_add_event_hook(prof_event_hook, RUBY_EVENT_CALL);
137
+ }
138
+ EOF
139
+
140
+ builder.c <<-'EOF'
141
+ void remove_event_hook() {
142
+ rb_remove_event_hook(prof_event_hook);
143
+ }
144
+ EOF
145
+ end
146
+ end
147
+
148
+ END { ZenOptimizer::stop_optimizing }
149
+ ZenOptimizer::start_optimizing
@@ -0,0 +1,170 @@
1
+ require 'inline'
2
+ require 'singleton'
3
+
4
+ require 'pp'
5
+
6
+ class ZenProfiler
7
+
8
+ include Singleton
9
+
10
+ @@start = @@stack = @@map = nil
11
+
12
+ def self.start_profile
13
+ @@start = self.instance.time_now
14
+ @@stack = [[0, 0, [nil, :toplevel]], [0, 0, [nil, :dummy]]]
15
+ @@map = {"#toplevel" => [1, 0.0, 0.0, [nil, "#toplevel"]]}
16
+ self.instance.add_event_hook
17
+ end
18
+
19
+ def self.stop_profile
20
+ self.instance.remove_event_hook
21
+ end
22
+
23
+ def self.print_profile(f)
24
+ stop_profile
25
+ total = self.instance.time_now - @@start
26
+ if total == 0 then total = 0.01 end
27
+ @@map["#toplevel"][1] = total
28
+ data = @@map.values
29
+ data.sort!{|a,b| b[2] <=> a[2]} # TODO: change to sort_by
30
+ sum = 0
31
+
32
+ f.printf " %% cumulative self self total\n"
33
+ f.printf " time seconds seconds calls ms/call ms/call name\n"
34
+ for d in data
35
+ sum += d[2]
36
+ klass = d[3].first
37
+ meth = d[3].last.to_s
38
+
39
+ signature = if klass.nil?
40
+ meth
41
+ elsif klass.kind_of?(Class)
42
+ klass.to_s.sub(/#<\S+:(\S+)>/, '\\1') + "#" + meth
43
+ else
44
+ klass.class.name + "." + meth
45
+ end
46
+
47
+ f.printf "%6.2f %8.2f %8.2f %8d ", d[2]/total*100, sum, d[2], d[0]
48
+ f.printf "%8.2f %8.2f %s\n", d[2]*1000/d[0], d[1]*1000/d[0], signature
49
+ end
50
+ end
51
+
52
+ ############################################################
53
+ # Inlined Methods:
54
+
55
+ inline(:C) do |builder|
56
+
57
+ builder.add_type_converter("rb_event_t", '', '')
58
+ builder.add_type_converter("ID", '', '')
59
+ builder.add_type_converter("NODE *", '', '')
60
+
61
+ builder.include '"ruby.h"'
62
+ builder.include '"node.h"'
63
+
64
+ builder.prefix "
65
+ static VALUE profiler_klass = Qnil;
66
+ static VALUE stack = Qnil;
67
+ static VALUE map = Qnil;
68
+ "
69
+
70
+ builder.c_raw <<-'EOF'
71
+ VALUE time_now() {
72
+ return rb_float_new(((double) clock() / CLOCKS_PER_SEC));
73
+ }
74
+ EOF
75
+
76
+ builder.c_raw <<-'EOF'
77
+ static void
78
+ prof_event_hook(rb_event_t event, NODE *node,
79
+ VALUE self, ID mid, VALUE klass) {
80
+
81
+ static int profiling = 0;
82
+
83
+ if (mid == ID_ALLOCATOR) return;
84
+ if (profiling) return;
85
+ profiling++;
86
+
87
+ if (NIL_P(profiler_klass))
88
+ profiler_klass = rb_path2class("ZenProfiler");
89
+ if (NIL_P(stack))
90
+ stack = rb_cv_get(profiler_klass, "@@stack");
91
+ if (NIL_P(map))
92
+ map = rb_cv_get(profiler_klass, "@@map");
93
+
94
+ switch (event) {
95
+ case RUBY_EVENT_CALL:
96
+ case RUBY_EVENT_C_CALL:
97
+ {
98
+ VALUE signature;
99
+ signature = rb_ary_new2(2);
100
+ rb_ary_store(signature, 0, klass);
101
+ rb_ary_store(signature, 1, ID2SYM(mid));
102
+ VALUE time = rb_ary_new();
103
+ rb_ary_push(time, time_now());
104
+ rb_ary_push(time, rb_float_new(0.0));
105
+ rb_ary_push(time, signature);
106
+ rb_ary_push(stack, time);
107
+ }
108
+ break;
109
+ case RUBY_EVENT_RETURN:
110
+ case RUBY_EVENT_C_RETURN:
111
+ {
112
+ VALUE now = time_now();
113
+ VALUE tick = rb_ary_pop(stack);
114
+
115
+ VALUE signature = rb_ary_entry(tick, -1);
116
+
117
+ VALUE data = Qnil;
118
+ st_lookup(RHASH(map)->tbl, signature, &data);
119
+ if (NIL_P(data)) {
120
+ data = rb_ary_new();
121
+ rb_ary_push(data, INT2FIX(0));
122
+ rb_ary_push(data, rb_float_new(0.0));
123
+ rb_ary_push(data, rb_float_new(0.0));
124
+ rb_ary_push(data, signature);
125
+ rb_hash_aset(map, signature, data);
126
+ }
127
+
128
+ rb_ary_store(data, 0, ULONG2NUM(NUM2ULONG(rb_ary_entry(data, 0)) + 1));
129
+
130
+ double cost = NUM2DBL(now) - NUM2DBL(rb_ary_entry(tick, 0));
131
+
132
+ rb_ary_store(data, 1, rb_float_new(NUM2DBL(rb_ary_entry(data, 1))
133
+ + cost));
134
+ rb_ary_store(data, 2, rb_float_new(NUM2DBL(rb_ary_entry(data, 2))
135
+ + cost
136
+ - NUM2DBL(rb_ary_entry(tick, 1))));
137
+
138
+ VALUE toplevel = rb_ary_entry(stack, -1);
139
+ VALUE tl_stats = rb_ary_entry(toplevel, 1);
140
+ long n = NUM2DBL(tl_stats) + cost;
141
+ rb_ary_store(toplevel, 1, rb_float_new(n));
142
+ }
143
+ break;
144
+ }
145
+ profiling--;
146
+ }
147
+ EOF
148
+
149
+ builder.c <<-'EOF'
150
+ void add_event_hook() {
151
+ rb_add_event_hook(prof_event_hook,
152
+ RUBY_EVENT_CALL | RUBY_EVENT_RETURN |
153
+ RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN);
154
+ }
155
+ EOF
156
+
157
+ builder.c <<-'EOF'
158
+ void remove_event_hook() {
159
+ rb_remove_event_hook(prof_event_hook);
160
+ }
161
+ EOF
162
+
163
+ end
164
+
165
+ end
166
+
167
+ END {
168
+ ZenProfiler::print_profile(STDOUT)
169
+ }
170
+ ZenProfiler::start_profile
@@ -0,0 +1,26 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ class Factorial
4
+
5
+ def factorial(n)
6
+ f = 1
7
+ n.downto(2) { |x| f *= x }
8
+ return f
9
+ end
10
+
11
+ end
12
+
13
+ f = Factorial.new()
14
+
15
+ max = ARGV.shift || 1000000
16
+ max = max.to_i
17
+
18
+ tstart = Time.now
19
+
20
+ (1..max).each { |m| n = f.factorial(5); }
21
+
22
+ tend = Time.now
23
+
24
+ total = tend - tstart
25
+ avg = total / max
26
+ printf "Iter = #{max}, T = %.8f sec, %.8f sec / iter\n", total, avg
@@ -0,0 +1,49 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ require 'pp'
4
+
5
+ class PP
6
+ module PPMethods
7
+ alias old_pp_hash pp_hash
8
+ def pp_hash(obj)
9
+ group(1, '{', '}') {
10
+ seplist(obj, nil, :each_pair) {|k, v|
11
+ group {
12
+ pp k
13
+ text ' => '
14
+ group(1) {
15
+ breakable ''
16
+ pp v
17
+ }
18
+ }
19
+ }
20
+ }
21
+ end
22
+ end
23
+ end
24
+
25
+ # KLASSES[klass] = c_name
26
+ KLASSES = {}
27
+
28
+ # METHODS[klass][ruby_name] = c_name
29
+ METHODS = Hash.new { |h,k| h[k] = {} }
30
+
31
+ Dir.chdir ARGV.shift do
32
+ Dir['*.c'].each do |c_file|
33
+ File.open c_file do |fp|
34
+ fp.each_line do |line|
35
+ case line
36
+ when /([^ ]+)\s+=\srb_define_class\("([^"]+)"/ then
37
+ KLASSES[$1] = $2
38
+ when /rb_define_method\(([^,]+),\s+"([^"]+)",\s+([^,]+), (-?\d+)/ then
39
+ klass = KLASSES[$1]
40
+ METHODS[klass][$2.intern] = $3, $4.to_i
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ print "RUBY_C_METHOD_MAP = "
48
+ pp METHODS
49
+
@@ -0,0 +1,62 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ # Refax automatic refactoring system by Rudi Cilibrasi (cilibrar@gmail.com)
4
+ # A simple example test of the new ParseTree library.
5
+ #
6
+ # This program is meant to suggest possible refactoring opportunities
7
+ #
8
+ # to convert loops of the form
9
+ # stmt A
10
+ # stmt B
11
+ # stmt C
12
+ # while cond
13
+ # stmt A
14
+ # stmt B
15
+ # stmt C
16
+ # end
17
+ #
18
+ # to
19
+ #
20
+ # begin
21
+ # stmt A
22
+ # stmt B
23
+ # stmt C
24
+ # end while cond
25
+ #
26
+ # to use this system, just put
27
+ #
28
+ # require 'fixloops'
29
+ # at the bottom of your ruby source file after requiring all classes to check
30
+ #
31
+ # ParseTree notes:
32
+ # 1) How can you distinguish while loops with preconditions
33
+ # versus those with postconditional guards? It looks like the parse tree
34
+ # is showing them the same at this moment.
35
+ # 2) Trying to parse_tree(Object) throws a nil ptr
36
+
37
+
38
+ # And here is an example hastily.rb that can be refactored using it:
39
+ # Example Ruby program to test automatic refactoring engine based on ParseTree
40
+ # by Rudi Cilibrasi (cilibrar@gmail.com)
41
+
42
+ class HastilyWritten
43
+ def keepgoing() rand(2) == 0 end
44
+ def doSomethingWeird() puts "zzzzz" end
45
+ def weirdfunc
46
+ puts "This is a weird loop"
47
+ doSomethingWeird()
48
+ while keepgoing
49
+ puts "This is a weird loop"
50
+ doSomethingWeird()
51
+ end
52
+ end
53
+ def finefunc
54
+ begin
55
+ puts "This is a weird loop"
56
+ doSomethingWeird()
57
+ end while keepgoing
58
+ end
59
+ end
60
+
61
+ # Here is the line you need to use Refax automatic refactoring system
62
+ require 'fixloops'