lua-users home
lua-l archive

LuaJIT FFI with libev

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Dear All,
i am trying to implement a Copas equivalent using libev (and maybe later also libuv to perform some benchmarks). For a first prototype, i used [lua-ev](https://github.com/brimworks/lua-ev). It works quite well, but does not seem to be maintained anymore. Then, i came acroos [evffi.lua](https://github.com/AlloSphere-Research-Group/alive/blob/master/master/ev.lua) a LuaJIT FFI binding to libev. It is quite similar to lua-ev. Maybe i understood wrong, but it seemed to me that the FFI binding would be more efficient that the C module when used with LuaJIT. So, i ran a simple benchmark [source code](https://gist.github.com/saucisson/deb9048863386905e121#file-evffi-lua-L315). This gist also contains the code for the FFI binding.
 local ev = require "ev" -- or "evffi"
 print (ev.version ()) -- 4.15 in both cases
 collectgarbage "stop" -- only for "evffi"
 local max = 10000000
 local count = 0
 local loop = ev.Loop.default
 ev.Idle.new (function (loop, idle)
 count = count+1
 if count == max then
 idle:stop (loop)
 loop:unloop ()
 end
 end):start (loop)
 local socket = require "socket"
 local start = socket.gettime ()
 loop:loop ()
 local finish = socket.gettime ()
 print (math.floor (max / (finish - start)), "idles / second")
Question 1: why do i get a "segmentation fault" when running luajit with ev FFI on the example without `collectgarbage "stop"`? The code for Idle handler is as below (the same applies to the modified version shown after):
 ev.Idle = {}
 function ev.Idle.new (on_idle_fn)
 assert (on_idle_fn, "on_idle_fn cannot be nil")
 local ev_idle = ev_idle_t ()
 ev_idle.active = 0
 ev_idle.pending = 0
 ev_idle.priority = 0
 ev_idle.cb = on_idle_fn
 return ev_idle
 end
Question 2: when i run the benchmarks; the FFI version is faster (3.212.945 idles/second vs 2.242.525 for lua-ev). But when using sockets, i get the "too many callbacks" error from LuaJIT. So, i had to update the code as below (shown for idle, but useless in the example):
 ev.Idle.callbacks = {}
 ev.Idle.on_idle = ffi.cast ("idle_cb", function (loop, idle, revents)
 local address = tostring (idle):match "(0x%x+)$"
 ev.Idle.callbacks [address] (loop, idle, revents)
 end)
 function ev.Idle.new (on_idle_fn)
 assert (on_idle_fn, "on_idle_fn cannot be nil")
 local ev_idle = ev_idle_t ()
 local address = tostring (ev_idle):match "(0x%x+)$"
 ...
 ev_idle.cb = ev.Idle.on_idle
 ev.Idle.callbacks [address] = on_idle_fn
 return ev_idle
 end
It creates only one callback for each event type (idle, io, ...) and does the dispatch in this callback. And with this, the performance drops to 795.885 idles/second. Awful! And there is still the segmantation fault when garbage collection is enabled.
So, i suspect i am doing something wrong... but what?
Best regards,
Alban Linard

AltStyle によって変換されたページ (->オリジナル) /