[ruby][java] JRuby ã®ä¸¦åå®è¡ã§ã¡ã½ããã®è¿½å ã®å®è£ æ¹æ³
ææã«ã¦ã JRuby ã®ã¹ã¬ããã¯æ¬å½ã«ä¸¦åã§èµ°ã(global interpreter lock ã¯ä½¿ã£ã¦ããªã)ã¨ãã話ãèãã¾ãããRuby (大æåã§å§ã¾ãã®ã§è¨èªã®ã»ãã) ã§ã¯ãdefine_method ã¨ãåçãªä»çµã¿ãããããã¨ããã®ã§ãå®è£
ã¯å¤§å¤ãªããããªãããããã¨æãã JRuby ã®ã½ã¼ã¹ãä¹
ãæ¯ãã«çºãã¦ã¿ããã¨ã«ãã¾ããã
ã¡ã½ããã®è¿½å ã¨ãããã¨å¯¾ã«ãªãã¡ã½ããå¼ã³åºã(æ¢ç´¢)ã ãã«æ³¨ç®ãã¦ããã®ã§ãã»ãã®ä¸é¨ããç®ãéãã¦ãã¾ãããæ³¨ç®ããã¨ããã¯ã以ä¸ã® 2 ç¹ã®ã¿ã§ãã
- ã¡ã½ããè¿½å æã«åå¾ããããã¯ã¯ãªã«ã
- ã¡ã½ããå¼ã³åºã(æ¢ç´¢)æã«ããã¯ãåå¾ããã®ãããããªãé »åº¦ã¨ããã¯ã®ç¨®é¡
This is a community wiki dedicated to JRuby, an implementation of the Ruby programming language atop the Java Virtual Machine (JVM).
JRuby: Wiki: Home ― Project Kenai
対象ãã¼ã¸ã§ã³: 1.4.0
追ãæããããããªã¡ã½ãã追å å´
Emacs ã§ rgrep define_method ãRubyModule#define_method ãåºã¦ããã®ã§ãããã§ééããªãããããããã£ã±ã«ã¾ã¨ããã¨ã
- module åä½ã§ token ããã£ã¦åãä¸ä»£ç®¡çãã¦ãã (åã®åå㨠token ã®çµã§èå¥)
- ã¡ã½ããè¿½å æã«ã¯ãmodule åä½ã®ããã¯ãåå¾
- ãã®ããã¯ãæã£ãä¸ã§ã
- ã¡ã½ããã® Map ãæ´æ°ãã¦
- ã¡ã½ããæ¢ç´¢ç¨ã®ãã£ãã·ã¥ãæ´æ°ãã
- ãã®æã¯ãã°ãã¼ãã«ãªãã£ãã·ã¥ãåã£ã¦
- æ´æ°ã§å½±é¿ãåããåãªãã¸ã§ã¯ãã«å¯¾ãã token ãæ´æ°ãã¦ãã
以ä¸ãã³ã¼ãèªãã ã¨ãã®èµ°ãæ¸ãã
RubyModule.java
L. 1348
@JRubyMethod(name = "define_method", frame = true, visibility = PRIVATE,
reads = VISIBILITY)
public IRubyObject define_method(ThreadContext context, IRubyObject arg0,
Block block) {
Ruby runtime = context.getRuntime();
...
RuntimeHelpers.addInstanceMethod(this, name, newMethod,
context.getPreviousVisibility(), context, runtime);
L. 885 addMethod
L. 895 addMethodInternal
synchronized(getMethods()) {
addMethodAtBootTimeOnly(name, method);
// -> methods#put ãã¦ãã
invalidateCacheDescendants();
// ->
// 1. generation ã®æ´æ° (token ã new Object() ã§ä¸æ¸ã)
// 2. getRuntime().getHierarchyLock() ã®ããã¯(ã°ãã¼ãã«)ãæã£ã¦
// includingHierarchy.invalidateCacheDescendants();
}
L. 369 getMethods
methods ã® getter
L. 199 private final Map<String, DynamicMethod> methods =
new ConcurrentHashMap<String, DynamicMethod>(4, 0.9f, 1);
RuntimeHelpers (org.jruby.javasupport.util)
L. 1398 addInstanceMethod
L. 1403 call addModuleMethod
L. 1409 addModuleMethod
L. 1410
containingClass.getSingletonClass().addMethod(name, new WrapperMethod(
containingClass.getSingletonClass(), method, Visibility.PUBLIC));
// containingClass: RubyModule
ã¡ã½ããå¼ã³åºã(æ¢ç´¢)å´
org.jruby.runtime.Callsite ããã®åããªã¼ãå¼ã³åºãå´ã£ã½ãã
ã¶ãã¨ã¾ã¨ãã
- åºæ¬ã¯ããã¯ãåããªãã
- ã¨ãã©ã(0xFF + 1 = 256)ãsynchronized ãããã¦ã¹ã¬ããã¯ã¼ãã³ã°ã¡ã¢ãªã追ãåºãã
- token ã調ã¹ã¦ãå¤ãã£ããçé¢ç®ã«ã¡ã½ãããæ¢ç´¢ãã(親ãªã©ã追ãæãã)ã
èµ°ãæ¸ãã
CashingCallsite.java
L. 63 call
L. 64 callAndGetClass
ThreadContext#callThreadPoll (L.502) // 0xFF åãã¨ã«ã¯ synchronized ãã
L. 66 CacheEntry#typeOK
// ãã£ãã·ã¥ã®æå¹æ§ç¢ºèª
L. 69 cacheAndCall
L. 274 selfType.searchWithCache // selfType: RubyClass
L. 277 callMethodmissing
L. 280 method.call // method: DynamicMethod
RubyModule
L. 962 searchWithCache
L. 963 cacheHit // ã¡ã½ãããã£ãã·ã¥ããååã§å¼ã㦠token ãå½ã¦ã
åãããããã return
L. 969 åããªãã¨ãã¯ãé層ãä¸ã«æ¢ç´¢ãã¦ããã£ãã·ã¥ãã¦è¿ã
L. 967 - 968 ãã®å¦çã¸ã®ã³ã¡ã³ã
// we grab serial number first; the worst that will happen is we cache a later
// update with an earlier serial number, which would just flush anyway
// 訳: å
ã«ã·ãªã¢ã«ãã³ãã¼(token)ãåããææªã§ãããã£ãã·ã¥ãããã¡ã½ããã«
// 対ãã token ãå¤ããªãããããã¯ãã¨ã§æ¨ã¦ãããã ãã§ããã
å®éã«åããã¦ã¿ã
- ããã¯ç«¶åãã»ã¨ãã§ç¡ãã§ããã fibonacchi ã one thread, two threads ã§è¨ç®
- åãã¯ã©ã¹ã®åãã¡ã½ããã define/undefine
- åãã¯ã©ã¹ã®éãã¡ã½ããã define/undefine
- éãã¯ã©ã¹ã®ã¡ã½ããã define/undefine
ã³ã¼ã㯠gist
difine_method_bench.rb
gist: 235787 - GitHub
ç°å¢
% sw_vers
ProductName: Mac OS X
ProductVersion: 10.6.2
BuildVersion: 10C540
% system_profiler SPHardwareDataType | grep Processor
Processor Name: Intel Core 2 Duo
Processor Speed: 2.2 GHz
Number Of Processors: 1
% ï¿£/local/jruby-1.4.0/bin/jruby --version
jruby 1.4.0 (ruby 1.8.7 patchlevel 174) (2009-11-02 69fbfa3)
(Java HotSpot(TM) 64-Bit Server VM 1.6.0_15) [x86_64-java]çµæ
2 -> 3 -> 4 ã®é ã§å®è¡æéãçããªã£ã¦ãããã¨ãåãããä¸ã®ã½ã¼ã¹èªã¿ã¨æ´åæ§ã¯åãã¦ããããã ããå®å
¨ã«ã¯èª¬æã§ãã¦ããããã§ã¯ãªãã
% ï¿£/local/jruby-1.4.0/bin/jruby define_method_bench.rb 38 2000000
Almost no lock contention (calculate Fibonacchi sequence)
user system total real
1 thread (0) 12.088000 0.000000 12.088000 ( 12.038000)
2 threads (0) 14.528000 0.000000 14.528000 ( 14.528000)
1 thread (1) 12.234000 0.000000 12.234000 ( 12.235000)
2 threads (1) 17.308000 0.000000 17.308000 ( 17.308000)
1 thread (2) 12.030000 0.000000 12.030000 ( 12.031000)
2 threads (2) 15.870000 0.000000 15.870000 ( 15.870000)
1 thread (3) 12.298000 0.000000 12.298000 ( 12.298000)
2 threads (3) 15.762000 0.000000 15.762000 ( 15.762000)
Add/remove the SAME method for the SAME class object
user system total real
1 thread (0) 4.203000 0.000000 4.203000 ( 4.202000)
2 threads (0) 7.575000 0.000000 7.575000 ( 7.575000)
1 thread (1) 3.629000 0.000000 3.629000 ( 3.629000)
2 threads (1) 7.204000 0.000000 7.204000 ( 7.205000)
1 thread (2) 3.545000 0.000000 3.545000 ( 3.545000)
2 threads (2) 7.292000 0.000000 7.292000 ( 7.291000)
1 thread (3) 3.684000 0.000000 3.684000 ( 3.684000)
2 threads (3) 7.060000 0.000000 7.060000 ( 7.060000)
Add/remove different methods for the SAME class object
user system total real
1 thread (0) 3.758000 0.000000 3.758000 ( 3.758000)
2 threads (0) 5.774000 0.000000 5.774000 ( 5.774000)
1 thread (1) 3.840000 0.000000 3.840000 ( 3.840000)
2 threads (1) 5.938000 0.000000 5.938000 ( 5.938000)
1 thread (2) 3.709000 0.000000 3.709000 ( 3.709000)
2 threads (2) 5.482000 0.000000 5.482000 ( 5.483000)
1 thread (3) 3.749000 0.000000 3.749000 ( 3.749000)
2 threads (3) 5.614000 0.000000 5.614000 ( 5.613000)
Add/remove different methods for different class objects
user system total real
1 thread (0) 3.805000 0.000000 3.805000 ( 3.805000)
2 threads (0) 5.156000 0.000000 5.156000 ( 5.156000)
1 thread (1) 3.662000 0.000000 3.662000 ( 3.662000)
2 threads (1) 4.900000 0.000000 4.900000 ( 4.900000)
1 thread (2) 3.656000 0.000000 3.656000 ( 3.656000)
2 threads (2) 4.611000 0.000000 4.611000 ( 4.611000)
1 thread (3) 3.735000 0.000000 3.735000 ( 3.735000)
2 threads (3) 4.800000 0.000000 4.800000 ( 4.800000)
è£è¶³
Java ä»®æ³ãã·ã³ã®ä»æ§ã§ã synchronized (修飾å or ãããã¯)ã«ãããã¹ã¬ããã®ã¯ã¼ãã³ã°ã¡ã¢ãªã¨ã¡ã¤ã³ã¡ã¢ãªã®åæè¦æ±ã«é¢ãã¦ã¯ãçµåããã®æ¬ã«ã«èª¬æããã£ã¦ããããåããããã(æ¬æãããªãã¦ã³ã©ã ãã¢ãã³ãã£ãã¯ã¹ã ã£ããã)ã
Amazon.co.jp: 増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編: 結城 浩: 本
ãã°ã»ãªã¼å çã®ãã¼ã¸ã«ãã£ããã¨ãã説æãããã
In essence, releasing a lock forces a flush of all writes from
Synchronization and the Java Memory Model
working memory employed by the thread, and acquiring a lock
forces a (re)load of the values of accessible fields. While lock
actions provide exclusion only for the operations performed
within a synchronized method or block, these memory effects are
defined to cover all fields used by the thread performing the
action.
Note the double meaning of synchronized: it deals with locks that
permit higher-level synchronization protocols, while at the same
time dealing with the memory system (sometimes via low-level
memory barrier machine instructions) to keep value
representations in synch across threads. This reflects one way in
which concurrent programming bears more similarity to distributed
programming than to sequential programming. The latter sense of
synchronized may be viewed as a mechanism by which a method
running in one thread indicates that it is willing to send and/or
receive changes to variables to and from methods running in other
threads. From this point of view, using locks and passing
messages might be seen merely as syntactic variants of each
other.