Skip to content

Instantly share code, notes, and snippets.

@rtsisyk
Created July 30, 2013 06:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save rtsisyk/6110709 to your computer and use it in GitHub Desktop.
Save rtsisyk/6110709 to your computer and use it in GitHub Desktop.
Lua/C API + string vs Lua/C API + cdata vs LuaJIT FFI
extern "C" {
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include <lj_obj.h>
#include <lj_ctype.h>
#include <lj_cdata.h>
#include <lj_cconv.h>
#include <lj_state.h>
}
struct request;
static const char *requestlib_name = "box.request";
static int
lbox_process(lua_State *L)
{
if (lua_gettop(L) != 1)
luaL_error(L, "Invalid number of arguments");
if (lua_type(L, 1) == LUA_TCDATA) {
/* const struct request *request = lbox_checkrequest(L, 1); */
GCcdata *cd = cdataV(L->base);
/* check ctypeid here */
const struct request *req = *(const struct request **) cdataptr(cd);
/* Process request received as a cdata using the stack */
(void) req;
lua_pushnumber(L, 42);
return 1;
} else if (lua_type(L, 1) == LUA_TSTRING) {
size_t req_size;
const char *req = lua_tolstring(L, 1, &req_size);
/* Process request received as a string */
(void) req;
(void) req_size;
lua_pushnumber(L, 42);
return 1;
} else {
return luaL_error(L, "Invalid argument 1");
}
}
static int
lbox_pack(struct lua_State *L)
{
luaL_Buffer b;
const char *format = luaL_checkstring(L, 1);
int i = 2;
int nargs = lua_gettop(L);
luaL_buffinit(L, &b);
uint32_t num;
uint32_t size;
while (*format) {
if (i > nargs)
luaL_error(L, "invalid number of arguments");
switch (*format) {
case 'i':
num = lua_tointeger(L, i);
luaL_addlstring(&b, (const char *) &num, sizeof(num));
break;
case 'p':
num = lua_tointeger(L, i);
size = sizeof(uint32_t),
luaL_addlstring(&b, (const char *) &size, sizeof(size));
luaL_addlstring(&b, (const char *) &num, sizeof(num));
break;
default:
luaL_error(L, "invalid format specifier '%c'", *format);
}
i++;
format++;
}
luaL_pushresult(&b);
return 1;
}
extern "C" int
process(const struct request *req)
{
/* Process request received as a cdata using FFI */
(void) req;
return 42;
}
int main()
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);
lua_register(L, "process", lbox_process);
lua_register(L, "pack", lbox_pack);
lua_pop(L, 1);
try {
lua_getglobal(L, "dofile");
lua_pushstring(L, "main.lua");
lua_call(L, 1, 0);
} catch (...) {
const char *error = lua_tostring(L, 1);
printf("catch(...): %s\n", error);
}
lua_close(L);
return 0;
}
print(jit.version)
box = {};
bench = {};
(function(box)
local ffi = require('ffi')
local luajit_v21 = (jit.version >= 'LuaJIT 2.1')
if not luajit_v21 then
print('!!! Using a workaround for LuaJIT v2.0.x')
ffi.cdef([[
void *malloc(size_t size);
void free(void *ptr);
]])
end
ffi.cdef([[
struct request
{
uint32_t type;
uint32_t flags;
uint32_t space_no;
uint32_t index_no;
uint32_t offset;
uint32_t limit;
uint32_t key_count;
const char *keys;
const char *keys_end;
};
int
process(const struct request *req);
]])
function pack_u32(buf, value)
ffi.cast('uint32_t *', buf)[0] = value
return buf + ffi.sizeof('uint32_t')
end
local request_t = ffi.typeof('struct request')
local request_ptr_t = ffi.typeof('struct request *')
function request_select(space, index, offset, limit, ...)
local key_part_count = select('#', ...)
local keys_len = ffi.sizeof('uint32_t') +
key_part_count * 2 * ffi.sizeof('uint32_t');
local chunk_len = ffi.sizeof(request_t) + keys_len
local chunk = nil
if luajit_v21 then
chunk = ffi.new('uint8_t[?]', chunk_len)
else
chunk = ffi.cast('uint8_t *',
ffi.gc(ffi.C.malloc(chunk_len), ffi.C.free))
end
local request = ffi.cast(request_ptr_t, chunk)
request.keys = chunk + ffi.sizeof(request_t)
request.type = 17
request.flags = 0
request.space_no = space
request.index_no = index
request.offset = offset
request.limit = limit
request.key_count = 1
local b = request.keys
b = pack_u32(b, key_part_count); -- key_part_count
for i=1,key_part_count,1 do
b = pack_u32(b, ffi.sizeof('uint32_t'));
b = pack_u32(b, select(i, ...));
end
request.keys_end = b;
assert (b == chunk + chunk_len)
return request
end
function box.select_cdata(space, index, offset, limit, ...)
local request = request_select(space, index, offset, limit, ...)
return process(request)
end
function box.select_ffi(space, index, offset, limit, ...)
local request = request_select(space, index, offset, limit, ...)
return ffi.C.process(request)
end
function box.select(space, index, offset, limit, ...)
local key_part_count = select('#', ...)
return process(pack('iiiiiii' .. string.rep('p', key_part_count),
17,
space,
index,
offset,
limit,
1, -- key count
key_part_count, ...))
end
end)(box);
(function(bench)
ffi = require('ffi')
local ffi = require("ffi")
ffi.cdef[[
typedef long time_t;
typedef struct timeval {
time_t tv_sec;
time_t tv_usec;
} timeval;
int gettimeofday(struct timeval *t, void *tzp);
]]
function bench.now()
local t = ffi.new("timeval")
ffi.C.gettimeofday(t, nil)
return tonumber(t.tv_sec * 1000 + (t.tv_usec / 1000))
end
function bench.selects(count)
for i=1,count,1 do
local t = box.select(0, 0, 0, 100, i)
assert(t ~= nil)
end
end
function bench.selects_cdata(count)
for i=1,count,1 do
local t = box.select_cdata(0, 0, 0, 100, i)
assert(t ~= nil)
end
end
function bench.selects_ffi(count)
for i=1,count,1 do
local t = box.select_ffi(0, 0, 0, 100, i)
assert(t ~= nil)
end
end
function bench.run(fun, count)
local start_time = bench.now();
fun(count);
local end_time = bench.now();
print(math.floor(count * 1000 / (end_time - start_time)), ' req/s');
end
end)(bench);
--
-- FFI:
-- [TRACE 1 main.lua:35 return]
-- [TRACE --- main.lua:69 -- leaving loop in root trace at main.lua:73]
-- [TRACE 2 main.lua:135 loop]
-- 1000000 req/s
--
assert(box.select(0, 0, 0, 100, 1, 2, 3, 4, 5) ~= nil)
assert(box.select_cdata(0, 0, 0, 100, 1, 2, 3, 4, 5) ~= nil)
assert(box.select_ffi(0, 0, 0, 100, 1, 2, 3, 4, 5) ~= nil)
io.write("Count: ")
count = io.read("*number")
print('FFI:')
bench.run(bench.selects_ffi, count)
print('Lua C/API + string:')
bench.run(bench.selects, count)
print('Lua C/API + cdata:')
bench.run(bench.selects_cdata, count)
LuaJIT 2.0.2
Count: 5000000
FFI:
1400952 req/s
Lua C/API + string:
854700 req/s
Lua C/API + cdata:
166295 req/s
LuaJIT 2.1.0-alpha
Count: 5000000
FFI:
17361111 req/s
Lua C/API + string:
2503755 req/s
Lua C/API + cdata:
437828 req/s
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment