lua源碼詳解
1. Lua字元串拼接
之前研究lua中字元串拼接,看了一些文章都說 "table.concat" 高於 ".."。最近項目做優化,發現項目中使用table.concat的效率並不比..高,所以實際測試了一下。
1、一些文章說的"table.concat" 高於 "..",是在特定環境中才有效的,看一下他們使用的測試用例:
local str = "a"
local count = 100000
local start_time = os.clock()
local result = ""
for i=1,count do
result = result .. str
end
print("operatorConcatTime:" .. os.clock() - start_time)
local tbl = {}
for i=1,count do
table.insert( tbl,str)
end
table.concat(tbl)
print("tableConcatTime:" .. os.clock() - start_time)
運行後測試結果如上圖,運行效率明顯table.concat遠高於..。在上述測試用例中,「..」 每次只拼接一個字元串,一共拼了10000次。「..」每次拼接都會產生一個新的字元串,而在lua中每產生一個新的字元串都需要將該字元串存放在全局狀態表(global_State)的 strt 域中,隨著拼接次數增大,就會需要更大的空間存儲新的字元串,當達到一定大小時,舊的字元串就需要GC,伴隨著不斷的開辟新空間和GC,就導致性能降低。 而table.concat 底層拼接字元串的方式也是使用運算符.. ,但是其使用演算法減少了使用運算符..的次數,減少了GC,從而提高效率。主要思路:採用二分思想,用棧存儲字元串,新入棧的字元串與下方的字元串比較長度,大於則使用運算符..拼接成新字元串,並移除棧頂的字元串,不斷向下直至遇到長度更大的字元串或者棧底,這樣保持最大的字元串位於棧底,棧呈現金字塔的形狀,最終在使用運算符..將棧中的字元串拼接成最終的字元串。引用( Lua大量字元串拼接方式效率對比及原因分析_AaronChan的博客-CSDN博客_lua 字元串拼接 )。
2、而在實際項目中一般都是幾個字元串的拼接,拼接頻次高,拼接個數少。針對項目中實際情況,寫了如下測試用例測試:
(1)測試用例1:
function global_SpliceString (...)
local t = { ... }
return table.concat (t)
end
local count = 10 000
local sM1 = 0
local sM2 = 0
local start_time
collectgarbage("collect")
sM1 =collectgarbage("count")
print("sM1:",sM1)
local result = ""
start_time = os.clock()
for i = 1, count do
result =global_SpliceString("SELECT * FROM ", "QuestPlot", "WHERE sn='", i, "'")
end
print("CostTime:",(os.clock() - start_time))
sM2 =collectgarbage("count")
print("sM2:",sM2)
print("Genery Mem ory:" , (sM2 - sM1))
(2)測試用例2:
local count = 10 000
local sM1 = 0
local sM2 = 0
local start_time
collectgarbage("collect")
sM1 = collectgarbage("count")
print("sM1:",sM1)
local result = ""
start_time = os.clock()
for i = 1, count do
result = "SELECT * FROM" .. "QuestPlot" .. " WHERE sn='" .. i .."'"
end
print("CostTime:",(os.clock() - start_time))
sM2 =collectgarbage("count")
print("sM2:",sM2)
print("Genery Memory:",(sM2 - sM1))
(3)測試用例3:
local tb = {[1]= "SELECT * FROM ", [2] = "QuestPlot", [3] = " WHEREsn='", [4] = 100101, [5] = "'"}
collectgarbage("collect")
sM1 =collectgarbage("count")
print("sM1:",sM1)
local result = ""
start_time = os.clock()
for i = 1, count do
tb[4] = i
result = table.concat( tb )
end
print("CostTime:",(os.clock() - start_time))
sM2 =collectgarbage("count")
print("sM2:",sM2)
print("Genery Memory:",(sM2 - sM1))
測試結果如下所示:
(1)global_SpliceString:
(2)..
(3)table.concat:
為了模仿更真實的使用環境,以上三個測試用例中要拼接的字元串都有一個動態變化的字元串。通過結果比對,測試用例2和用例3的耗時和內存相差無幾,而測試用例1的耗時和內存明顯高出很多。通過查看lua源碼知道,在一個語句中用「..」連續拼接幾個字元串並不會產生中間字元串,而是會把所有需要連接的字元串都收集起來一起連接。所以table.concat和「..」的方式相差無幾。再看測試用例1,同樣使用table.concat,為什麼用例1的耗時和內存明顯增多。 是因為在 gobal_SpliceString() 方法中的 local t = { ... }, 每次調用這個方法都需要先構造一個table,然後才能使用table.concat。而table在lua中也屬於GC對象,table的創建需要消耗更多時間和內存。同時調用global_SpliceString也會有函數調用消耗,所以用例1的性能消耗是由構造table導致的。
3、總結:
通過以上分析,我們不能簡單的說table.concat和「..」誰的性能更好,還要根據具體使用場景,具體問題具體分析。一般如果一次拼接大量字元串並且要拼接的字元串基本都是固定的,可以使用table緩存起來,使用table.concat拼接;如果是動態的且比較少的字元串拼接,可以直接在一個語句中使用「..」連續拼接。
2. 如何閱讀luajit的代碼
為什麼要看luajit的源碼
作為目前最快的腳本語言之一,luajit確實是一個傑作,但相比原生lua僅僅幾萬行的代碼而言,luajit卻可以說是巨無霸。更要命的是,luajit之所以快,是因為大量使用了機器碼相關的技術,無論是它的機器碼編譯部分,還是位元組碼執行部分,讀起來都非常麻煩。
網上這方面的資料非常少,即使是lua社區的雲風大大也主要以分析原生lua為主,跟luajit有很多不同。萬一遇到了性能坑,或者其他難以解決的問題,需要找到原因,又不能閱讀源碼的話,就只能依賴網上其他人的結論,否則完全無從下手。
調試luajit
萬事的開頭,從能夠自己調試代碼開始
一個能調試的代碼,閱讀起來會遠遠比眼看要清晰得多。
而如果能利用visual studio進行調試,那麼對讀luajit而言還是非常有幫助的,畢竟藉助visual assist的代碼查找,能夠非常快的幫你找到你想了解的東西
luajit下面提供了一個msvcbuild.bat用於編譯luajit,但如果你需要調試的話,可以進行以下幾個步驟:
1.將luajit解壓,比如解壓到LuaJIT-2.1.0-beta2_msvc目錄
2.如果要得到精確的棧,修改LuaJIT-2.1.0-beta2_msvc\src\msvcbuild.bat,搜索/O2,將/O2改為/Od
3.在win64版本的visual studio命令行,執行一次msvcbuild.bat debug,這時會生成luajit.exe,測試一下exe是否正常。
4.用visual studio建立一個命令行工程,例如工程保存在LuaJIT-2.1.0-beta2_msvc\luajitcmd
5.把LuaJIT-2.1.0-beta2_msvc\src下所有.h和.c代碼加入工程
6.把工程的調試路徑設置為
命令:$(ProjectDir)..\..\src\luajit.exe
工作目錄:$(ProjectDir)..\..\src\
7.此時你可以正常按f5下斷點調試了
至於可以調試什麼呢?
最簡單就是寫一個lua文件,require之,執行裡面的代碼,下斷點觀察luajit的行為。
這里必須說明,luajit的執行過程中有兩大部分是沒有.c對應的:
1.有一部分代碼是通過dasm工具生成的,這部分直接通過匯編生成,沒有.c,所以沒有辦法在visual studio調試(其實也可以,但是只能匯編調試)。這些主要是lua虛擬機的代碼(是的,為了快,作者hand tune匯編的方式來寫lua虛擬機)
2.luajit會通過jit模塊編譯一部分代碼變為高度優化的機器碼,這些也是臨時生成的可執行機器碼,你只能在.c看到他們是如何生成的,但執行階段當然是沒有.c對應的。
除此以外,幾乎所有東西都可以直接vs調試:所有的編譯過程、所有的lua標准庫和api、luatable等常規數據結構、profiler,等等
下一步我們會簡單說一下代碼結構,以及結果luajit一些基本原理來說說怎麼閱讀
尤其是luajit的原理,如果對此毫無了解的情況下,閱讀其源碼是十分困難的,因為luajit從編譯到執行的過程,走了很多步,跨越了多個模塊,涉及了三種不同形式的編碼(bytecode, SSA IR, 機器碼),可見其復雜程度。
3. 如何安裝luasocket源碼
第一種方法:如果你有安裝了 Lua 模塊的安裝和部署工具 LuaRocks,那麼一條指令就能安裝部署好 LuaSocket:
# luarocks install luasocket
第二種方法:如果沒安裝有 LuaRocks,也可以源碼安裝。
先把 LuaRocks 下載下來,當前可用的版本是 luasocket-3.0-rc1(luasocket的源碼有託管在Github.com):
# git clone https://github.com/diegonehab/luasocket.git
把源碼clone下來之後就可以進行本地源碼安裝,直接進入到luasocket目錄進行編譯安裝了
# cd luasocket
# make && make install
4. 如何在java中使用Lua腳本語言
如何在Java中使用Lua腳本語言是本文要介紹的內容,主要是來學習LUA腳本語言在JAVA中如何來使用,Lua就不說了, 現在比較熱門, 語法也很簡單. 為了在Java中調用, 折騰了比較長的時間, 就把一些東西記在下面.來看詳細內容講解。
Lua是支持內嵌在C程序中的, 但是官方不支持Java. 在網上查了下, 有LuaJava開源庫, 拿來試用了一下, 發現這個庫還算比較完善的.
這個LuaJava實際上就是按照Lua官方文檔, 把Lua的C介面通過JNI包裝成Java的庫. 下載, 裡面是一個.dll, 一個.jar. 把.dll放到java.library.path下, 再把.lib放到classpath中, helloworld運行OK.
但是, 測試的時候, 很快發現了第一個問題: 在調用LuaJava中提供的LuaState.pushInteger 方法的時候, 出現了錯誤 : Unsatisfied Link Error. 其他的LuaState.pushNumber方法倒是沒有問題. 用Depends工具看了下, 這個.dll居然沒有導出pushInteger這個函數. 暈....
下載LuaJava的源代碼, 查看了下Luajava.c 和 Luajava.h, 發現果然裡面有點問題, 在.h裡面定義了JNI中對應Java函數的C函數
JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1pushInteger
但是.c中沒有實現這個函數. 無語, 看來大馬虎哪都有啊. 幸虧有源代碼, 照貓畫虎在Luajava.c中加上這個函數的實現,
JNIEXPORT void JNICALL Java_org_keplerproject_luajava_LuaState__1pushInteger (JNIEnv * env, jobject jobj, jobject cptr, jint i) { lua_State * L = getStateFromCPtr( env , cptr ); lua_pushinteger(L, i); }
然後編譯. 編譯也出現了問題了, 官方文檔中說可以用VC++來Build, 但是沒有說官方用的是什麼版本. 我用VC2005就不行. 好在Luajava比較小, 就一個.h 一個 .c , 在VC中新建一個.dll項目, 把文件加進去, 修改一下build參數 (Include 需要加上lua的頭文件, lib中需要加上lua的.lib文件, 另外要選上 Compile as C Code (/TC) ) Build, 通過了.
這時再在Java中調用pushInteger方法就沒有問題了.
在測試中, 發現Luajava提供的文檔中, 對於Lua腳本怎麼調用Java對象/方法很詳細, 但是在Java中怎麼調用Lua函數/取得返回值 就沒有. 參考了http://www.lua.org/manual/5.1/manual.html#lua_CFunction 的Lua C文檔, 實現了傳遞對象到Lua中並取得返回值的代碼:
Test1: 測試傳遞簡單類型, 並取得返回值:
Lua 腳本(test.lua):
function test(a,b) return a+b end
Java代碼:
static { //載入Lua5.1.dll, 因為LuaJava最後還是要調用Lua的東西 System.loadLibrary("lua5.1"); } public static void main(String[] argu) throws LuaException { LuaState L = LuaStateFactory.newLuaState(); L.openLibs(); //讀入Lua腳本 int error = L.LdoFile("test.lua"); if (error != 0) { System.out.println("Read/Parse lua file error. Exit."); return; } //找到函數test L.getField(LuaState.LUA_GLOBALSINDEX, "test"); //參數1壓棧 L.pushInteger(1); //參數2壓棧 L.pushInteger(2); //調用!! 一共兩個參數, 1個返回值 L.call(2, 1); //保存返回值, 到a中 L.setField(LuaState.LUA_GLOBALSINDEX, "a"); //讀入a LuaObject l = L.getLuaObject("a"); //列印結果. System.out.println("Result is " + l.getString()); L.close(); }
測試2: 傳遞Java對象
class Value { public int i; public void inc() { i++; } public int get() { return i; } public String toString() { return "Value is " + i; } }
Lua腳本: (該腳本中調用兩次對象的inc方法, 並調用get方法輸出結果)
function test1(v) v:inc(); v:inc(); print("In lua: " .. v:get()); return v end
Java 代碼: (前面都一樣, 略)
//找到函數test1 L.getField(LuaState.LUA_GLOBALSINDEX, "test1"); //生成新的對象供測試 Value v = new Value(); //對象壓棧 L.pushObjectValue(v); //調用函數test1, 此時1個參數, 1個返回值 L.call(1, 1); //結果放在b中. L.setField(LuaState.LUA_GLOBALSINDEX, "b"); LuaObject l = L.getLuaObject("b"); System.out.println("Result is " + l.getObject());
運行結果:
Result is Value is 2 In lua: 2
和預期的一致.
實現一個怪物的創建,把lua里的設定當作初始狀態傳給monstor,名字為sample monstor,防禦10,攻擊10,生命100
1.先導入lib--luajava-1.1.jar
import org.keplerproject.luajava.LuaState; import org.keplerproject.luajava.LuaStateFactory; public class Load{ LuaState luaState; /** * Constructor * @param fileName File name with Lua . */ Load(final String fileName) { this.luaState = LuaStateFactory.newLuaState(); this.luaState.openLibs(); this.luaState.LdoFile(fileName); } /** * Ends the use of Lua environment. */ void close() { this.luaState.close(); } /** * Call a Lua inside the Lua to insert * data into a Java object passed as parameter * @param Name Name of Lua . * @param obj A Java object. */ void run(String Name, Object obj) { this.luaState.getGlobal(Name); this.luaState.pushJavaObject(obj); this.luaState.call(1,0); } } public class Monster{ /* Info */ protected String race; protected int defense; protected int attack; protected int life; /* */ private Load ; public Monster(String race) { /* Loads Lua for this race.*/ this. = new Load(race+".lua"); /*Call Lua create .*/ .run("create", this); } public void setRace(String race) { this.race = race; } public String getRace() { return race; } public int getDefense() { return this.defense; } public void setDefense(int defense) { this.defense = defense; } public int getLife() { return this.life; } public void setLife(int life) { this.life = life; } public void setAttack(int attack) { this.attack = attack; } public int getAttack() { return this.attack; } } monstor.lua--- create(monster) monster:setRace("Sample Monster") monster:setDefense(10) monster:setAttack(10) monster:setLife(100) end
但總是拋出這個錯誤:
PANIC: unprotected error in call to Lua API (Invalid method call. No such method.)
不知為何,以後用到的時候再research.
已經查出來,原來在Monster類中少了個方法:
public void setRace(String race) { this.race = race; }
怪不得會找不到,
要在一lua文件a.lua里導入其他的lua文件b.lua,用require "b"
如果要從lua中運算後得到返回參數,則需要做一下修改:在lua文件中改成:
create(monster) monster:setRace("Sample Monster") monster:setDefense(10) monster:setAttack(10) monster:setLife(100) return monster end
在Load.java中的run改成如下:
void run(String Name, Object obj) { this.luaState.getGlobal(Name); this.luaState.pushJavaObject(obj); this.luaState.call(1, 1);// 一個參數,0個返回 try { Object object =luaState.getObjectFromUserdata(1); } catch (LuaException e) { e.printStackTrace(); } }
轉載僅供參考,版權屬於原作者。祝你愉快,滿意請採納哦
5. 程序開發中遇到的lua語言概念是什麼呢
Lua 教程
lua
Lua 是一種輕量小巧的腳本語言,用標准C語言編寫並以源代碼形式開放, 其設計目的是為了嵌入應用程序中,從而為應用程序提供靈活的擴展和定製功能。
Lua 是巴西里約熱內盧天主教大學(Pontifical Catholic University of Rio de Janeiro)里的一個研究小組於 1993 年開發的,該小組成員有:Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo。
設計目的
其設計目的是為了嵌入應用程序中,從而為應用程序提供靈活的擴展和定製功能。
Lua 特性
輕量級: 他用標准C語言編寫並以源代碼形式開放,編譯後僅僅一百餘K,可以很方便的嵌入別的程序里。
可擴展: Lua提供了非常易於使用的擴展介面和機制:由宿主語言(通常是C或C++)提供這些功能,Lua可以使用它們,就像是本來就內置的功能一樣。
其它特性:
支持面向過程(procere-oriented)編程和函數式編程(functional programming);
自動內存管理;只提供了一種通用類型的表(table),用它可以實現數組,哈希表,集合,對象;
語言內置模式匹配;閉包(closure);函數也可以看作一個值;提供多線程(協同進程,並非操作系統所支持的線程)支持;
通過閉包和table可以很方便地支持面向對象編程所需要的一些關鍵機制,比如數據抽象,虛函數,繼承和重載等。
Lua 應用場景
游戲開發
獨立應用腳本
Web 應用腳本
擴展和資料庫插件如:MySQL Proxy 和 MySQL WorkBench
安全系統,如入侵檢測系統
第一個 Lua 程序
接下來我們使用 Lua 來輸出"Hello World!"
實例(Lua 5.3)
print("Hello World!")