luaJIT FFI Library

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.

    时间: 2024-10-12 20:37:21

    luaJIT FFI Library的相关文章

    解决luajit ffi cdata引用cdata的问题

    使用luajit ffi会遇到cdata引用cdata的情况.官方说明是必须手动保存所有cdata的引用,否则会被gc掉. ffi.cdef[[ struct A { int id; }; struct B { int id; struct A* a; }; ]] function createB() local a = ffi.new("struct A") local b = ffi.new("struct B") b.a = a return b end 比如

    初识Luajit

    转自:http://www.cppblog.com/pwq1989/archive/2013/11/28/204487.html 大家可以从官网下载到源码(http://luajit.org/),也可以从Github(https://github.com/LuaDist/luajit)down下来,顺便还可以看下commit记录. 大家对着luajit的wiki结合源码看的话会更好些,因为..文档太特么少了!! 目录结构:-- src    -- host    -- jit    *.c   

    nodejs ffi(DLL)

    npm install ffi Error: The specified module could not be found. npm rebuild var ffi = require('ffi'); var Test = ffi.Library('user32.dll',{ 'MessageBoxA': ['int',['int','string','string','int']] }); Test.MessageBoxA(0,'123','456',0);

    用ffi调用.dylib库

    有一个特性需要调用第三方库libsync,在node.js里可以用ffi来实现 首先稍微包装了一下,ffi也是支持异步调用的,但是API比较麻烦,包装以后调用起来会比较容易 var ffi = require("ffi"); var lib = ffi.Library('./libsync', { 'file_chunk': ['int', ['string', 'string', 'int']], 'file_delta': ['int', ['string', 'string',

    node.js调用ffi

    原帖 例一:调用系统的user32.dll函数 MessageBoxW,有4个参数,输出一个整数 , var FFI = require('ffi'); function TEXT(text){ return new Buffer(text, 'ucs2').toString('binary'); } var user32 = new FFI.Library('user32', { 'MessageBoxW': [ 'int32', [ 'int32', 'string', 'string',

    ngx_lua应用最佳实践

    引子: 以下文字,是UPYUN系统开发工程师timebug在SegmentFault D-Day南京站技术沙龙上所做分享的内容要义提炼,主题为UPYUN系统开发团队在进行业务逻辑由C模块到ngx_lua的迁移过程中产生的心得体会,以及在NGINX上基于ngx_lua的方面的最佳实践方案. Upyun公众号:upaiyun --------------------------------------------------------------------- ngx_lua 是一个NGINX的第

    高级主题

    外部函数接口 在Rust中,通过外部函数接口 (foreign function interface) 可以直接调用C语言库: extern crate libc; use libc::size_t; #[link(name = "snappy")] extern { fn snappy_max_compressed_length(source_length: size_t) -> size_t; } fn main() { let x = unsafe { snappy_max

    帮你提升 Python 的 27 种编程语言

    以下为正文: 作为全球最流行的编程语言联合设计者之一,我经常看到的一种令人沮丧的行为(在Python社区和其它社区都有),就是那些有影响力的人试图把“缺失”的恐惧感灌输给其它开源社区,将其当作对社区贡献的源动力.(我偶尔会对自己的这种不当行为感到内疚,当别人落入同样的陷阱时我也就更容易察觉到). 虽然借鉴其他编程语言社区的经验是一件好事,但以恐惧为基础的方法来激励行动存在严重的问题,因为它将助涨社区成员为争取贡献者的关注而将其它社区的成员视为敌人,而不是当做潜在的盟友去迎接更大的挑战,共同推动顶

    使用spec与fpm 2种方式进行rpm打包

    最近分享zabbix与ansible好多,大家看烦了,我也没啥存货了,现在就介绍一下如何进行rpm打包吧. rpm打包有2个,一个是自己写spec,另外要给是使用ruby的fpm,我个人比较倾向于第二种fpm,这个不需要写spec文件,特简单,不过为了让大家看明白,我分别介绍一下这2款软件是如何进行rpm打包的. 目录 一.使用spec构建rpm 二.使用fpm构建rpm 一.使用spec构建rpm 在构架rpm包的时候,需要经过3个过程 1.解压源码包 2.编译环境configure 3.ma