1

Тема: Qlua + luajit

1) Берём luajit (тестировал со сборкой Scilua/ulua, там удобный менеджер пакетов).
2) Для связи с quik - любая библиотека RPC (remote procedure call), как минимум достаточно socket + сериализации таблиц в строку/json. Я остановился на tango-copas (http://github.com/lipp/tango)
3) Для проверки типов аргументов функций - annotate (http://siffiejoe.github.io/lua-annotate/)

Схема примерно такая:

Qlua                            luajit
-------------                -----------------
| callback  |---------->| tango server |- - - ->/--------------\
-------------                -----------------          |  SQLite        |
                                         ^                     |                    |   
                                         |                      |  database     |
                                         v                      |                   | 
----------------            ----------------           | logs             |
| main          |<-------| Main           | - - - ->\--------------/
|tango server|            |tango server|
----------------            ----------------



Плюсы:
Так как luajit на порядок быстрее lua, проверка типов получается "бесплатной".
Вообще можно всю логику перенести в luajit, по аналогии со связками qlua + C#/C++
Доступны все библиотеки lua, включая GUI (без танцев с бубнами вокруг dll и mainloop UI/Qlua), SQLite.
Доступна реальная многозадачность.
Можно заменить luajit на что угодно - Python, javascript, Java, C# при сохранении совместимого протокола передачи данных.
Можно заменить Qlua на эмулятор для тестирования и отладки.

Минусы:
Так как main и колбэки работают параллельно, соответствующие им процедуры на стороне luajit нужно запускать в параллельных потоках или даже в отдельных процессах. Для передачи данных между ними нужно использовать либо базу данных, либо те же sockets+json.
Усложняется процедура инициализации и восстановления после сбоев.

2

Re: Qlua + luajit

Сравнение annotate c checks (проверка типов в dll) и прямой проверкой типов в коде.

-- compare checks and annotate.check
local getmetatable=getmetatable

require 'checks'
     
-- Custom checker function --
function checkers.port(p)
  return type(p)=='number' and p>0 and p<0x10000
end

-- A new named type --
socket_mt = { __type='socket', _type='socket' }
asocket = setmetatable ({ }, socket_mt)

-- Annotate
local annotate = require( "annotate" )
local check = require( "annotate.check" )
check.types.port = function( p )
  return type(p)=='number' and p>0 and p<0x10000
end

check.types.socket = function( sock )
  return (getmetatable (sock) or {})._type or type (sock)
end

-- original function
local test_func = function (n, s, t, sock, port, str)
  sock.port=port 
  if str and not sock[str] then 
     sock[str]= getmetatable(sock)
  end
  sock.n = (sock.n or 1) + 1
  return s..n
end

local function test_checks ( n, s, t, sock, port, str)
  checks ('number', 'string', 'table', 'socket', 'port', '?string')
  return test_func
end

local test_annotate = annotate[=[
test_annotate( n, s, t, sock, port, str ) => string
    n: number
    s: string
    t: table
    sock: socket
    port: port
    str:  nil/string
]=] .. test_func

-- my checks
local io_type=io.type
local function _type (x)
  return (getmetatable (x) or {})._type or io_type (x) or type (x)
end

-- assert - my best
local function test_mycheck(n, s, t, sock, port, str)
  if     type(n)~="number"  then error("wrong param #1:"..tostring(n))
  elseif type(s)~="string"   then error("wrong param #2:"..tostring(s))
  elseif type(t)~="table"   then error("wrong param #3:"..tostring(t))
  elseif not ( type( sock)=="table" and (getmetatable(sock) or {})._type=="socket" ) 
    then error("wrong param #4:"..tostring(sock))
  elseif not ( type( port)=="number" and port>0 and port<0x10000) -- "type" port
    then error("wrong param #5:"..tostring(port))
  elseif type(str)~="string"   then error("wrong param #6:"..tostring(str))
  end
  return test_func
end

function run_test(fname, fn, sock, port, str)
  local x=''
  local t={1,2,3}
  local n=1000000
  local start_time=os.clock()

  for i=1,n do
    fn( i, 'dhdh', t, sock, i%10000+1, str)
  end
  print(fname, "time:", tonumber(os.clock() - start_time))
end

print("------------------------------------")
run_test("pure test_func", test_func, asocket, 1024, "hello")
run_test("test_checks", test_checks, asocket, 1024, "hello")
run_test("test_annotate", test_annotate, asocket, 1024, "hello")
run_test("test_mycheck", test_mycheck, asocket, 1024, "hello")

Результат в luajit:
------------------------------------
pure test_func    time:    0.008
test_checks    time:    0.853
test_annotate    time:    0.009
test_mycheck    time:    0.007

Накладные расходы 1 ms на 1 млн вызовов в pure lua - отличный результат.
Проверка типов через checks.dll на 2 порядка медленнее.

3

Re: Qlua + luajit

Тест tango.

Сервер tango_srv.lua:

require'tango.server.copas_socket'
local random = math.random

function Frand(i)
    return random(-i,i)
end

print("start server localhost:12345")
tango.server.copas_socket.loop{
  port = 12345,
  host = "*"
}

Запуск:

start luajit tango_srv.lua >tango_srv.log

test_tango_rpc.lua:

require'tango.client.socket'
local random = math.random
local res, time, N = 0, 0, 1000
-- 1 вычисления в цикле
-- 2 то же в функции
-- 3 то же в функции RPC

function runtest0( times)
    local begin = os.clock()
    local x=0
    for i = 1, times do
        x = x + random(-i,i)
    end
    local endtm = os.clock()
    return x, endtm - begin
end

function runtest(func, times)
    local begin = os.clock()
    local x=0
    for i = 1, times do
        x = x + func(i)
    end
    local endtm = os.clock()
    return x, endtm - begin
end

function Frand(i)
    return random(-i,i)
end

local proxy = tango.client.socket.connect{
   address = 'localhost',
   port = 12345
}

res,time = runtest0(N)
print("test loop", time)

res,time = runtest(Frand, N)
print("test Frand", time)

res,time = runtest(proxy.Frand,N)
print("test proxy.Frand", time)

Запуск:

luajit -joff test_tango_rpc.lua

результат:

test loop    0
test Frand    0
test proxy.Frand    0.238

0.24 ms на вызов функции через sockets.

4

Re: Qlua + luajit

Нашел подобный проект https://github.com/Enfernuz/quik-lua-rpc
Там используется ZeroMQ и Protocol Buffers.
Странно, что не видел его раньше, хотя целенаправленно искал полгода назад lua rpc библиотеки.

И сегодня вышел QUIKSharp ver 1.0, цель проекта: «повторить API QLUA в C# максимально точно и качественно».
https://smart-lab.ru/blog/538825.php
Реализация аналогичная - передача сообщений через сокеты и json.

На мой личный вкус, luajit сравним по скорости с C#но проще и компактнее по размеру кода и рантайма.
Ещё интересная связка Moonscript + qlua/luajit.

5

Re: Qlua + luajit

Что касается luajit - то проблема в многопоточности и наличии main().
Как это решили в QUIKSharp - я не смотрел.

Если я всё верно понял, то программы, использующие QUIKSharp, запускаются отдельным процессом, внешним по отношению к QUIK. Что как раз позволяет свободно использовать любой функционал, в том числе создание пользовательского интерфейса.
Но могу ошибаться, хорошо бы уточнить.

Конечно в идеале было бы правильно просто встроить luajit в QUIK для ускорения работы роботов, это было бы логично.
Ну а дальше уже начинается вкусовщина. Я лично не очень понимаю зачем писать на разных питонах/C# и далее по списку, когда предлагается Lua. Роботы-то от этого не начинают лучше работать )
Другое дело, если эти внешние средства предоставляют новое возможности. Но вот какие - я не очень понимаю.