文档章节

luaJIT FFI Library

epiclight
 epiclight
发布于 2015/06/10 18:40
字数 1185
阅读 115
收藏 0

LuaJIT

FFI Library

The FFI library allows calling external C functions and using C data structures from pure Lua code.

The FFI library largely obviates the need to write tedious manual Lua/C bindings in C. No need to learn a separate binding language — it parses plain C declarations! These can be cut-n-pasted from C header files or reference manuals. It's up to the task of binding large libraries without the need for dealing with fragile binding generators.

The FFI library is tightly integrated into LuaJIT (it's not available as a separate module). The code generated by the JIT-compiler for accesses to C data structures from Lua code is on par with the code a C compiler would generate. Calls to C functions can be inlined in JIT-compiled code, unlike calls to functions bound via the classic Lua/C API.

This page gives a short introduction to the usage of the FFI library. Please use the FFI sub-topics in the navigation bar to learn more.

Motivating Example: Calling External C Functions

It's really easy to call an external C library function:

①
②


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

So, let's pick that apart:

 Load the FFI library.

 Add a C declaration for the function. The part inside the double-brackets (in green) is just standard C syntax.

 Call the named C function — Yes, it's that simple!

Actually, what goes on behind the scenes is far from simple:  makes use of the standard C library namespace ffi.C. Indexing this namespace with a symbol name ("printf") automatically binds it to the standard C library. The result is a special kind of object which, when called, runs the printf function. The arguments passed to this function are automatically converted from Lua objects to the corresponding C types.

Ok, so maybe the use of printf() wasn't such a spectacular example. You could have done that with io.write() and string.format(), too. But you get the idea ...

So here's something to pop up a message box on Windows:

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

Bing! Again, that was far too easy, no?

Compare this with the effort required to bind that function using the classic Lua/C API: create an extra C file, add a C function that retrieves and checks the argument types passed from Lua and calls the actual C function, add a list of module functions and their names, add aluaopen_* function and register all module functions, compile and link it into a shared library (DLL), move it to the proper path, add Lua code that loads the module aaaand ... finally call the binding function. Phew!

Motivating Example: Using C Data Structures

The FFI library allows you to create and access C data structures. Of course the main use for this is for interfacing with C functions. But they can be used stand-alone, too.

Lua is built upon high-level data types. They are flexible, extensible and dynamic. That's why we all love Lua so much. Alas, this can be inefficient for certain tasks, where you'd really want a low-level data type. E.g. a large array of a fixed structure needs to be implemented with a big table holding lots of tiny tables. This imposes both a substantial memory overhead as well as a performance overhead.

Here's a sketch of a library that operates on color images plus a simple benchmark. First, the plain Lua version:

local floor = math.floor

local function image_ramp_green(n)
  local img = {}
  local f = 255/(n-1)
  for i=1,n do
    img[i] = { red = 0, green = floor((i-1)*f), blue = 0, alpha = 255 }
  end
  return img
end

local function image_to_grey(img, n)
  for i=1,n do
    local y = floor(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

This creates a table with 160.000 pixels, each of which is a table holding four number values in the range of 0-255. First an image with a green ramp is created (1D for simplicity), then the image is converted to greyscale 1000 times. Yes, that's silly, but I was in need of a simple example ...

And here's the FFI version. The modified parts have been marked in bold:

①





②

③
④






③
⑤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, so that wasn't too difficult:

 First, load the FFI library and declare the low-level data type. Here we choose astruct which holds four byte fields, one for each component of a 4x8 bit RGBA pixel.

 Creating the data structure with ffi.new() is straightforward — the '?' is a placeholder for the number of elements of a variable-length array.

 C arrays are zero-based, so the indexes have to run from 0 to n-1. One might want to allocate one more element instead to simplify converting legacy code.

 Since ffi.new() zero-fills the array by default, we only need to set the green and the alpha fields.

 The calls to math.floor() can be omitted here, because floating-point numbers are already truncated towards zero when converting them to an integer. This happens implicitly when the number is stored in the fields of each pixel.

Now let's have a look at the impact of the changes: first, memory consumption for the image is down from 22 Megabytes to 640 Kilobytes (400*400*4 bytes). That's a factor of 35x less! So, yes, tables do have a noticeable overhead. BTW: The original program would consume 40 Megabytes in plain Lua (on x64).

Next, performance: the pure Lua version runs in 9.57 seconds (52.9 seconds with the Lua interpreter) and the FFI version runs in 0.48 seconds on my machine (YMMV). That's a factor of 20x faster (110x faster than the Lua interpreter).

The avid reader may notice that converting the pure Lua version over to use array indexes for the colors ([1] instead of .red, [2] instead of .green etc.) ought to be more compact and faster. This is certainly true (by a factor of ~1.7x). Switching to a struct-of-arrays would help, too.

However the resulting code would be less idiomatic and rather error-prone. And it still doesn't get even close to the performance of the FFI version of the code. Also, high-level data structures cannot be easily passed to other C functions, especially I/O functions, without undue conversion penalties.


本文转载自:http://luajit.org/ext_ffi.html

epiclight
粉丝 3
博文 41
码字总数 79
作品 0
深圳
架构师
私信 提问
nginx lua 找不着ffi模块

2018/04/18 07:18:41 [error] 7304#0: *10 lua entry thread aborted: runtime error: ...ua_extend/lua_lib/lua-resty-string/lib/resty/md5.lua:4: module 'ffi' not found: no field pack......

会当凌绝顶
2018/04/18
0
0
Building openresty1.7.10.1 with luajit on windows

Building openresty1.7.10.1 with luajit on windows using Cygwin, 在windows下用Cygwin下编译带Luajit的openresty 2013-11-27 15:39 815人阅读 评论(0) 收藏 举报 (heeroz原创 )First i......

epiclight
2015/06/03
0
0
[转]Lua和Lua JIT及优化指南

一、什么是lua&luaJit lua(www.lua.org)其实就是为了嵌入其它应用程序而开发的一个脚本语言, luajit(www.luajit.org)是lua的一个Just-In-Time也就是运行时编译器,也可以说是lua的一个高...

赵青青
02/18
0
0
LuaJIT 2.0.3 稳定版发布,Lua 编译器

LuaJIT 2.0.3 发布,此版本是目前最新的稳定版本。更新内容如下: 添加了 PS4 移植 Add support for multilib distro builds. Fix OSX build. Fix MinGW build. Fix Xbox 360 build. Improv...

oschina
2014/03/13
4.5K
6
Openresty中使用LuaJit

今天在使用Openresty的时候,遇到了一个问题: local resty_sha1 = require "resty.sha1" 想使用resy.sha1的时候,出现了error,log如下: 2013/09/25 09:00:40 [error] 19620#0: *3 lua en......

timingbob
2013/09/25
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Oracle:本地表空间管理,字典表空间管理

本地管理表空间 一、概述 1、理解本地管理表空间的由来 2、理解什么是字典管理表空间及工作原理 3、理解本地管理表空间的优势(为什么要使用本地管理表空间) 4、理解本地管理表空间的内部结...

突突突酱
22分钟前
1
0
深度剖析Spring Boot源码,看完薪资敢要30K!

1 实例化SpringApplication SpringApplication.run(BootifulApplication.class, args); public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {ret......

Java架构资源分享
25分钟前
4
0
tornadofx控制台输出“三门问题”,蒙特卡洛算法

import tornadofx.*fun main() { val wins = intProperty() val lose = intProperty() val Msg = stringProperty()// 1000万次 val n = 10000000 (1..n......

oschina4cyy
31分钟前
1
0
你可能不知道的MySQL中的定点数类型

定点数类型 正因为用浮点数表示小数可能会有不精确的情况,在一些情况下我们必须保证小数是精确的,所以设计MySQL的大叔们提出一种称之为定点数的数据类型,它也是存储小数的一种方式: 其中...

爱编程的浪子
34分钟前
2
0
第十讲:Python爬取网页图片并保存到本地,包含次层页面

上一讲我们讲到了从昵图网的首页下载图片到本地,但是我们发现首页上面的大部分链接其实都可以进入到二级页面。 在二级页面里面,我们也可以同样进行图片的下载,通过层层循环我们可以把网址...

刘日辉
40分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部