3Qu
3Qu личный блог
30 марта 2021, 15:04

Победил подвисание Quik при включенной ТС. Это просто праздник какой-то.(с)


При включенной ТС при интенсивных торгах регулярно подвисал Quik. Мало того, серверное время Quik, отставало от московского иногда до 30 с. Естественно, все это приводило к ошибкам ТС.
ТС представляет собой программу Lua, связанную с DLL. DLL, собственно, и является самой ТС, a Lua только реал-тайм получает данные из терминала (таблицу обезличенных сделок, стаканы, котировки, реал-тайм свечи — OHLCV, и передает эти данные в DLL, т.е., является интерфейсом между терминалом и ТС. В Lua на сегодня всего ~250 строк кода.
Деле в том, что все события QLua (обновление стакана, таблицы обезличенных сделок и пр.) происходят в одном потоке, потоке терминала, и наша задача, как можно быстрее считать данные о событии, и освободить функцию получения события (см. мануал QLua). Если мы начнем обрабатывать данные в функции события, то все остальные события просто будут ждать своей очереди, а терминал встанет, и тоже будет ждать своей очереди на этом празднике жизни.
Однако, хотя в Lua и DLL реализовалось всего лишь чтение данных событий и преобразование их к формату C++, и далее обрабатывалось уже другими потоками DLL, тем не менее, при высокой интенсивности торгов терминал начинал заметно подвисать. И, хотя DLL и так уже занималось только чтением данных, при высокой интенсивности торгов даже и это занимало в потоке терминала слишком много времени.
Уже пару месяцев назад с этим геморроем надо было что-то делать, но ничего разумного в голову не приходило.
И, вот, вчера пришло (тугодум, блин).
Данные событий пишем в таблицы Lua, а DLL, уже в своих потоках в цикле, определяет обновились ли таблицы, и читает данные из этих таблиц. Цена вопроса — задержка получения данных не более 5 мс. Помогают нам в этом специфические потоконезависимые функции QLua sinsert и sremove.
Посмотрим что у нас получилось на примере стакана
Было:
-- получение стакана
function OnQuote(class, sec)
	if class == EX_CODE and sec == FUT_S then
		
		QL_s = getQuoteLevel2(EX_CODE, FUT_S)
		dt = getInfoParam ('TRADEDATE') .. " " ..getInfoParam ('SERVERTIME')
		ret =CCS.GlassPrice(FUT_S, dt, QL_s) -- вызов функции ДЛЛ
		--message("Запись в ДБ S " .. tostring(ret))
	elseif class == EX_CODE and sec == FUT_L then
		--message("стакан FUT_L изменился.")
		QL_l = getQuoteLevel2(EX_CODE,FUT_L)
		dt = getInfoParam ('TRADEDATE') .. " " ..getInfoParam ('SERVERTIME')
		ret =CCS.GlassPrice(FUT_L, dt, QL_l) -- вызов функции ДЛЛ
        --message("Запись в ДБ L" .. tostring(ret))
	end
end
Стало:
-- получение стакана
function OnQuote(class, sec)
	if class == EX_CODE and sec == FUT_S and #quote_iv_S == 0 then
	    table.sinsert(quote_iv_S,{sec})
	elseif class == EX_CODE and sec == FUT_L and #quote_iv_L == 0 then
	    table.sinsert(quote_iv_L,{sec})
	end<br />end
Здесь мы даже саму функцию получения данных стакана getQuoteLevel2 вынесли за пределы потока события OnQuote.
И теперь мы с этого имеем работающую ТС в терминале, никаких задержек, никаких подвисаний.
Победил подвисание Quik при включенной ТС. Это просто праздник какой-то.(с)

Однако, процесс доработки системы остановить невозможно, а лучшее враг хорошего. Проблемы еще есть и будут, но это по мере их поступления и решения.
18 Комментариев
  • Алексей Никитин
    30 марта 2021, 15:27
    ничога не поняна, но оченя интересна!!!
  • bascomo
    30 марта 2021, 15:34
    Семён Семёныч! ©

    Так в документации же это описано.
    Тоже наступал на эти грабли.
    Ещё было сказано, что, по возможности, table.insert следует избегать, т.к. он работает гораздо медленнее, чем присвоение через прямое индексирование.

    то есть,
    my_table[10] = {'new'}
    работает реально в разы быстрее, чем
    table.insert(my_table, {'new'})

    Правда, что с sinsert, не знаю, но, логично, что он должен быть ещё тормознутее.

    А в event/callback-функциях всё складывается в массивы, и только, а они уже обрабатываются, например, в main.
  • bstone
    30 марта 2021, 16:08
    Ну надо было просто спросить раньше :)

    А так да, вроде и очевидно, и в доках описано жирными буквами:




  • П М
    30 марта 2021, 16:20
    у меня вообще колбэки не используются, но подвисания случаются
    правда серверное время уже давно не отстаёт от текущего
    везде наставил пауз, где 0 мс, где целых 5 мс, кстати да, тоже 5 мс :)

    кстати, если уж вы всё равно dll используете, так лучше реализовать OnQuote прямо в dll, и зарегать её через lua_pushcclosure, lua_setglobal. Спросите что это даст?
    ну можно будет вообще таблицы lua не использовать, а писать сразу в свои,
    и пользоваться штуками типа SetEvent, WaitForSingleObject.
    • bstone
      30 марта 2021, 16:25
      ПBМ, имхо, это и есть единственно верный вариант.
    • Khan Tengri
      30 марта 2021, 16:44
      ПBМ, я использую WaitForMultipleObjects вместо sleep в main.
      • П М
        30 марта 2021, 17:32
        Khan Tengri, у меня только одно событие, его и жду, а в промежутках когда его ожидать нет смысла, пользуюсь sleep

        кмк всё равно в момент опроса квиковых функций можно квик немножко подвесить, если ему приходит большой поток данных, при переподключениях и резких движениях, грешу на синхронизацию во внутренних таблицах квик
      • П М
        31 марта 2021, 07:49
        3Qu, я про выигрыш и виртуальную машину не говорил. Это вы сами придумали и сами опровергли. Зачем виртуальную машину в колбэках использовать, если они на C? Пишите тикер из С колбека сразу в таблицу на C, которая никак не связана с Lua. Вот и выигрыш, которого якобы нет.
          • П М
            01 апреля 2021, 10:08
            3Qu, 
            >> Как мы знаем, Луа не в курсе действий своего C-API.
            не понимаю это предложение. что-то мы наверно действительно не понимаем друг друга

            еще одна попытка:
            я имел в виду OnQuote сделать на C. там вкладывать в обычную C таблицу (а не в lua) приходящий seccode, и делать SetEvent.
            в другом потоке в dll делать WaitForSingleObject и по сигналу SetEvent уже считывать из таблицы на C seccode, без sremove, но тем не менее с синхронизацией (уже на C), и дальше конечно обращаться к Lua за getQuoteLevel2, ну или читать из CreateDataSource/ ds::o,h,l,c[i]

            то что многие вещи удобнее сделать на Lua, совершенно согласен. но переключение контекстов наверное тоже «денег стоит», хотя надо измерять.
  • Glago
    30 марта 2021, 21:01

    т. е. если было:
    if #X >= 5 then table.remove(X, 1) end;
    Можно переписать так?
    if #X >= 5 then table.sremove(X, [1]) end;



Активные форумы
Что сейчас обсуждают

Старый дизайн
Старый
дизайн