llrb 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.gitmodules +4 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +56 -0
- data/README.md +311 -0
- data/Rakefile +30 -0
- data/bin/bm_app_fib +41 -0
- data/bin/bm_empty_method +33 -0
- data/bin/bm_loop_while +27 -0
- data/bin/bm_plus +33 -0
- data/bin/console +14 -0
- data/bin/loop_while.rb +5 -0
- data/bin/setup +8 -0
- data/ext/llrb/cfg.h +124 -0
- data/ext/llrb/compiler.c +987 -0
- data/ext/llrb/compiler/funcs.h +164 -0
- data/ext/llrb/compiler/stack.h +43 -0
- data/ext/llrb/cruby.h +42 -0
- data/ext/llrb/cruby/ccan/build_assert/build_assert.h +40 -0
- data/ext/llrb/cruby/ccan/check_type/check_type.h +63 -0
- data/ext/llrb/cruby/ccan/container_of/container_of.h +142 -0
- data/ext/llrb/cruby/ccan/list/list.h +773 -0
- data/ext/llrb/cruby/ccan/str/str.h +16 -0
- data/ext/llrb/cruby/internal.h +1774 -0
- data/ext/llrb/cruby/iseq.h +252 -0
- data/ext/llrb/cruby/method.h +213 -0
- data/ext/llrb/cruby/node.h +520 -0
- data/ext/llrb/cruby/probes_helper.h +43 -0
- data/ext/llrb/cruby/ruby_assert.h +54 -0
- data/ext/llrb/cruby/ruby_atomic.h +233 -0
- data/ext/llrb/cruby/thread_pthread.h +54 -0
- data/ext/llrb/cruby/vm_core.h +1646 -0
- data/ext/llrb/cruby/vm_debug.h +37 -0
- data/ext/llrb/cruby/vm_exec.h +182 -0
- data/ext/llrb/cruby/vm_opts.h +57 -0
- data/ext/llrb/cruby_extra/id.h +220 -0
- data/ext/llrb/cruby_extra/insns.inc +113 -0
- data/ext/llrb/cruby_extra/insns_info.inc +796 -0
- data/ext/llrb/cruby_extra/probes.h +80 -0
- data/ext/llrb/extconf.rb +102 -0
- data/ext/llrb/llrb.c +148 -0
- data/ext/llrb/optimizer.cc +118 -0
- data/ext/llrb/parser.c +191 -0
- data/ext/llrb/profiler.c +336 -0
- data/ext/llrb_insn_checkkeyword.c +20 -0
- data/ext/llrb_insn_checkmatch.c +28 -0
- data/ext/llrb_insn_concatarray.c +23 -0
- data/ext/llrb_insn_concatstrings.c +21 -0
- data/ext/llrb_insn_defined.c +9 -0
- data/ext/llrb_insn_getclassvariable.c +10 -0
- data/ext/llrb_insn_getinstancevariable.c +44 -0
- data/ext/llrb_insn_getlocal.c +14 -0
- data/ext/llrb_insn_getlocal_level0.c +8 -0
- data/ext/llrb_insn_getlocal_level1.c +8 -0
- data/ext/llrb_insn_getspecial.c +14 -0
- data/ext/llrb_insn_invokeblock.c +39 -0
- data/ext/llrb_insn_invokesuper.c +47 -0
- data/ext/llrb_insn_opt_aref.c +25 -0
- data/ext/llrb_insn_opt_aset.c +28 -0
- data/ext/llrb_insn_opt_div.c +32 -0
- data/ext/llrb_insn_opt_eq.c +57 -0
- data/ext/llrb_insn_opt_ge.c +28 -0
- data/ext/llrb_insn_opt_gt.c +38 -0
- data/ext/llrb_insn_opt_le.c +29 -0
- data/ext/llrb_insn_opt_lt.c +38 -0
- data/ext/llrb_insn_opt_ltlt.c +27 -0
- data/ext/llrb_insn_opt_minus.c +36 -0
- data/ext/llrb_insn_opt_mod.c +32 -0
- data/ext/llrb_insn_opt_mult.c +30 -0
- data/ext/llrb_insn_opt_neq.c +103 -0
- data/ext/llrb_insn_opt_plus.c +48 -0
- data/ext/llrb_insn_opt_send_without_block.c +45 -0
- data/ext/llrb_insn_opt_str_freeze.c +12 -0
- data/ext/llrb_insn_putspecialobject.c +23 -0
- data/ext/llrb_insn_send.c +49 -0
- data/ext/llrb_insn_setclassvariable.c +19 -0
- data/ext/llrb_insn_setconstant.c +23 -0
- data/ext/llrb_insn_setinstancevariable.c +48 -0
- data/ext/llrb_insn_setlocal.c +16 -0
- data/ext/llrb_insn_setlocal_level0.c +9 -0
- data/ext/llrb_insn_setlocal_level1.c +10 -0
- data/ext/llrb_insn_setspecial.c +15 -0
- data/ext/llrb_insn_splatarray.c +13 -0
- data/ext/llrb_insn_throw.c +11 -0
- data/ext/llrb_insn_trace.c +37 -0
- data/ext/llrb_push_result.c +14 -0
- data/ext/llrb_self_from_cfp.c +12 -0
- data/ext/llrb_set_pc.c +8 -0
- data/lib/llrb.rb +2 -0
- data/lib/llrb/jit.rb +76 -0
- data/lib/llrb/start.rb +2 -0
- data/lib/llrb/version.rb +3 -0
- data/llrb.gemspec +48 -0
- data/wercker.yml +31 -0
- metadata +227 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6a0d444c8fb55a7f69c33bd803cd86a39533f73d
|
4
|
+
data.tar.gz: 0403e982edb24a764bfee28bf2f7356982c6500b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: aa33e4543d3478c82409862252f39a08e63327e51582e6575452d20b0da4df5fe04447e7cf8156ff9eba351c12b1ce4d7c7840b48d97083875a4da9e18464f01
|
7
|
+
data.tar.gz: d97bec3202de9f3693f7edf453509052737909a0e3799ccef6dee3ad05802ac9cdb96e715f3ff280b445dca7cb0725aae94b5daa123ee4fa5e0722580cfde50d
|
data/.gitignore
ADDED
data/.gitmodules
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>.
|
2
|
+
You can redistribute it and/or modify it under either the terms of the
|
3
|
+
2-clause BSDL (see the file BSDL), or the conditions below:
|
4
|
+
|
5
|
+
1. You may make and give away verbatim copies of the source form of the
|
6
|
+
software without restriction, provided that you duplicate all of the
|
7
|
+
original copyright notices and associated disclaimers.
|
8
|
+
|
9
|
+
2. You may modify your copy of the software in any way, provided that
|
10
|
+
you do at least ONE of the following:
|
11
|
+
|
12
|
+
a) place your modifications in the Public Domain or otherwise
|
13
|
+
make them Freely Available, such as by posting said
|
14
|
+
modifications to Usenet or an equivalent medium, or by allowing
|
15
|
+
the author to include your modifications in the software.
|
16
|
+
|
17
|
+
b) use the modified software only within your corporation or
|
18
|
+
organization.
|
19
|
+
|
20
|
+
c) give non-standard binaries non-standard names, with
|
21
|
+
instructions on where to get the original software distribution.
|
22
|
+
|
23
|
+
d) make other distribution arrangements with the author.
|
24
|
+
|
25
|
+
3. You may distribute the software in object code or binary form,
|
26
|
+
provided that you do at least ONE of the following:
|
27
|
+
|
28
|
+
a) distribute the binaries and library files of the software,
|
29
|
+
together with instructions (in the manual page or equivalent)
|
30
|
+
on where to get the original distribution.
|
31
|
+
|
32
|
+
b) accompany the distribution with the machine-readable source of
|
33
|
+
the software.
|
34
|
+
|
35
|
+
c) give non-standard binaries non-standard names, with
|
36
|
+
instructions on where to get the original software distribution.
|
37
|
+
|
38
|
+
d) make other distribution arrangements with the author.
|
39
|
+
|
40
|
+
4. You may modify and include the part of the software into any other
|
41
|
+
software (possibly commercial). But some files in the distribution
|
42
|
+
are not written by the author, so that they are not under these terms.
|
43
|
+
|
44
|
+
For the list of those files and their copying conditions, see the
|
45
|
+
file LEGAL.
|
46
|
+
|
47
|
+
5. The scripts and library files supplied as input to or produced as
|
48
|
+
output from the software do not automatically fall under the
|
49
|
+
copyright of the software, but belong to whomever generated them,
|
50
|
+
and may be sold commercially, and may be aggregated with this
|
51
|
+
software.
|
52
|
+
|
53
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
54
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
55
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
56
|
+
PURPOSE.
|
data/README.md
ADDED
@@ -0,0 +1,311 @@
|
|
1
|
+
# LLRB [![wercker status](https://app.wercker.com/status/71d808ff9de7f4f411714d40f9e99127/s/master "wercker status")](https://app.wercker.com/project/byKey/71d808ff9de7f4f411714d40f9e99127)
|
2
|
+
|
3
|
+
LLRB is a LLVM-based JIT compiler for Ruby.
|
4
|
+
|
5
|
+
## What's LLRB?
|
6
|
+
|
7
|
+
This is an experimental project to implement an idea presented by [@evanphx](https://github.com/evanphx) at [RubyKaigi 2015 Keynote](http://rubykaigi.org/2015/presentations/evanphx):<br>
|
8
|
+
Method JIT compiler inlining CRuby core functions using LLVM.
|
9
|
+
|
10
|
+
### How does it work?
|
11
|
+
|
12
|
+
On build time, some core functions are compiled to LLVM bitcode (binary form of LLVM IR) files via LLVM IR.
|
13
|
+
|
14
|
+
```
|
15
|
+
________ _________ ______________
|
16
|
+
| | | | | |
|
17
|
+
| CRuby | | CRuby | | CRuby |
|
18
|
+
| C code |-->| LLVM IR |-->| LLVM bitcode |
|
19
|
+
|________| |_________| |______________|
|
20
|
+
```
|
21
|
+
|
22
|
+
Those files are separated per function to load only necessary functions.
|
23
|
+
You can see how they are separated in [ext directory](./ext).
|
24
|
+
|
25
|
+
After profiler of LLRB JIT is started, when Ruby is running,
|
26
|
+
LLRB compiles Ruby method's YARV Instruction Sequence to native machine code.
|
27
|
+
|
28
|
+
```
|
29
|
+
______ ______________ __________ ___________ _________
|
30
|
+
| | | | | | | | | |
|
31
|
+
| YARV | | ISeq Control | | LLVM IR | | Optimized | | Machine |
|
32
|
+
| ISeq |-->| Flow Graph |-*->| for ISeq |-*->| LLVM IR |-*->| code |
|
33
|
+
|______| |______________| | |__________| | |___________| | |_________|
|
34
|
+
| | |
|
35
|
+
| Link | Optimize | JIT compile
|
36
|
+
______|_______ ___|____ __|____
|
37
|
+
| | | | | |
|
38
|
+
| CRuby | | LLVM | | LLVM |
|
39
|
+
| LLVM Bitcode | | Passes | | MCJIT |
|
40
|
+
|______________| |________| |_______|
|
41
|
+
```
|
42
|
+
|
43
|
+
### Does it improve performance?
|
44
|
+
|
45
|
+
Now basic instruction inlining is done. Let's see its effect.
|
46
|
+
|
47
|
+
Consider following Ruby method, which is the same as [ruby/benchmark/bm\_loop\_whileloop.rb](https://github.com/ruby/ruby/blob/v2_4_1/benchmark/bm_loop_whileloop.rb).
|
48
|
+
|
49
|
+
```rb
|
50
|
+
def while_loop
|
51
|
+
i = 0
|
52
|
+
while i<30_000_000
|
53
|
+
i += 1
|
54
|
+
end
|
55
|
+
end
|
56
|
+
```
|
57
|
+
|
58
|
+
The YARV ISeq, compilation target in LLRB, is this:
|
59
|
+
|
60
|
+
```
|
61
|
+
> puts RubyVM::InstructionSequence.of(method(:while_loop)).disasm
|
62
|
+
== disasm: #<ISeq:while_loop@(pry)>=====================================
|
63
|
+
== catch table
|
64
|
+
| catch type: break st: 0015 ed: 0035 sp: 0000 cont: 0035
|
65
|
+
| catch type: next st: 0015 ed: 0035 sp: 0000 cont: 0012
|
66
|
+
| catch type: redo st: 0015 ed: 0035 sp: 0000 cont: 0015
|
67
|
+
|------------------------------------------------------------------------
|
68
|
+
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
|
69
|
+
[ 1] i
|
70
|
+
0000 trace 8 ( 1)
|
71
|
+
0002 trace 1 ( 2)
|
72
|
+
0004 putobject_OP_INT2FIX_O_0_C_
|
73
|
+
0005 setlocal_OP__WC__0 3
|
74
|
+
0007 trace 1 ( 3)
|
75
|
+
0009 jump 25
|
76
|
+
0011 putnil
|
77
|
+
0012 pop
|
78
|
+
0013 jump 25
|
79
|
+
0015 trace 1 ( 4)
|
80
|
+
0017 getlocal_OP__WC__0 3
|
81
|
+
0019 putobject_OP_INT2FIX_O_1_C_
|
82
|
+
0020 opt_plus <callinfo!mid:+, argc:1, ARGS_SIMPLE>, <callcache>
|
83
|
+
0023 setlocal_OP__WC__0 3
|
84
|
+
0025 getlocal_OP__WC__0 3 ( 3)
|
85
|
+
0027 putobject 30000000
|
86
|
+
0029 opt_lt <callinfo!mid:<, argc:1, ARGS_SIMPLE>, <callcache>
|
87
|
+
0032 branchif 15
|
88
|
+
0034 putnil
|
89
|
+
0035 trace 16 ( 6)
|
90
|
+
0037 leave ( 4)
|
91
|
+
=> nil
|
92
|
+
```
|
93
|
+
|
94
|
+
By LLRB compiler, such YARV ISeq is compiled to LLVM IR like:
|
95
|
+
|
96
|
+
```llvm
|
97
|
+
define i64 @llrb_exec(i64, i64) {
|
98
|
+
label_0:
|
99
|
+
call void @llrb_insn_trace(i64 %0, i64 %1, i32 8, i64 52)
|
100
|
+
call void @llrb_insn_trace(i64 %0, i64 %1, i32 1, i64 52)
|
101
|
+
call void @llrb_insn_setlocal_level0(i64 %1, i64 3, i64 1)
|
102
|
+
call void @llrb_insn_trace(i64 %0, i64 %1, i32 1, i64 52)
|
103
|
+
br label %label_25
|
104
|
+
|
105
|
+
label_15: ; preds = %label_25
|
106
|
+
call void @llrb_insn_trace(i64 %0, i64 %1, i32 1, i64 52)
|
107
|
+
%2 = call i64 @llrb_insn_getlocal_level0(i64 %1, i64 3)
|
108
|
+
call void @llrb_set_pc(i64 %1, i64 94225474387824)
|
109
|
+
%opt_plus = call i64 @llrb_insn_opt_plus(i64 %2, i64 3)
|
110
|
+
call void @llrb_insn_setlocal_level0(i64 %1, i64 3, i64 %opt_plus)
|
111
|
+
br label %label_25
|
112
|
+
|
113
|
+
label_25: ; preds = %label_15, %label_0
|
114
|
+
%3 = call i64 @llrb_insn_getlocal_level0(i64 %1, i64 3)
|
115
|
+
call void @llrb_set_pc(i64 %1, i64 94225474387896)
|
116
|
+
%opt_lt = call i64 @llrb_insn_opt_lt(i64 %3, i64 60000001)
|
117
|
+
%RTEST_mask = and i64 %opt_lt, -9
|
118
|
+
%RTEST = icmp ne i64 %RTEST_mask, 0
|
119
|
+
br i1 %RTEST, label %label_15, label %label_34
|
120
|
+
|
121
|
+
label_34: ; preds = %label_25
|
122
|
+
call void @llrb_insn_trace(i64 %0, i64 %1, i32 16, i64 8)
|
123
|
+
call void @llrb_set_pc(i64 %1, i64 94225474387960)
|
124
|
+
call void @llrb_push_result(i64 %1, i64 8)
|
125
|
+
ret i64 %1
|
126
|
+
}
|
127
|
+
```
|
128
|
+
|
129
|
+
As LLRB compiler links precompiled LLVM bitcode of CRuby functions,
|
130
|
+
using LLVM's FunctionInliningPass ("Pass" is LLVM's optimizer),
|
131
|
+
some C functions are inlined and inlined code will be well optimized by Passes like InstCombinePass.
|
132
|
+
|
133
|
+
```llvm
|
134
|
+
define i64 @llrb_exec(i64, i64) #0 {
|
135
|
+
...
|
136
|
+
|
137
|
+
land.lhs.true.i: ; preds = %label_25
|
138
|
+
%49 = load %struct.rb_vm_struct*, %struct.rb_vm_struct** @ruby_current_vm, align 8, !dbg !3471, !tbaa !3472
|
139
|
+
%arrayidx.i = getelementptr inbounds %struct.rb_vm_struct, %struct.rb_vm_struct* %49, i64 0, i32 39, i64 7, !dbg !3471
|
140
|
+
%50 = load i16, i16* %arrayidx.i, align 2, !dbg !3471, !tbaa !3473
|
141
|
+
%and2.i = and i16 %50, 1, !dbg !3471
|
142
|
+
%tobool6.i = icmp eq i16 %and2.i, 0, !dbg !3471
|
143
|
+
br i1 %tobool6.i, label %if.then.i, label %if.else11.i, !dbg !3475, !prof !3380
|
144
|
+
|
145
|
+
if.then.i: ; preds = %land.lhs.true.i
|
146
|
+
call void @llvm.dbg.value(metadata i64 %48, i64 0, metadata !2680, metadata !3361) #7, !dbg !3477
|
147
|
+
call void @llvm.dbg.value(metadata i64 60000001, i64 0, metadata !2683, metadata !3361) #7, !dbg !3478
|
148
|
+
%cmp7.i = icmp slt i64 %48, 60000001, !dbg !3479
|
149
|
+
%..i = select i1 %cmp7.i, i64 20, i64 0, !dbg !3481
|
150
|
+
br label %llrb_insn_opt_lt.exit
|
151
|
+
|
152
|
+
if.else11.i: ; preds = %land.lhs.true.i, %label_25
|
153
|
+
%call35.i = call i64 (i64, i64, i32, ...) @rb_funcall(i64 %48, i64 60, i32 1, i64 60000001) #7, !dbg !3483
|
154
|
+
br label %llrb_insn_opt_lt.exit, !dbg !3486
|
155
|
+
|
156
|
+
llrb_insn_opt_lt.exit: ; preds = %if.then.i, %if.else11.i
|
157
|
+
%retval.1.i = phi i64 [ %..i, %if.then.i ], [ %call35.i, %if.else11.i ]
|
158
|
+
%RTEST_mask = and i64 %retval.1.i, -9
|
159
|
+
%RTEST = icmp eq i64 %RTEST_mask, 0
|
160
|
+
|
161
|
+
...
|
162
|
+
}
|
163
|
+
```
|
164
|
+
|
165
|
+
In this example, you can see many things are inlined.
|
166
|
+
LLRB's compiled code fetches RubyVM state and check whether `<` method is redefined or not,
|
167
|
+
and if `<` is not redefined, `if.then.i` block is used and in that block `icmp slt` is used instead of calling Ruby method `#<`.
|
168
|
+
Note that it's done by just inlining YARV's `opt_lt` instruction directly and it's not hard to implement.
|
169
|
+
|
170
|
+
Thus, following benchmark shows the performance is improved.
|
171
|
+
|
172
|
+
```rb
|
173
|
+
ruby = Class.new
|
174
|
+
def ruby.script
|
175
|
+
i = 0
|
176
|
+
while i< 30_000_000
|
177
|
+
i += 1
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
llrb = Class.new
|
182
|
+
def llrb.script
|
183
|
+
i = 0
|
184
|
+
while i< 30_000_000
|
185
|
+
i += 1
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
LLRB::JIT.compile(llrb, :script)
|
190
|
+
|
191
|
+
Benchmark.ips do |x|
|
192
|
+
x.report('Ruby') { ruby.script }
|
193
|
+
x.report('LLRB') { llrb.script }
|
194
|
+
x.compare!
|
195
|
+
end
|
196
|
+
```
|
197
|
+
|
198
|
+
```
|
199
|
+
# x86_64 GNU/Linux, Intel(R) Core(TM) i7-4790K CPU @ 4.00GHz
|
200
|
+
Calculating -------------------------------------
|
201
|
+
Ruby 2.449 (± 0.0%) i/s - 13.000 in 5.308125s
|
202
|
+
LLRB 8.533 (± 0.0%) i/s - 43.000 in 5.040016s
|
203
|
+
|
204
|
+
Comparison:
|
205
|
+
LLRB: 8.5 i/s
|
206
|
+
Ruby: 2.4 i/s - 3.48x slower
|
207
|
+
```
|
208
|
+
|
209
|
+
## How is the design?
|
210
|
+
### Built as C extension
|
211
|
+
|
212
|
+
Currently LLRB is in an experimental stage. For fast development and to stay up-to-date
|
213
|
+
for CRuby core changes, LLRB is built as C extension.
|
214
|
+
We can use bundler, benchmark-ips, pry, everything else in normal C extension repository. It's hard in just CRuby fork.
|
215
|
+
|
216
|
+
For optimization, unfortunately it needs to export some symbols from CRuby,
|
217
|
+
so it needs to compile [k0kubun/ruby's llrb branch](https://github.com/k0kubun/ruby/tree/llrb)
|
218
|
+
and install llrb.gem from that Ruby.
|
219
|
+
|
220
|
+
But I had the rule that I don't add modification except exporting symbols.
|
221
|
+
And LLRB code refers to such exported functions or variables in CRuby core by including CRuby header as possible.
|
222
|
+
I believe it contributes to maintainability of LLRB prototype.
|
223
|
+
|
224
|
+
### Conservative design for safety
|
225
|
+
|
226
|
+
YARV is already proven to be reliable. In LLRB, YARV is not modified at all.
|
227
|
+
Then, how is JIT achieved?
|
228
|
+
|
229
|
+
YARV has `opt_call_c_function` instruction, which is explained to ["call native compiled method"](https://github.com/ruby/ruby/blob/v2_4_1/insns.def#L2136).
|
230
|
+
Why not use that?
|
231
|
+
|
232
|
+
LLRB compiles any ISeq to following ISeq.
|
233
|
+
|
234
|
+
```
|
235
|
+
0000 opt_call_c_function
|
236
|
+
0002 leave
|
237
|
+
```
|
238
|
+
|
239
|
+
So simple. Note that `opt_call_c_function` [can handle YARV's internal exception](https://github.com/ruby/ruby/blob/v2_4_1/insns.def#L2147-L2151).
|
240
|
+
In that instruction, we can do everything.
|
241
|
+
|
242
|
+
One more conservative thing in LLRB is that it fills `leave` instructions to remaining places.
|
243
|
+
To let YARV catch table work, it needs to update program counter properly,
|
244
|
+
and then it requires an instruction to the place that program counter points to.
|
245
|
+
|
246
|
+
For safe exit when catch table is used, `leave` instructions are filled to the rest of first `opt_call_c_function`.
|
247
|
+
|
248
|
+
### Sampling-based lightweight profiler
|
249
|
+
|
250
|
+
Sampling profiler is promising approach to reduce the overhead of profiling without spoiling profiling efficiency.
|
251
|
+
LLRB's profiler to schedule JIT-ed ISeq is implemented in almost the same way as [stackprof](https://github.com/tmm1/stackprof).
|
252
|
+
It is widely-used on production and proven to be a reliable approach.
|
253
|
+
|
254
|
+
It kicks profiler in some CPU-time interval, and the parameter can be modified if necessary.
|
255
|
+
Using [optcarrot](https://github.com/mame/optcarrot) benchmark, I tested profiler overhead
|
256
|
+
and LLRB's profiler didn't reduce the fps of optcarrot with current parameter.
|
257
|
+
|
258
|
+
Also, it uses `rb_postponed_job_register_one` API, which is used by stackprof too, to do JIT compile.
|
259
|
+
So the compilation is done in very safe timing.
|
260
|
+
|
261
|
+
### Less compilation effort
|
262
|
+
|
263
|
+
CRuby's C functions to inline are precompiled as LLVM bitcode on LLRB build process.
|
264
|
+
LLRB's compiler builds LLVM IR using LLVM IRBuilder, so the bitcode files are directly linked to that.
|
265
|
+
|
266
|
+
It means that LLRB has no overhead of parsing and compiling C source code on runtime.
|
267
|
+
It has less compilation effort, right?
|
268
|
+
|
269
|
+
Currently the performance bottleneck is not in compiler, unfortunately!
|
270
|
+
So it doesn't use extra thread for JIT compilation for now.
|
271
|
+
|
272
|
+
## Project status
|
273
|
+
|
274
|
+
Experimental. Not matured at all.
|
275
|
+
|
276
|
+
Everything I said above is already implemented, but that's all!
|
277
|
+
Core function inlining is achieved but it's not completely applied to all instructions
|
278
|
+
and we need MUCH MORE effort to improve performance in real-world application.
|
279
|
+
|
280
|
+
## Build dependency
|
281
|
+
|
282
|
+
- **64bit CPU**
|
283
|
+
- This should be fixed later
|
284
|
+
- LLVM/clang 3.8+
|
285
|
+
- `llvm-config`, `clang` and `llvm-as` commands need to appear in `$PATH`
|
286
|
+
- [CRuby fork in k0kubun/ruby's llrb branch](https://github.com/k0kubun/ruby/tree/llrb)
|
287
|
+
|
288
|
+
## Usage
|
289
|
+
|
290
|
+
Once build dependency is met, execute `gem install llrb` and do:
|
291
|
+
|
292
|
+
```ruby
|
293
|
+
require 'llrb/start'
|
294
|
+
```
|
295
|
+
|
296
|
+
`llrb/start` file does `LLRB::JIT.start`. Note that you can also do that by `ruby -rllrb/start -e "..."`.
|
297
|
+
|
298
|
+
If you want to see which method is compiled, compile the gem with `#define LLRB_ENABLE_DEBUG 1`.
|
299
|
+
Again, it's in an experimental stage and currently it doesn't improve performance in real-world application.
|
300
|
+
|
301
|
+
## TODOs
|
302
|
+
|
303
|
+
- Improve performance...
|
304
|
+
- Implement ISeq method inlining
|
305
|
+
- Support all YARV instructions
|
306
|
+
- `expandarray`, `reverse`, `reput`, `defineclass`, `once`, `opt_call_c_function` are not supported yet.
|
307
|
+
- Care about unexpectedly GCed object made during compilation
|
308
|
+
|
309
|
+
## License
|
310
|
+
|
311
|
+
The same as Ruby.
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
|
4
|
+
RSpec::Core::RakeTask.new(:spec)
|
5
|
+
|
6
|
+
require 'rake/extensiontask'
|
7
|
+
|
8
|
+
task :build => :compile
|
9
|
+
|
10
|
+
Rake::ExtensionTask.new('llrb') do |ext|
|
11
|
+
ext.lib_dir = 'lib/llrb'
|
12
|
+
end
|
13
|
+
|
14
|
+
task :default => [:compile, :spec]
|
15
|
+
|
16
|
+
desc 'Clone optcarrot'
|
17
|
+
file :optcarrot do
|
18
|
+
sh 'git clone --depth 1 https://github.com/mame/optcarrot'
|
19
|
+
Dir.chdir("#{__dir__}/optcarrot") do
|
20
|
+
File.write('Gemfile', "#{File.read('Gemfile')}\ngem 'llrb', path: '..'\n")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
desc 'Run optcarrot benchmark'
|
25
|
+
task 'optcarrot:run' => :optcarrot do
|
26
|
+
Dir.chdir("#{__dir__}/optcarrot") do
|
27
|
+
sh "bundle check || bundle install -j#{`nproc`.rstrip}"
|
28
|
+
sh 'bundle exec ruby -I../lib -rllrb/start bin/optcarrot --benchmark examples/Lan_Master.nes'
|
29
|
+
end
|
30
|
+
end
|