<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆 - 4, 文章 - 0, 評論 - 2, 引用 - 0
    數(shù)據(jù)加載中……

    Groovy深入探索——Call Site分析

    Groovy 1.6引入了Call Site優(yōu)化。Call Site優(yōu)化實(shí)際上就是方法選擇的cache。

    方法選擇
    在靜態(tài)語言(如Java)中,方法調(diào)用的綁定是在編譯期完成的(不完全是這樣,如虛函數(shù),但總的來說,靜態(tài)語言的方法調(diào)用是非常高效的)。而在動態(tài)語言(如Groovy)中,調(diào)用的方法是在運(yùn)行時(shí)選擇的。這也是造成動態(tài)語言比靜態(tài)語言慢的重要原因之一。
    舉個例子來說明,譬如要調(diào)用“a.call(1)”。
    如果是Java的話,在編譯期就會選擇好調(diào)用的方法,在這個例子中,就是選擇a對象聲明的類(注意,不是a對象真正的類,因?yàn)檎嬲念愐竭\(yùn)行時(shí) 才能知道)中,名字為call、有一個參數(shù)、參數(shù)類型為int的方法(實(shí)際情況要復(fù)雜很多,譬如還要考慮boxing和變參等情況),如果找不到的話則編 譯不通過,否則進(jìn)行方法綁定。反匯編這個方法調(diào)用的話可以看到如“invokevirtual #4; //Method call:(I)V”的指令,表明方法是綁定好的,包括方法名字“call”,參數(shù)類型“I”(int),返回值“V”(void)。
    如果是Groovy的話,這些都是由Groovy運(yùn)行時(shí)完成的,Groovy對代碼進(jìn)行編譯時(shí)并不會檢查到底有沒有一個方法能匹配這個調(diào)用。用 Groovy 1.5.7進(jìn)行編譯,再反編譯為Java代碼之后,可以看到如 “ScriptBytecodeAdapter.invokeMethodN(class1, a, "call", new Object[] { new Integer(1) })”的語句,由此看出Groovy在編譯時(shí)并沒有真正的選擇調(diào)用的方法,而是交由運(yùn)行時(shí)決定。

    Call Site
    根據(jù)wikipedia的定義(http://en.wikipedia.org/wiki/Call_site),Call Site是一行方法的調(diào)用,譬如:
    = sqr(b);
    = sqr(b);
    是兩個Call Site。

    Call Site優(yōu)化
    在Groovy 1.6之前,對于同一個Call Site來說,調(diào)用該方法n次,則會進(jìn)行n次的方法選擇,譬如:
    for (i in 1..3) {
        a.call(i)
    }
    這里,Groovy對call方法就進(jìn)行了3次的選擇,即使3次選擇的結(jié)果都是一樣的。
    Groovy 1.6引入的Call Site優(yōu)化,則是把同一個Call Site的方法選擇結(jié)果緩存起來,如果下一次調(diào)用時(shí)的參數(shù)類型一樣,則調(diào)用該緩存起來的方法,否則重新選擇。這就是Call Site優(yōu)化的基本思想。

    代碼分析
    考慮以下的Groovy代碼:
    class A {
        def a() {}
        def b() {}
        def b(
    int i) {}
    }

    class B {
        def a 
    = new A()
        def c() {
            a.a()
            d()
        }
        def d() {
            a.a()
            a.b()
            a.b(
    1)
        }
    }
    我們先用Groovy 1.6對這段代碼進(jìn)行編譯,然后再用jad對編譯后的class文件進(jìn)行反編譯。因?yàn)锳類中的方法都是空的,所以我們只看B類的反編譯結(jié)果。下面是B類中的c()和d()方法:
        public Object c()
        {
            CallSite acallsite[] 
    = $getCallSiteArray();
            acallsite[
    1].call(a);
            
    return acallsite[2].callCurrent(this);
        }

        
    public Object d()
        {
            CallSite acallsite[] 
    = $getCallSiteArray();
            acallsite[
    3].call(a);
            acallsite[
    4].call(a);
            
    return acallsite[5].call(a, $const$0); // $const$0就是常量1
        }
    我們來看看$getCallSiteArray():
        private static CallSiteArray $createCallSiteArray()
        {
            
    return new CallSiteArray($ownClass, new String[] {
                
    "<$constructor$>""a""d""a""b""b" // 每個Call Site的方法名字
            });
        }

        
    private static CallSite[] $getCallSiteArray()
        {
            CallSiteArray callsitearray;
            
    if($callSiteArray == null || (callsitearray = (CallSiteArray)$callSiteArray.get()) == null)
            {
                callsitearray 
    = $createCallSiteArray();
                $callSiteArray 
    = new SoftReference(callsitearray);
            }
            
    return callsitearray.array;
        }
    $getCallSiteArray()實(shí)際上就是對$callSiteArray的lazy創(chuàng)建。
    我們可以看到,“acallsite[1].call(a);”就是對方法名為"a"的CallSite進(jìn)行調(diào)用,而“acallsite[2].callCurrent(this);”則是對方法名為“d”的CallSite進(jìn)行調(diào)用,如此類推。
    我們再來看看CallSiteArray的構(gòu)造函數(shù)里做些什么:
        public CallSiteArray(Class owner, String [] names) {
            
    this.owner = owner;
            array 
    = new CallSite[names.length];
            
    for (int i = 0; i < array.length; i++) {
                array[i] 
    = new AbstractCallSite(this, i, names[i]);
            }
        }
    所以,第一次調(diào)用“acallsite[1].call(a);“時(shí),就是調(diào)用AbstractCallSite類的call方法。下面是該方法的代碼:
        public Object call(Object receiver, Object[] args) throws Throwable {
            
    return CallSiteArray.defaultCall(this, receiver, args);
        }
    再看看CallSiteArray.defaultCall()的代碼:
        public static Object defaultCall(CallSite callSite, Object receiver, Object[] args) throws Throwable {
            
    return createCallSite(callSite, receiver, args).call(receiver, args);
        }
        
        
    private static CallSite createCallSite(CallSite callSite, Object receiver, Object[] args) {
            CallSite site;
            
    if (receiver == null)
              
    return new NullCallSite(callSite);

            
    if (receiver instanceof Class)
              site 
    = createCallStaticSite(callSite, (Class) receiver, args);
            
    else if (receiver instanceof GroovyObject) {
                site 
    = createPogoSite(callSite, receiver, args); // 我們只考慮這種情況
            } else {
                site 
    = createPojoSite(callSite, receiver, args);
            }

            replaceCallSite(callSite, site); 
    // 替換CallSite
            return site;
        }

        
    private static void replaceCallSite(CallSite oldSite, CallSite newSite) {
            oldSite.getArray().array [oldSite.getIndex()] 
    = newSite;
        }
    可以看到createCallSite()最后通過調(diào)用replaceCallSite()把舊的CallSite替換為新的CallSite,因此第二次 調(diào)用“acallsite[1].call(a);”時(shí)就是直接調(diào)用新的CallSite,也就是說該CallSite被緩存起來了。
    我們在這里只考慮POGO的情況,即createPogoSite()方法。而POJO的情況稍微復(fù)雜一點(diǎn),因?yàn)樯婕暗絇OJO per-instance metaclass的情況(我將在下一篇文章中分析它的實(shí)現(xiàn))。下面是createPogoSite()的代碼:
        private static CallSite createPogoSite(CallSite callSite, Object receiver, Object[] args) {
            
    if (receiver instanceof GroovyInterceptable)
              
    return new PogoInterceptableSite(callSite);

            MetaClass metaClass 
    = ((GroovyObject)receiver).getMetaClass();
            
    if (metaClass instanceof MetaClassImpl) {
                
    return ((MetaClassImpl)metaClass).createPogoCallSite(callSite, args); // 我們只考慮這種情況
            }

            
    return new PogoMetaClassSite(callSite, metaClass);
        }
    我們只考慮對象的metaclass是MetaClassImpl的情況(這也是Groovy對象的默認(rèn)情況)。下面是MetaClassImpl.createPogoCallSite()的代碼:
        public CallSite createPogoCallSite(CallSite site, Object[] args) {
            
    if (site.getUsage().get() == 0 && !(this instanceof AdaptingMetaClass)) {
                Class [] params 
    = MetaClassHelper.convertToTypeArray(args); // 獲取參數(shù)的類型
                MetaMethod metaMethod = getMethodWithCachingInternal(theClass, site, params); // 選擇方法
                if (metaMethod != null)
                   
    return PogoMetaMethodSite.createPogoMetaMethodSite(site, this, metaMethod, params, args); // 如果找到匹配的方法,則創(chuàng)建一個PogoMetaMethodSite,并把找到的方法綁定其中
            }
            
    return new PogoMetaClassSite(site, this); //否則創(chuàng)建一個PogoMetaClassSite
        }
    PogoMetaMethodSite.createPogoMetaMethodSite()就是用來根據(jù)不同的情況創(chuàng)建PogoMetaMethodSite或它的子類的一個實(shí)例。我們最后來看看PogoMetaMethodSite.call()方法:
        public Object call(Object receiver, Object[] args) throws Throwable {
            
    if(checkCall(receiver, args)) { // 如果參數(shù)類型相同,則調(diào)用綁定的方法
                try {
                    
    return invoke(receiver,args); // 調(diào)用綁定的方法
                } catch (GroovyRuntimeException gre) {
                    
    throw ScriptBytecodeAdapter.unwrap(gre);
                }
            } 
    else { // 否則創(chuàng)建新的CallSite,即再次進(jìn)行方法查找
                return CallSiteArray.defaultCall(this, receiver, args);
            }
        }

        
    protected boolean checkCall(Object receiver, Object[] args) {
            
    try {
                
    return usage.get() == 0
                   
    && ((GroovyObject)receiver).getMetaClass() == metaClass // metaClass still be valid
                   && MetaClassHelper.sameClasses(params, args); // 檢查參數(shù)類型是否一樣
            }
            
    catch (NullPointerException e) {
                
    if (receiver == null)
                  
    return false;
                
    throw e;
            }
            
    catch (ClassCastException e) {
                
    if (!(receiver instanceof GroovyObject))
                  
    return false;
                
    throw e;
            }
        }

    最后,我們來再次總結(jié)這個過程:
    第一次調(diào)用“acallsite[1].call(a)“時(shí),通過CallSiteArray.createCallSite()方法創(chuàng)建了 PogoMetaMethodSite類的一個新CallSite,并把默認(rèn)的AbstractCallSite覆蓋掉。在創(chuàng)建 PogoMetaMethodSite的過程中,將進(jìn)行方法的選擇,并把找到的方法綁定到PogoMetaMethodSite中。最后就是調(diào)用該方法:
    當(dāng)?shù)诙握{(diào)用“acallsite[1].call(a)“時(shí),就是直接調(diào)用PogoMetaMethodSite.call(),這時(shí)候 PogoMetaMethodSite.call()就會檢查傳入的參數(shù)類型是否與綁定的方法(即上次找到的方法)的參數(shù)類型相同,相同則調(diào)用該綁定的方 法,否則將再次調(diào)用CallSiteArray.createCallSite()方法,創(chuàng)建一個新的CallSite對象,并重新進(jìn)行方法選擇。

    除了普通的方法調(diào)用的情況外,還有調(diào)用當(dāng)前對象方法、獲取/設(shè)置屬性、調(diào)用構(gòu)造函數(shù)、調(diào)用靜態(tài)函數(shù)的情況,在此不再做詳細(xì)分析,有興趣的可以直接查閱Groovy的源代碼。

    以上分析有不當(dāng)之處敬請指出,謝謝大家的閱讀。

    posted on 2009-03-16 09:39 Johnny Jian 閱讀(2368) 評論(1)  編輯  收藏 所屬分類: Groovy

    評論

    # re: Groovy深入探索——Call Site分析  回復(fù)  更多評論   

    寫得很好 :)
    2009-03-16 20:22 | 山風(fēng)小子
    主站蜘蛛池模板: 国产精品区免费视频| 成年女人看片免费视频播放器| 亚洲成年轻人电影网站www| 亚洲人成网www| 无码高潮少妇毛多水多水免费| 国产综合激情在线亚洲第一页| 国产亚洲成av片在线观看| 18勿入网站免费永久| 特级毛片爽www免费版| 亚洲色图视频在线观看| 免费观看日本污污ww网站一区| 四虎影视无码永久免费| 亚洲欧美日韩一区二区三区在线| 18观看免费永久视频| 黄页视频在线观看免费| 亚洲AV无码一区二区二三区入口 | av成人免费电影| 亚洲精品福利网泷泽萝拉| 无码专区一va亚洲v专区在线 | 亚洲国产精品一区二区第一页免 | 久久精品国产亚洲一区二区三区| 1000部羞羞禁止免费观看视频| 豆国产96在线|亚洲| 精品亚洲综合久久中文字幕| 最新猫咪www免费人成| 亚洲国产精品久久久天堂| 热99re久久精品精品免费| 蜜桃视频在线观看免费视频网站WWW| 亚洲AV无码一区二区三区牲色| 亚洲三级电影网址| 中文字幕精品亚洲无线码一区应用| 亚洲人成网站免费播放| 久久青草免费91线频观看站街| 日韩免费高清一级毛片| 亚洲精品123区在线观看| 久久亚洲国产成人精品性色| 久久久青草青青国产亚洲免观| 国产青草视频免费观看97| 色费女人18女人毛片免费视频| 亚洲国产精品成人精品软件| 国产亚洲免费的视频看|