小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Lua FFI 實(shí)戰(zhàn)

 quasiceo 2014-01-03

Lua FFI 實(shí)戰(zhàn) May 19, 2013

由來

FFI庫,是LuaJIT中最重要的一個擴(kuò)展庫。它允許從純Lua代碼調(diào)用外部C函數(shù),使用C數(shù)據(jù)結(jié)構(gòu)。有了它,就不用再像Lua標(biāo)準(zhǔn)math庫一樣,編寫Lua擴(kuò)展庫。把開發(fā)者從開發(fā)Lua擴(kuò)展C庫(語言/功能綁定庫)的繁重工作中釋放出來。

FFI簡介

FFI庫,允許從純Lua代碼調(diào)用外部C函數(shù),使用C數(shù)據(jù)結(jié)構(gòu)。

FFI庫最大限度的省去了使用C手工編寫繁重的Lua/C綁定的需要。不需要學(xué)習(xí)一門獨(dú)立/額外的綁定語言——它解析普通C聲明。這樣可以從C頭文件或參考手冊中,直接剪切,粘貼。它的任務(wù)就是綁定很大的庫,但不需要搗鼓脆弱的綁定生成器。

FFI緊緊的整合進(jìn)了LuaJIT(幾乎不可能作為一個獨(dú)立的模塊)。JIT編譯器為Lua代碼直接訪問C數(shù)據(jù)結(jié)構(gòu)而產(chǎn)生的代碼,等同于一個C編譯器應(yīng)該生產(chǎn)的代碼。在JIT編譯過的代碼中,調(diào)用C函數(shù),可以被內(nèi)連處理,不同于基于Lua/C API函數(shù)調(diào)用。

這一頁將簡要介紹FFI庫的使用方法。

激勵范例:調(diào)用外部C函數(shù)

真的很用容易去調(diào)用一個外部C庫函數(shù):

① local ffi = require("ffi") 
② ffi.cdef[[
  int printf(const char* fmt, ...);
    ]]
③ ffi.C.printf("Hello %s!", "world")

以上操作步驟,如下:

① 加載FFI庫
② 為函數(shù)增加一個函數(shù)聲明。這個包含在`中括號`對之間的部分,是標(biāo)準(zhǔn)C語法。.
③ 調(diào)用命名的C函數(shù)——非常簡單

事實(shí)上,背后的實(shí)現(xiàn)遠(yuǎn)非如此簡單:③ 使用標(biāo)準(zhǔn)C庫的命名空間ffi.C。通過符號名("printf")索引這個命名空間,自動綁定標(biāo)準(zhǔn)C庫。索引結(jié)果是一個特殊類型的對象,當(dāng)被調(diào)用時,執(zhí)行printf函數(shù)。傳遞給這個函數(shù)的參數(shù),從Lua對象自動轉(zhuǎn)換為相應(yīng)的C類型。

Ok,使用printf()不是一個壯觀的示例。你也可能使用了io.write()string.format()。但你有這個想法…… 以下是一個Windows平臺彈出消息框的示例:

  1. local ffi = require("ffi")
  2. ffi.cdef[[
  3. int MessageBoxA(void *w, const char *txt, const char *cap, int type);
  4. ]]
  5. ffi.C.MessageBoxA(nil, "Hello world!", "Test", 0)

Bing! 再一次, 遠(yuǎn)非如此簡單,不?

和要求使用Lua/C API去綁定函數(shù)的努力相比:

  • 創(chuàng)建一個外部C文件,
  • 增加一個C函數(shù),遍歷和檢查Lua傳遞的參數(shù),并調(diào)用這個真實(shí)的函數(shù),

傳統(tǒng)的處理方式

  • 增加一個模塊函數(shù)列表和對應(yīng)的名字,
  • 增加一個luaopen_*函數(shù),并注冊所有模塊函數(shù),
  • 編譯并鏈接為一個動態(tài)庫(DLL),
  • 并將庫文件遷移到正確的路徑,
  • 編寫Lua代碼,加載模塊
  • 等等……
  • 最后調(diào)用綁定函數(shù)。

?。。ê懿凰剑。?/p>

激勵示例: 使用C數(shù)據(jù)結(jié)構(gòu)

FFI庫允許你創(chuàng)建,并訪問C數(shù)據(jù)結(jié)構(gòu)。當(dāng)然,其主要應(yīng)用是C函數(shù)接口。但,也可以獨(dú)立使用。

Lua構(gòu)建在高級數(shù)據(jù)類型之上。它們很靈活、可擴(kuò)展,而且是動態(tài)的。這就是我們大家都喜歡Lua的原因所在。唉,針對特殊任務(wù),你需要一個低級的數(shù)據(jù)結(jié)構(gòu)時,這可能會低效。例如,一個超大的不同結(jié)構(gòu)的數(shù)組,需要通過一張超大的表,存儲非常多的小表來實(shí)現(xiàn)。這需要大量的內(nèi)存開銷以及性能開銷。

這里是一個庫的草圖,操作一個彩圖,以及一個基準(zhǔn)。首先,樸素的Lua版本,如下:

  1. local floor = math.floor
  2.  
  3. local function image_ramp_green(n)
  4. local img = {}
  5. local f = 255/(n-1)
  6. for i=1,n do
  7. img[i] = { red = 0, green = floor((i-1)*f), blue = 0, alpha = 255 }
  8. end
  9. return img
  10. end
  11.  
  12. local function image_to_grey(img, n)
  13. for i=1,n do
  14. local y = floor(0.3*img[i].red + 0.59*img[i].green + 0.11*img[i].blue)
  15. img[i].red = y; img[i].green = y; img[i].blue = y
  16. end
  17. end
  18.  
  19. local N = 400*400
  20. local img = image_ramp_green(N)
  21. for i=1,1000 do
  22. image_to_grey(img, N)
  23. end

以上代碼,創(chuàng)建一個160.000像素的一張表,其中每個元素是一張持有4個范圍0至255的數(shù)字值的表。首先,創(chuàng)建了一張綠色斜坡的圖(1D,為了簡單化),然后進(jìn)行1000次灰階轉(zhuǎn)換操作。實(shí)在很蠢蛋,可是我需要一個簡單示例……

以下是FFI版本代碼。其中,被修改的部分加粗標(biāo)注:

① local ffi = require("ffi")
ffi.cdef[[
typedef struct { uint8_t red, green, blue, alpha; } rgba_pixel;
]]

② local function image_ramp_green(n)
  local img = ffi.new("rgba_pixel[?]", n)
  local f = 255/(n-1)
③  for i=0,n-1 do
④  img[i].green = i*f
    img[i].alpha = 255
  end
  return img
end

local function image_to_grey(img, n)
③ for i=0,n-1 do
⑤   local y = 0.3*img[i].red + 0.59*img[i].green + 0.11*img[i].blue
    img[i].red = y; img[i].green = y; img[i].blue = y
  end
end

local N = 400*400
local img = image_ramp_green(N)
for i=1,1000 do
  image_to_grey(img, N)
end

Ok, 這是不是太困難:

① 首先,加載FFI庫,聲明底層數(shù)據(jù)類型。這里我們選擇一個數(shù)據(jù)結(jié)構(gòu),持有4字節(jié)字段,每一個由4x8 RGBA像素組成。

② 通過ffi.new()直接創(chuàng)建這個數(shù)據(jù)結(jié)構(gòu)——其中'?'是一個占位符,變長數(shù)組元素個數(shù)。

③ C數(shù)據(jù)是基于0的(zero-based),所以索引必須是0 到 n-1。你可能需要分配更多的元素,而不僅簡化轉(zhuǎn)換一流代碼。

④ 由于ffi.new()默認(rèn)0填充(zero-fills)數(shù)組, 我們僅需要設(shè)置綠色和alpha字段。

⑤ 調(diào)用math.floor()的過程可以省略,因?yàn)檗D(zhuǎn)換為整數(shù)時,浮點(diǎn)數(shù)已經(jīng)被向0截斷。這個過程隱式的發(fā)生在數(shù)據(jù)被存儲在每一個像素的字段時。

現(xiàn)在讓我們看一下主要影響的變更:

首先,內(nèi)存消耗從22M降到640K(4004004字節(jié))。少了35x。所以,表確實(shí)有一個顯著的開銷。BTW(By the Way: 順便說一句): 原始Lua程序在x64平臺應(yīng)該消耗40M內(nèi)存。

其次,性能:純Lua版本運(yùn)行耗時9.57秒(使用Lua解析器52.9秒),而FFI版本在我的主機(jī)上耗時0.48秒(YMMV: 因人而異)。快了20x(比Lua解析器快了110x`)。

狂熱的讀者,可能注意到了為顏色將純Lua代碼版本轉(zhuǎn)為使用數(shù)組索引([1] 替換 .red, [2] 替換 .green 等)應(yīng)該更加緊湊和更快。這個千真萬確(大約1.7x)。從結(jié)構(gòu)切換到數(shù)組也會有幫助。

雖然最終的代碼不是慣用的,而容易出錯。它仍然沒有得到甚至接近FFI版本代碼的性能。同時,高級數(shù)據(jù)結(jié)構(gòu)不容易傳遞給別的C函數(shù),尤其是I/O函數(shù),沒有過分轉(zhuǎn)換處罰。

待續(xù)

擴(kuò)展閱讀

安裝LuaJIT

mkdir -p ~/lua-ffi_in_action && cd ~/lua-ffi_in_action
git clone http:///git/luajit-2.0.git
cd luajit-2.0
make && make install

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多