Java調用Lua腳本(熱載實現)


 

前言:
  Lua作為解析執行的腳本語言, 往往是易變邏輯編寫的首選語言, 尤其是在游戲領域. C/C++和Lua的結合, 往往了標配. 比如Redis, Nginx其對Lua的支持, 也是杠杠的. 當然Lua也可以作為規則引擎中的規則編寫語言. 本文對Java調用Lua(Luaj)的實現機制, 做下簡單的介紹.

 

Luaj簡介:
  Luaj是Java調用Lua的一種實現方式, 其是構建一個虛擬機解析執行Lua腳本來實現的, 這和Groovy的方式有所不同.
  這是Luaj的官網, http://www.luaj.org/luaj/3.0/README.html.
  它是針對5.2.x的lua版本的解析器, 其Luaj庫的編寫是通過JavaCC來實現的.

 

簡單示例:
  集合Luaj, 可以通過Maven進行如下配置:

        <dependency>
            <groupId>org.luaj</groupId>
            <artifactId>luaj-jse</artifactId>
            <version>3.0.1</version>
        </dependency>

  Luaj的一個簡單的示例程序:

import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.jse.JsePlatform;

public class TestLuaJ {


    public static void main(String[] args) {

        String luaStr = "print 'hello,world!'";
        Globals globals = JsePlatform.standardGlobals();
        LuaValue chunk = globals.load(luaStr);
        chunk.call();

    }

}

  注: Globals繼承LuaValue對象,LuaValue對象用來表示在Lua語言的基本數據類型,比如:Nil,Number,String,Table,userdata,Function等。尤其要注意LuaValue也表示了Lua語言中的函數。所以,對於Lua語言中的函數操作都是通過LuaValue來實現的.
  其輸出的結果:

hello,world!

 

原理初探:
  根據官方的說法, Luaj在包裝執行具體的Lua代碼時, 有三種不同的模式.
  1). 純腳本解析執行(不選用任何Compiler)
  2). To Lua字節碼(LuaC, lua-to-lua-bytecode compiler)
  3). To Java字節碼(LuaJC, lua-to-java-bytecode compiler)
  其中LuaC是默認的選用Compiler.
  依據官方的介紹:
  不使用LuaC的方法是, 則不調用如何行:

org.luaj.vm2.compiler.LuaC.install(globals);

  而使用LuaJC的方法, 則是調用

org.luaj.vm2.jse.luajc.LuaJC.install(globals);

  可惜, 筆者在自己測試過程中, 遇到了異常(org.luaj.vm2.LuaError: No compiler.), 好尷尬:
  

 

性能評估:
  對Lua解析的代碼進行簡單的性能評估:
  其對同樣的邏輯代碼:

            int a = 0;
            for ( int i = 0; i < 10000; i++ ) {
                a = a + i;
            }

  執行10000次, 具體對比耗時值.
  整體的測試代碼如下:

import org.luaj.vm2.Globals;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.jse.JsePlatform;

public class TestLuaJPerf {

    public static void main(String[] args) {

        int iterNum = 10000;

        // *) java 模式運行
        long beg = System.currentTimeMillis();
        for ( int j = 0; j < iterNum; j++ ) {
            int a = 0;
            for ( int i = 0; i < 10000; i++ ) {
                a = a + i;
            }
        }
        long end = System.currentTimeMillis();
        System.out.println(String.format("Java consume: %dms", end - beg));

        // *) Lua腳本解析執行
        String luaStr = "a = 0; for i = 0, 10000, 1 do a = a + i; end";
        Globals globals = JsePlatform.standardGlobals();
        LuaValue chunk = globals.load(luaStr);
        beg = System.currentTimeMillis();
        for ( int i = 0; i < iterNum; i++ ) {
            chunk.call();
        }
        end = System.currentTimeMillis();
        System.out.println(String.format("Lua consume: %dms", end - beg));

    }

}

  測試結果如下:

Java consume: 10ms
Lua consume: 10249ms

  幾乎1000倍的差異, 這個性能對比, 差異有些大, Lua確實慢的不止半點(可能和Luaj的具體實現也有些關系), 因此從這方面來說, Java+Groovy的結合, 比Java+Lua的結合更有優勢.


線程安全:
  Luaj中的Globals對象不是線程安全的, 因此最佳實踐是每個線程一個Globals對象.
  事實上, 可以采用ThreadLocal的方式來存儲該對象.
  因為是對象, 而不是Class, 其和Groovy編譯的Script類, 其實現思路是本質區別的.

 

總結:
  個人對Luaj的認識還是有些膚淺, 沒有深入地去研究, 所以可能這邊的一些結論可能不准確. 同時Luaj對Lua腳本的支持, 到什么程度, 其實也是一個問號. 不管怎么樣, 能對Luaj能有一個初步的認識, 也是好事.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM