You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openresty/doc/LuaJIT-2.1/ext_ffi.pod

286 lines
7.8 KiB
C

=pod
LuaJIT
=head1 FFI Library
=over
=item * LuaJIT
=over
=item * Download E<rchevron>
=item * Installation
=item * Running
=back
=item * Extensions
=over
=item * FFI Library
=over
=item * FFI Tutorial
=item * ffi.* API
=item * FFI Semantics
=back
=item * jit.* Library
=item * Lua/C API
=item * Profiler
=back
=item * Status
=over
=item * Changes
=back
=item * FAQ
=item * Performance E<rchevron>
=item * Wiki E<rchevron>
=item * Mailing List E<rchevron>
=back
The FFI library allows B<calling external C functions> and B<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 E<mdash>
B<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.
I<Please use the FFI sub-topics in the navigation bar to learn more.>
=head2 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 E<mdash> Yes, it's that simple!
Actually, what goes on behind the scenes is far from simple: makes use
of the standard C library namespace C<ffi.C>. Indexing this namespace
with a symbol name (C<"printf">) automatically binds it to the standard
C library. The result is a special kind of object which, when called,
runs the C<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 C<printf()> wasn't such a spectacular example.
You could have done that with C<io.write()> and C<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
a C<luaopen_*> 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!
=head2 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 a C<struct> which holds four byte fields, one for each
component of a 4x8 bit RGBA pixel.
Creating the data structure with C<ffi.new()> is straightforward
E<mdash> the C<'?'> 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 C<0> to
C<n-1>. One might want to allocate one more element instead to simplify
converting legacy code.
Since C<ffi.new()> zero-fills the array by default, we only need to set
the green and the alpha fields.
The calls to C<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 (C<[1]> instead of C<.red>, C<[2]>
instead of C<.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.
----
Copyright E<copy> 2005-2017 Mike Pall E<middot> Contact
=cut
#Pod::HTML2Pod conversion notes:
#From file ext_ffi.html
# 10336 bytes of input
#Mon May 14 13:19:16 2018 agentzh
# No a_name switch not specified, so will not try to render <a name='...'>
# No a_href switch not specified, so will not try to render <a href='...'>