ZenHacks 1.0.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.
@@ -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'