Created
July 30, 2013 06:21
-
-
Save rtsisyk/6110709 to your computer and use it in GitHub Desktop.
Lua/C API + string vs Lua/C API + cdata vs LuaJIT FFI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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