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

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

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

    莊周夢蝶

    生活、程序、未來
       :: 首頁 ::  ::  :: 聚合  :: 管理

    高性能EL——Fel探秘,兼談EL

    Posted on 2011-09-17 12:52 dennis 閱讀(9936) 評論(5)  編輯  收藏 所屬分類: java源碼解讀計算機科學與基礎
        Fel是最近javaeye比較火的關鍵詞,這是由網友lotusyu開發的一個高性能的EL,從作者給出的數據來看,性能非常優異,跟前段時間溫少開源的Simple EL有的一拼。首先要說,這是個好現象,國內的開源項目越來越多,可以看出開發者的水平是越來越高了,比如我最近還看到有人開源的類似kestel的輕量級MQ——fqueue也非常不錯,有興趣可以看下我的分析《fqueue初步分析》。

        進入正文,本文是嘗試分析下Fel的實現原理,以及優缺點和aviator——我自己開源的EL之間的簡單比較。

        Fel的實現原理跟Simple EL是類似,都是使用template生成中間代碼——也就是普通的java代碼,然后利用javac編譯成class,最后運行,當然,這個過程都是動 態的。JDK6已經引入了編譯API,在此之前的版本可以調用sun的類來編譯,因為javac其實就是用java實現的。回到Fel里 面,FelCompiler15就是用 com.sun.tools.javac.Main來編譯,而FelCompiler16用標準的javax.tools.JavaCompiler來編譯的。

        文法和語法解釋這塊是使用antlr這個parse generator生成的,這塊不多說,有興趣可以看下antlr,整體一個運行的過程是這樣:

        expression string -> antlr -> AST -> comiple -> java source template -> java class -> Expression 

        這個思路我在實現aviator之前就想過,但是后來考慮到API需要用的sun獨有的類,而且要求classpath必須有tools.jar這個依賴包,就放棄了這個思路,還是采用ASM生成字節碼的方式。題外,velocity的優化可以采用這個思路,我們有這么一個項目是這么做的,也準備開源了。

     

        看看Fel生成的中間代碼,例如a+b這樣的一個簡單的表達式,假設我一開始不知道a和b的類型,編譯是這樣:

        FelEngine fel = new FelEngineImpl();  
        Expression exp 
    =  fel.compile("a+b"null); 

        我稍微改了下FEL的源碼,讓它打印中間生成的java代碼,a+b生成的中間結果為:

        package com.greenpineyu.fel.compile;  
          
        
    import com.greenpineyu.fel.common.NumberUtil;  
        
    import com.greenpineyu.fel.Expression;  
        
    import com.greenpineyu.fel.context.FelContext;  
        
    import org.apache.commons.lang.ObjectUtils;  
        
    import org.apache.commons.lang.StringUtils;  
          
        
    public class Fel_0  implements Expression{  
          
            
    public Object eval(FelContext context) {  
                java.lang.Object var_1 
    = (java.lang.Object)context.get("b");   //b  
                java.lang.Object var_0 = (java.lang.Object)context.get("a");   //a  
                return (ObjectUtils.toString(var_0))+(ObjectUtils.toString(var_1));  
            }  
        } 

         可見,FEL對表達式解析和解釋后,利用template生成這么一個普通的java類,而a和b都從context中獲取并轉化為Object類型,這里沒有做任何判斷就直接認為a和b是要做字符串相加,然后拼接字符串并返回。

     

         問題出來了,因為沒有在編譯的時候傳入context(我們這里是null),FEL會將a和b的類型默認都為java.lang.Object,a+b解釋為字符串拼接。但是運行的時候,我完全可以傳入a和b都為數字,那么結果就非常詭異了:

         FelEngine fel = new FelEngineImpl();  
          
        Expression exp 
    = fel.compile("a+b"null);  
        Map
    <String, Object> env=new HashMap<String, Object>();  
        env.put(
    "a"1);  
        env.put(
    "b"3.14);  
        System.out.println(exp.eval(
    new MapContext(env))); 

    輸出:

        13.14 

        1+3.14的結果,作為字符串拼接就是13.14,而不是我們想要的4.14。如果將表達式換成a*b,就完全運行不了

        com.greenpineyu.fel.exception.CompileException: package com.greenpineyu.fel.compile;  
          
        
    import com.greenpineyu.fel.common.NumberUtil;  
        
    import com.greenpineyu.fel.Expression;  
        
    import com.greenpineyu.fel.context.FelContext;  
        
    import org.apache.commons.lang.ObjectUtils;  
        
    import org.apache.commons.lang.StringUtils;  
          
        
    public class Fel_0  implements Expression{  
          
            
    public Object eval(FelContext context) {  
                java.lang.Object var_1 
    = (java.lang.Object)context.get("b");   //b  
                java.lang.Object var_0 = (java.lang.Object)context.get("a");   //a  
                return (var_0)*(var_1);  
            }  
        }  
          
        [Fel_0.java:
    14: 運算符 * 不能應用于 java.lang.Object,java.lang.Object]  
            at com.greenpineyu.fel.compile.FelCompiler16.compileToClass(FelCompiler16.java:
    113)  
            at com.greenpineyu.fel.compile.FelCompiler16.compile(FelCompiler16.java:
    87)  
            at com.greenpineyu.fel.compile.CompileService.compile(CompileService.java:
    66)  
            at com.greenpineyu.fel.FelEngineImpl.compile(FelEngineImpl.java:
    62)  
            at TEst.main(TEst.java:
    14)  
        Exception in thread 
    "main" java.lang.NullPointerException  
            at TEst.main(TEst.java:
    18

     

        這個問題對于Simple EL同樣存在,如果沒有在編譯的時候能確定變量類型,這無法生成正確的中間代碼,導致運行時出錯,并且有可能造成非常詭異的bug。

     

        這個問題的本質是因為Fel和Simple EL沒有自己的類型系統,他們都是直接使用java的類型的系統,并且必須在編譯的時候確定變量類型,才能生成高效和正確的代碼,我們可以將它們稱為“強類型的EL“。

     

        現在讓我們在編譯的時候給a和b加上類型,看看生成的中間代碼:

        FelEngine fel = new FelEngineImpl();  
        fel.getContext().set(
    "a"1);  
        fel.getContext().set(
    "b"3.14);  
        Expression exp 
    = fel.compile("a+b"null);  
        Map
    <String, Object> env = new HashMap<String, Object>();  
        env.put(
    "a"1);  
        env.put(
    "b"3.14);  
        System.out.println(exp.eval(
    new MapContext(env))); 

        查看中間代碼:

        package com.greenpineyu.fel.compile;  
          
        
    import com.greenpineyu.fel.common.NumberUtil;  
        
    import com.greenpineyu.fel.Expression;  
        
    import com.greenpineyu.fel.context.FelContext;  
        
    import org.apache.commons.lang.ObjectUtils;  
        
    import org.apache.commons.lang.StringUtils;  
          
        
    public class Fel_0  implements Expression{  
          
            
    public Object eval(FelContext context) {  
                
    double var_1 = ((java.lang.Number)context.get("b")).doubleValue();   //b  
                double var_0 = ((java.lang.Number)context.get("a")).doubleValue();   //a  
                return (var_0)+(var_1);  
            }  
        } 

    可以看到這次將a和b都強制轉為double類型了,做數值相加,結果也正確了:

        4.140000000000001 

        Simple EL我沒看過代碼,這里猜測它的實現也應該是類似的,也應該有同樣的問題。

        相比來說,aviator這是一個弱類型的EL在編譯的時候不對變量類型做任何假設,而是在運行時做類型判斷和自動轉化。過去提過,我給aviator的定位是一個介于EL和script之間的東西,它有自己的類型系統。 例如,3這個數字,在java里可能是long,int,short,byte,而aviator統一為AviatorLong這個類型。為了在這兩個類 型之間做適配,就需要做很多的判斷和box,unbox操作。這些判斷和轉化都是運行時進行的,因此aviator沒有辦法做到Fel這樣的高效,但是已 經做到至少跟groovy這樣的弱類型腳本語言一個級別,也超過了JXEL這樣的純解釋EL,具體可以看這個性能測試

     

       強類型還是弱類型,這是一個選擇問題,如果你能在運行前就確定變量的類型,那么使用Fel應該可以達到或者接近于原生java執行的效率,但是失去了靈活性;如果你無法確定變量類型,則只能采用弱類型的EL。

     

       EL涌現的越來越多,這個現象有點類似消息中間件領域,越來越多面向特定領域的輕量級MQ的出現,而不是原來那種大而笨重的通用MQ大行其道,一方面是互 聯網應用的發展,需求不是通用系統能夠滿足的,另一方面我認為也是開發者素質的提高,大家都能造適合自己的輪子。從EL這方面來說,我也認為會有越來越多 特定于領域的,優點和缺點一樣鮮明的EL出現,它們包含設計者自己的目標和口味,選擇很多,就看取舍。


    評論

    # re: 高性能EL——Fel探秘,兼談EL  回復  更多評論   

    2011-09-17 14:38 by GhostZhang
    強人啊!

    # re: 高性能EL——Fel探秘,兼談EL  回復  更多評論   

    2011-09-18 08:39 by tb
    強人

    # re: 高性能EL——Fel探秘,兼談EL  回復  更多評論   

    2011-09-19 11:06 by kino.lucky
    前段時間,也看到Fel的介紹,對這個挺感興趣的,但沒研究過,謝謝你的文章。
    對這個用途我有一點想法,在現在做互聯網項目的時候,不管是前后端通信,還是做api的時候,用到json的地方越來越多,但后端生成json字符串的時候總感覺不夠好,一是用各種java data bean 組裝成一個JSONObject或Map,然后轉化成一個json字符串,但這種方式一點都不直觀,對后面維護代碼和變更都不方便。后來就想到用模板文件,比如用FreeMarker或Velocity做一個Json的映射文件,這樣是直觀了很多,一目了然,修改也很方便,但是感覺FreeMarker和Velocity又不適合,性能不好也太重量級。記得去年就看到你的aviator(當時記得說到命名的由來,映像挺深的),發現性能不錯,當時就想能不能用于json轉換。所以我在想能不能開發一個額外的工具包,專門用于復雜Json對象的高效生成。呵呵,不知有沒有人和我一樣的想法?

    # re: 高性能EL——Fel探秘,兼談EL  回復  更多評論   

    2011-09-19 21:30 by zkgale
    不知道能不能分析一下nutz的el實現呢?
    它主要思路是通過逆波蘭進行轉換,并生成一個執行鏈

    # re: 高性能EL——Fel探秘,兼談EL[未登錄]  回復  更多評論   

    2011-12-28 11:27 by James
    拜讀了大師的aviator源碼,發現詞法分析和語法分析是大師手工寫的嗯,非常強大.由于編譯原理學的不是很好,對于ExpressionParser里面的代碼看的不是很明白,感覺上是根據BNF轉換成的代碼.
    希望大師指點一二,提供一些參考資料.很想自己也寫個類似的東東提高一下.
    主站蜘蛛池模板: 国产成人自产拍免费视频| 亚洲一区二区三区亚瑟| 久久免费公开视频| 春意影院午夜爽爽爽免费| 亚洲综合久久综合激情久久| 亚洲av无码专区在线观看素人| 69堂人成无码免费视频果冻传媒| 免费无码又爽又刺激高潮软件| 国产精品亚洲小说专区| 亚洲综合精品伊人久久| 亚洲免费电影网站| 亚洲天堂2016| 亚洲精品无码中文久久字幕| 亚洲中文无码a∨在线观看| 久久精品国产亚洲AV麻豆网站 | 亚洲国产高清人在线| 亚洲一级二级三级不卡| 在线电影你懂的亚洲| 7777久久亚洲中文字幕蜜桃| 亚洲日本国产精华液| 亚洲国产精品综合久久网各| 日韩国产欧美亚洲v片| 人妻免费一区二区三区最新| 日韩a级无码免费视频| 97视频免费在线| 亚洲日韩国产一区二区三区| 久久亚洲国产中v天仙www| 亚洲婷婷综合色高清在线| 成人a毛片免费视频观看| 8x网站免费入口在线观看| 在线观看免费成人| 亚洲乱色熟女一区二区三区丝袜| 中文字幕亚洲精品资源网| 亚洲AV成人精品日韩一区 | 亚洲高清国产AV拍精品青青草原| 亚洲综合色丁香麻豆| 一级毛片免费观看不收费| 91精品国产免费网站| 亚洲国产精品国产自在在线| 久久精品国产亚洲AV电影| 一级毛片免费在线播放|