Просто наблюдение. Пока не могу подтвердить реальным тестом.
В последних версиях терминала, по ощущениям начиная с 12-ой, наличие колбека в скрипте стало ощутимо "тормозить" терминал. Естественно и раннее колбек использовался только для передачи в очередь. Но теперь даже это приводит к периодическим замираниям терминала. Особенно если у скрипта есть и окно со своим колбеком на обновление.
Наблюдаю у разных брокеров. Есть те, у кого это, можно сказать, постоянно. А есть те, у кого это только в периоды повышенной ликвидности.
Сильнее это проявляется если запустить несколько скриптов и даже в спокойное время. При этом на демо Quik Junior, где поток данных очевидно низкий, это малозаметно.
Хотелось бы понять - это у меня что-то вылезло и надо глубоко копать или такое есть и других.
Примечательно, что есть скрипт из одной строки запускающий dll реализующий jRPC сервер из Квика на плюсах. И если подписаться в нём на OnQuote, то происходит то же самое. Казалось бы - библиотека на ++, нет окон - а терминал не даёт переключить закладки, думает.
Пользователь
Сообщений: Регистрация: 27.01.2017
20.07.2026 16:50:17
Ок. Сделал нагрузочный тест Версия терминала 13.02.20. Реальный брокер.
Создаётся простая таблица. По списку (всего 7) инструментов выводятся данные.
Чтобы была какая-то нагрузка, то берем два колбека OnParam и OnQuote. Данные в таблицу выводятся только из main. В колбеке OnParam вызывается getParamEx несколько раз (нагрузка же).
OnQuote только для записи флага. Чтение DOM тоже в main.
Чтобы хоть как-то смоделировать реальный скрипт, созданы библиотеки заглушки с большим число функций. В main вызывается по одной функции из них.
В итоге скрипт занимает примерно 1 мб. Что не так и много.
Запускаю на реальном брокере и терминал "умирает". Не совсем, но уже ощутимо видно как таблица текущих торгов, да и весь интерфейс, перестаёт работать "плавно". В таблице данные выводятся с задержками, приходится "пинать окно" (вспоминаем тему )
Также на этом примере можно увидеть (по крайней мере у меня), что если не подключать большие библиотеки, то уже всё заметно живее. Т.е. банальный размер скрипта влияет.
При этом в более старых версиях терминала не замечалось особого влияния размера скрипта на работу.
Пользователь
Сообщений: Регистрация: 30.01.2015
21.07.2026 05:21:52
можете сделать вывод времени входа и выхода для колбеков и для main c помощью точного счетчик Windows?
Пользователь
Сообщений: Регистрация: 30.01.2015
Роботорговец
21.07.2026 14:54:06
Давно эта тема обсуждалась, ещё в 9-й версии. Оценить оверхэд от колбэков можно на следующих скриптах:
Код
local run = true
function OnStop()
run = nil
end
function main()
local n = getNumberOf("all_trades")
local m = n
message("Num all trades: " .. n)
repeat
sleep(1)
m = n; n = getNumberOf("all_trades")
if run == nil then return end
until n < m
local t = os.clock()
message("Num all trades: " .. m)
repeat
sleep(1)
n = getNumberOf("all_trades")
if run == nil then return end
until n >= m -- ожидание пока прокачаются все сделки
t = os.clock() - t
message(string.format("%.1f s; num: %u; avg: %.3f ms", t, n, t / n * 1000))
end
Код
local run = true
function OnStop()
run = nil
end
function main()
local n = getNumberOf("all_trades")
local m = n
message("Num all trades: " .. n)
repeat
sleep(1)
m = n; n = getNumberOf("all_trades")
if run == nil then return end
until n < m
local t = os.clock()
message("Num all trades: " .. m)
repeat
sleep(1)
n = getNumberOf("all_trades")
if run == nil then return end
until n >= m -- ожидание пока прокачаются все сделки
t = os.clock() - t
message(string.format("with OnAllTrade: %.1f s; num: %u; avg: %.3f ms", t, n, t / n * 1000))
end
function OnAllTrade(alltrade) end
Перезаказываете обезличенные сделки и замерете время прокачки всех сделок в обоих случаях. Важно: в поле «Выбрать классы инструментов» указать все инструменты без фильтров для более точных измерений! У меня результаты такие:
Код
40.2 s; num: 5719275; avg: 0.007 ms
with OnAllTrade: 85.7 s; num: 5731311; avg: 0.015 ms
Скрытый текст
Видно, что в первом случае упор идёт на пропускную способность канала, т.е. гипотетически сделки могли загрузиться быстрее при более широком канале. Во втором случае скрипт не успевает обрабатывать колбэки с той скоростью, с которой поступают сделки. А мы помним, что колбэки работают в том же потоке, что и GUI. Если у вас разницы нет, что с колбэком, что без него, значит узким местом является не процессор, а например, пропускная способность сети (подключение по Wi-Fi) или сервера. И тогда таким образом оценить оверхэд не получится.
Цитата
Nikolay написал: При этом на демо Quik Junior, где поток данных очевидно низкий, это малозаметно.
Поэтому у разработчиков всё отлично и "не воспроизводится".
Надо делать так, как надо. А как не надо - делать не надо.
Пользователь
Сообщений: Регистрация: 30.01.2015
Роботорговец
21.07.2026 15:21:10
Цитата
Старатель написал: Оценить оверхэд от колбэков можно на следующих скриптах:
Как использовать:
Настройка получения данных выполняется через пункт меню Система / Настройки / Основные настройки..., раздел «Программа» / «Получение данных» / «Обезличенные сделки» В поле «Выбрать классы инструментов» выбрать все без фильтров! Это важно для корректных измерений. Если у вас уже выбраны все классы без фильтров этот шаг можно пропустить. Если нет, - то нажать кнопку «Перезаказать данные».
Подождать пока прокачаются все сделки и запустить первый скрипт.
Снова перезаказать обезличенные сделки и дождаться, когда скрипт выведет итоговый результат и остановится.
Повторить 3-й пункт со вторым скриптом.
Надо делать так, как надо. А как не надо - делать не надо.
Пользователь
Сообщений: Регистрация: 27.01.2017
21.07.2026 16:24:42
Цитата
nikolz написал: можете сделать вывод времени входа и выхода для колбеков и для main c помощью точного счетчик Windows?
Если без излишеств, просто через os.clock, то сами колбеки работаю микросекунды. main уже зависит о того, что делает, но там тоже не что-то существенное.
Я пока наблюдаю странное - подключаешь несколько библиотек на 1000 функций - и привет. Собственно это в тесте и сделано, чтобы проверить влияние этого. Я, конечно, понимаю, что Lua - не тот язык в котором обычно пишутся модули на тысячи строк, но как-то я не встречал упоминаний о влиянии этого на производительность. Хотя если вспомнить World of Warcraft, то ему не мешает.
Пришлось разбираться, т.к. в последних версиях терминала стало всё очень медленно если запущены "большие" скрипты, банально вкладки переключаются со скрипом. Пальцем в небо - что-то сделали с GC. У меня функции использую локальные переменные, а функций много. В итоге GC надо работать. Как он работает в Квике не знаю, но надеюсь, что независимо от основного потока.
Впрочем, приведённый нагрузочный тест не делает ничего такого, чтобы сильно потреблять память, а падение производительности есть.
Пользователь
Сообщений: Регистрация: 12.05.2020
21.07.2026 22:41:32
Цитата
Nikolay написал: Я пока наблюдаю странное - подключаешь несколько библиотек на 1000 функций - и привет.
Странно. Я ваш тест проверял в QUIK 13.0.1.2 брокер ВТБ. Никаких особенностей не заметил (с подключением пакетов и без). В вашем тесте мной был выполнен замер Время выполнения всех коллбеков / Общее время выполнения скрипта: ~0,0004. Вы выполняли ваш тест на другом ПК?
Пользователь
Сообщений: Регистрация: 30.01.2015
Роботорговец
22.07.2026 00:03:34
Цитата
TGB написал: Никаких особенностей не заметил (с подключением пакетов и без).
Сколько у вас колбэков в секунду?
Надо делать так, как надо. А как не надо - делать не надо.
Пользователь
Сообщений: Регистрация: 27.01.2017
22.07.2026 09:17:48
Думаю, что всё зависит от времени, потока данных, да и брокера. На ВТБ позже проверю. Я проверяю на одном небольшом брокере, у которого сервера не особо "широкие".
Вот в 9 утра на всплеске ликвидности запустил. Плюс, что важно, запущены и другие скрипты, но можно и этот дублировать и запускать, в реальности у нас же не один скрипт работает.
Вот участок , всего 125 млс, 9 колбеков
Enter main loop Wed Jul 22 09:03:18 2026 Enter OnQuote SPBFUT RIU6 Exit OnQuote 0.0 SPBFUT RIU6 --------------------------------------------------------------------------- Enter OnParam SPBFUT MMU6 Exit OnParam 0.0 SPBFUT MMU6 90318 2089.0 --------------------------------------------------------------------------- Enter OnParam SPBFUT RIU6 Exit OnParam 0.0 SPBFUT RIU6 90318 82730.0 --------------------------------------------------------------------------- Enter OnQuote SPBFUT MMU6 Exit OnQuote 0.0 SPBFUT MMU6 --------------------------------------------------------------------------- Enter OnParam TQBR SBER Exit OnParam 0.0 TQBR SBER 90318 260.58 --------------------------------------------------------------------------- Enter OnParam TQBR GAZP Exit OnParam 0.0 TQBR GAZP 90318 85.22 --------------------------------------------------------------------------- Enter OnQuote TQBR GAZP Exit OnQuote 0.0 TQBR GAZP --------------------------------------------------------------------------- Enter OnParam SPBFUT MMU6 Exit OnParam 0.0 SPBFUT MMU6 90318 2089.0 --------------------------------------------------------------------------- Enter OnParam SPBFUT RIU6 Exit OnParam 0.0010000000002037 SPBFUT RIU6 90318 82740.0 --------------------------------------------------------------------------- Exit main loop 0.125
А вот участок уже 1.36 секунды, но уже 222 колбека.
Enter main loop Wed Jul 22 09:03:19 2026
Enter OnParam SPBFUT MMU6 Exit OnParam 0.0 SPBFUT MMU6 90319 2089.0 --------------------------------------------------------------------------- Enter OnParam SPBFUT USDRUBF Exit OnParam 0.0 SPBFUT USDRUBF 90319 78.6 --------------------------------------------------------------------------- Enter OnParam SPBFUT MMU6 Exit OnParam 0.0 SPBFUT MMU6 90319 2089.0 --------------------------------------------------------------------------- Enter OnParam SPBFUT SiU6 Exit OnParam 0.00099999999974898 SPBFUT SiU6 90319 79637.0 --------------------------------------------------------------------------- Enter OnParam SPBFUT RIU6 Exit OnParam 0.0 SPBFUT RIU6 90319 82730.0 --------------------------------------------------------------------------- Enter OnQuote SPBFUT SiU6 Exit OnQuote 0.0 SPBFUT SiU6 --------------------------------------------------------------------------- Enter OnQuote SPBFUT USDRUBF Exit OnQuote 0.0 SPBFUT USDRUBF --------------------------------------------------------------------------- Enter OnParam SPBFUT MMU6 Exit OnParam 0.0 SPBFUT MMU6 90319 2089.0 --------------------------------------------------------------------------- Enter OnParam SPBFUT RIU6 Exit OnParam 0.0 SPBFUT RIU6 90319 82730.0 --------------------------------------------------------------------------- ............................................................................................................... чтобы не делать сообщение очень длинным убрал часть ............................................................................................................... --------------------------------------------------------------------------- Enter OnParam SPBFUT MMU6 Exit OnParam 0.0 SPBFUT MMU6 90320 2088.85 --------------------------------------------------------------------------- Enter OnParam SPBFUT SiU6 Exit OnParam 0.0 SPBFUT SiU6 90320 79634.0 --------------------------------------------------------------------------- Enter OnParam SPBFUT USDRUBF Exit OnParam 0.0 SPBFUT USDRUBF 90320 78.6 --------------------------------------------------------------------------- Enter OnParam SPBFUT MMU6 Exit OnParam 0.0 SPBFUT MMU6 90320 2088.85 --------------------------------------------------------------------------- Exit main loop 1.3679999999999
Пользователь
Сообщений: Регистрация: 12.05.2020
22.07.2026 10:20:59
Цитата
Старатель написал: Сколько у вас колбэков в секунду?
На тесте Nikolay: 1) OnQuote всего (в сек.) = ~68 2) OnParam всего (в сек.) = ~482 3) OnParam обработано отфильтрованных (в сек.) = ~16 В моих скриптах эти колбэки не отрабатываются.
Пользователь
Сообщений: Регистрация: 27.01.2017
22.07.2026 10:21:11
Ок. Запустил на VTB в 10 утра. И это просто финиш. В основном была обработка main за 0-0.5 секунды.
А вот этот кусок за 593.92. Записал даже видео работы, где видно что происходит если запустить скрипт. Редактирование поля в таблице обновляется только после пинка. Цена не соответствует данным в ТТТ. Как только скрипт остановлен, то ТТТ сразу оживает.
Видео
Enter main loop Wed Jul 22 10:01:25 2026 Enter OnParam SPBFUT USDRUBF Exit OnParam 0.0 SPBFUT USDRUBF 100102 78.52 --------------------------------------------------------------------------- Enter OnParam SPBFUT CNYRUBF Exit OnParam 0.0 SPBFUT CNYRUBF 100102 11.587 --------------------------------------------------------------------------- Enter OnParam SPBFUT SiU6 Exit OnParam 0.0 SPBFUT SiU6 100102 79532.0 --------------------------------------------------------------------------- Enter OnParam SPBFUT MMU6 Exit OnParam 0.0 SPBFUT MMU6 100102 2095.95 ---------------------------------------------------------------------------
local path = _G.getScriptPath()
package.path = path.."/?.lua;"
local bl1 = require('big_lib')
local bl2 = require('big_lib2')
local bl3 = require('big_lib3')
local bl4 = require('big_lib4')
local logFile
local function log_tostring(...)
local n = select('#', ...)
if n == 1 then
return tostring(select(1, ...))
end
local t = {}
for i = 1, n do
t[#t + 1] = tostring((select(i, ...)))
end
return table.concat(t, " ")
end
local function log(...)
if logFile==nil then return end
logFile:write(log_tostring(...).."\n");
logFile:flush();
end
local sec_list = {
{'SPBFUT', 'MMU6'},
{'SPBFUT', 'SiU6'},
{'SPBFUT', 'RIU6'},
{'SPBFUT', 'USDRUBF'},
{'SPBFUT', 'CNYRUBF'},
{'TQBR', 'SBER'},
{'TQBR', 'GAZP'}
}
local rev_lis = {}
for i = 1, #sec_list do
rev_lis[sec_list[i][2]] = i
end
local sleep = _G.sleep
local isRun = true
local t_id = nil
-- local SeaGreen = 12713921 -- RGB(193, 255, 193) нежно-зеленый
-- local RosyBrown = 12698111 -- RGB(255, 193, 193) нежно-розовый
-- local GetCell = _G.GetCell
-- local Highlight = _G.Highlight
local SetCell = _G.SetCell
local getParamEx = _G.getParamEx
local getInfoParam = _G.getInfoParam
local getQuoteLevel2 = _G.getQuoteLevel2
local IsWindowClosed = _G.IsWindowClosed
local message = _G.message
local table_sremove = _G.table.sremove
local queue = {}
local get_dom = {}
function _G.OnParam(class_code, sec_code)
local s = os.clock()
if t_id and rev_lis[sec_code] then
log('Enter OnParam', class_code, sec_code)
local last_price = tonumber((getParamEx(class_code, sec_code, 'LAST') or {}).param_value) or nil
local res = {}
res.last_time = tonumber((getParamEx(class_code, sec_code, 'TIME') or {}).param_value) or 0
if res.last_time == 0 then return end
res.last_price = last_price
if not res.last_price then return end
local bid_depth = tonumber((getParamEx(class_code, sec_code, 'BIDDEPTHT') or {}).param_value) or 0
res.best_bid = bid_depth > 0 and tonumber((getParamEx(class_code, sec_code, 'BID') or {}).param_value) or nil
local ask_depth = tonumber((getParamEx(class_code, sec_code, 'OFFERDEPTHT') or {}).param_value) or 0
res.best_ask = ask_depth > 0 and tonumber((getParamEx(class_code, sec_code, 'OFFER') or {}).param_value) or nil
res.last_rec = tonumber((tostring(getInfoParam('LASTRECORDTIME') or ''):gsub(':', ''))) or 0
res.local_time = (tonumber((tostring(getInfoParam('LOCALTIME') or ''):gsub(':', ''))) or 0)
res.time_diff = math.abs(res.local_time - res.last_rec) + 10
queue[#queue+1] = {class_code, sec_code, last_price}
log('Exit OnParam', os.clock() - s, class_code, sec_code, res.last_rec, last_price)
log('---------------------------------------------------------------------------')
end
end
function _G.OnQuote(class_code, sec_code)
local s = os.clock()
if t_id and rev_lis[sec_code] and not get_dom[sec_code] then
log('Enter OnQuote', class_code, sec_code)
get_dom[sec_code] = {class_code, sec_code}
log('Exit OnQuote', os.clock() - s, class_code, sec_code)
log('---------------------------------------------------------------------------')
end
end
local function CreateTable()
t_id = _G.AllocTable()
_G.AddColumn(t_id, 0, "sec", true, _G.QTABLE_STRING_TYPE, 15)
_G.AddColumn(t_id, 1, "price", true, _G.QTABLE_DOUBLE_TYPE, 15)
_G.AddColumn(t_id, 2, "bid", true, _G.QTABLE_DOUBLE_TYPE, 15)
_G.AddColumn(t_id, 3, "ask", true, _G.QTABLE_DOUBLE_TYPE, 15)
_G.AddColumn(t_id, 4, "edit", true, _G.QTABLE_DOUBLE_TYPE, 15)
_G.CreateWindow(t_id)
_G.SetWindowPos(t_id, 90, 120, 470, 300)
for i = 1, #sec_list do
_G.InsertRow(t_id, i)
SetCell(t_id, i, 0, sec_list[i][2])
end
end
local function event_callback(_, msg, par1, par2)
log('Enter TableNotificationCallback')
if (msg == _G.QTABLE_CLOSE) then
isRun = false
end
if msg == _G.QTABLE_CHAR then
SetCell(t_id, par1, 4, tostring(par2), par2)
end
log('Exit TableNotificationCallback')
log('---------------------------------------------------------------------------')
end
local function process_queue()
if #queue == 0 then return end
while #queue > 0 do
local last_price = queue[1][3]
-- local lp = GetCell(t_id, 1, 0).value or last_price
-- if lp < last_price then
-- Highlight(t_id, 1, 0, SeaGreen, 0, 500)
-- elseif lp > last_price then
-- Highlight(t_id, 1, 0, RosyBrown, 0, 500)
-- end
SetCell(t_id, rev_lis[queue[1][2]], 1, tostring(last_price), last_price)
table_sremove(queue, 1)
end
end
local function GetQuotes(class_code, sec_code)
local q = getQuoteLevel2(class_code, sec_code)
if not q then return end
local qbid = q["bid"]
local qask = q["offer"]
if not qbid and not qask then
return
end
local best_bid = tonumber(qbid[#qbid]["price"])
local best_ask = tonumber(qask[1]["price"])
SetCell(t_id, rev_lis[sec_code], 2, tostring(best_bid), best_bid)
SetCell(t_id, rev_lis[sec_code], 3, tostring(best_ask), best_ask)
get_dom[sec_code] = nil
end
function _G.OnInit()
CreateTable()
_G.SetTableNotificationCallback(t_id, event_callback)
end
function _G.main()
for i = 1, #sec_list do
if not _G.Subscribe_Level_II_Quotes(sec_list[i][1], sec_list[i][2]) then
return message('Не удалось подписаться на получение данных стакана по инструменту '..tostring(sec_list[i][1])..'|'..tostring(sec_list[i][2]))
end
end
logFile = io.open(path.."\\load_test.txt", "w")
local lt = os.time()
while isRun do
local s = os.clock()
log('Enter main loop', os.date())
IsWindowClosed(t_id)
local ct = os.time()
local u
-- if ct-lt >= 2 then
-- for i = 1, #sec_list do
-- queue[#queue+1] = {sec_list[i][1], sec_list[i][2], tonumber(getParamEx(sec_list[i][1], sec_list[i][2], 'LAST').param_value)}
-- get_dom[sec_list[i][2]] = {sec_list[i][1], sec_list[i][2]}
-- u = true
-- end
-- lt = ct
-- end
process_queue()
for i = 1, #sec_list do
if get_dom[sec_list[i][2]] then
GetQuotes(sec_list[i][1], sec_list[i][2])
end
end
bl1.func1()
bl2.func100()
bl3.func200()
bl4.func300()
log('Exit main loop', os.clock()-s, u and 'update' or '')
log('---------------------------------------------------------------------------')
sleep(60)
end
end
function _G.OnStop()
isRun = false
if t_id and not IsWindowClosed(t_id) then
_G.DestroyTable(t_id)
end
end
Пользователь
Сообщений: Регистрация: 12.05.2020
22.07.2026 10:45:20
Цитата
Nikolay написал: Запустил на VTB в 10 утра. И это просто финиш.
А если запустить только ваш тест без остальных ваших скритов?
Пользователь
Сообщений: Регистрация: 27.01.2017
22.07.2026 10:46:15
Правда здесь уже, видимо, проявилась нагрузка на запись лога внутри колбеков, т.к. запись в файл внутри колбека - это очень плохая идея. Поэтому лучше заменить логирование на PrintDbgStr.
Код
local function log(...)
PrintDbgStr(log_tostring(...))
end
В первой версии теста лога не было. Но, с другой стороны, это тоже такой тест, явно показывающий как main просто перестает вызываться.
Цитата
TGB написал: В моих скриптах эти колбэки не отрабатываются.
Тоже, особенно OnQuote. Но бывают задачи где это необходимо и я стал замечать проблемы. Пришлось смотреть, что происходит.
Вы так ничего не измерите. Обработка кода внутри колбэка идёт на ура. Оверхэд возникает из-за самого вызова колбэка, даже пустого. Смотрите мой пример .[QUOTE]
Надо делать так, как надо. А как не надо - делать не надо.
Пользователь
Сообщений: Регистрация: 27.01.2017
22.07.2026 11:00:36
Цитата
Старатель написал: Вы так ничего не измерите. Обработка кода внутри колбэка идёт на ура. Оверхэд возникает из-за самого вызова колбэка, даже пустого. Смотрите мой пример
Возможно, но моя задача понять что вызывает проблемы. Пока я натолкнулся на влияние на работу main при странном сочетание наличия таблицы и обработки её реакций, библиотек и колбеков. Сами колбеки, судя по всему, работают быстро.
Пользователь
Сообщений: Регистрация: 27.01.2017
22.07.2026 11:16:43
Цитата
TGB написал: А если запустить только ваш тест без остальных ваших скритов?
Выключил все скрипты, только этот. Лог в PrintDbgStr. Вот участок с странным временем. Для начала, есть OnParam с 244 млс. Общее время 1.193 сек для 55 отфильтрованных колбеков.
if t_id and rev_lis[sec_code] then
log('Enter OnParam', class_code, sec_code)
local last_price = tonumber((getParamEx(class_code, sec_code, 'LAST') or {}).param_value) or nil
local res = {}
res.last_time = tonumber((getParamEx(class_code, sec_code, 'TIME') or {}).param_value) or 0
if res.last_time == 0 then return end
res.last_price = last_price
if not res.last_price then return end
local bid_depth = tonumber((getParamEx(class_code, sec_code, 'BIDDEPTHT') or {}).param_value) or 0
res.best_bid = bid_depth > 0 and tonumber((getParamEx(class_code, sec_code, 'BID') or {}).param_value) or nil
local ask_depth = tonumber((getParamEx(class_code, sec_code, 'OFFERDEPTHT') or {}).param_value) or 0
res.best_ask = ask_depth > 0 and tonumber((getParamEx(class_code, sec_code, 'OFFER') or {}).param_value) or nil
res.last_rec = tonumber((tostring(getInfoParam('LASTRECORDTIME') or ''):gsub(':', ''))) or 0
res.local_time = (tonumber((tostring(getInfoParam('LOCALTIME') or ''):gsub(':', ''))) or 0)
res.time_diff = math.abs(res.local_time - res.last_rec) + 10
queue[#queue+1] = {class_code, sec_code, last_price}
log('Exit OnParam', os.clock() - s, class_code, sec_code, res.last_rec, last_price)
Из выше приведенного, где может быть блокировка или зацикливание, "темный" фрагмент ?:
Код
res.last_rec = tonumber((tostring(getInfoParam('LASTRECORDTIME') or ''):gsub(':', ''))) or 0
res.local_time = (tonumber((tostring(getInfoParam('LOCALTIME') or ''):gsub(':', ''))) or 0)
Формально, почему бы нет, но функция getInfoParam в высокочастотном OnParam подозрительна.
Пользователь
Сообщений: Регистрация: 27.01.2017
22.07.2026 13:00:50
Цитата
TGB написал: функция getInfoParam в высокочастотном OnParam подозрительна
Ничего особенного по ней в описании нет. Впрочем, документация очень шаткая на данный момент.
Проблема OnParam в том, что он очень убог. Этот колбек должен быть реализован в виде:
где chng_params - это таблица с изменившимися ключами и их значениями (и еще лучше добавить время пакета в котором это приехало). А подписка на такой колбек должна быть адресной - указать инструмент и набор полей для отслеживания.
В текущей реализации приходится DDOS-ить API чтобы получить данные. Сделки могут быть раз в час, цена посл. сделки может не изменятся, а будет постоянно менять лучший спрос, предложение (есть такие инструменты где роботы постоянно меняют стакан) - постоянный холостой вызов колбека.
Впрочем, этот тест просто для нагрузки, так что можно и запрашивать данные. Тот же LASTRECORDTIME важен, т.к. иногда приходят опоздавшие данные и их лучше пропускать.
Цитата
TGB написал: А если отключить запись в пользовательскую таблицу?
Тогда это будет скрипт без таблицы, по факту. Хотелось бы решить вопрос при наличии таблицы. Я специально добавил постоянный опрос IsWindowClosed.
Пользователь
Сообщений: Регистрация: 30.01.2015
Роботорговец
22.07.2026 13:15:52
Цитата
Nikolay написал: Тот же LASTRECORDTIME важен, т.к. иногда приходят опоздавшие данные и их лучше пропускать.
В LASTRECORDTIME проставляется время сервера или клиента?
Надо делать так, как надо. А как не надо - делать не надо.
Пользователь
Сообщений: Регистрация: 27.01.2017
22.07.2026 13:38:20
Цитата
Старатель написал: В LASTRECORDTIME проставляется время сервера или клиента?
По моим наблюдениям - сервера.
Пользователь
Сообщений: Регистрация: 12.05.2020
22.07.2026 14:36:22
Цитата
Nikolay написал: Ничего особенного по ней в описании нет.
Все таки предлагается протестировать измененный вариант:
Код
res.last_rec = 0 --- tonumber((tostring(getInfoParam('LASTRECORDTIME') or ''):gsub(':', ''))) or 0
res.local_time = 0 --- (tonumber((tostring(getInfoParam('LOCALTIME') or ''):gsub(':', ''))) or 0)
Пользователь
Сообщений: Регистрация: 12.05.2020
22.07.2026 14:47:19
Цитата
Nikolay написал: Ничего особенного по ней в описании нет.
Может быть протестировать измененный вариант:
Код
res.last_rec = 0 --- tonumber((tostring(getInfoParam('LASTRECORDTIME') or ''):gsub(':', ''))) or 0
res.local_time = 0 --- (tonumber((tostring(getInfoParam('LOCALTIME') or ''):gsub(':', ''))) or 0)
написал: Оценить оверхэд от колбэков можно на следующих скриптах:
Как использовать: Настройка получения данных выполняется через пункт меню Система / Настройки / Основные настройки..., раздел «Программа» / «Получение данных» / «Обезличенные сделки» В поле «Выбрать классы инструментов» выбрать все без фильтров! Это важно для корректных измерений. Если у вас уже выбраны все классы без фильтров этот шаг можно пропустить. Если нет, - то нажать кнопку «Перезаказать данные». Подождать пока прокачаются все сделки и запустить первый скрипт. Снова перезаказать обезличенные сделки и дождаться, когда скрипт выведет итоговый результат и остановится. Повторить 3-й пункт со вторым скриптом.
122.6 s; num: 7088692; avg: 0.017 ms with OnAllTrade: 378.9 s; num: 7125638; avg: 0.053 ms
13.0.1.2
Пользователь
Сообщений: Регистрация: 27.01.2017
22.07.2026 17:36:05
Цитата
TGB написал: Может быть протестировать измененный вариант:
Вот убрал запрос getInfoParam, запустил. Всего 86 колбеков. Все они в 1 мкс. Но main выполнялся 1.37 сек. Как-то не верится, что банальный разбор такой небольшой очереди будет столько, хоть и через очень неэффективную table.sremove. Возможна и она проблема. Позже сделаю тест с другой двойной очередью без table.sremove.
Станислав написал: 122.6 s; num: 7088692; avg: 0.017 ms with OnAllTrade: 378.9 s; num: 7125638; avg: 0.053 ms
Оверхэд на колбэк не менее 0.036 мс, а то и больше. Не знаю, будут ли эти значения такими же для OnParam, OnQuote и др. Но если да, то умножте эту величину на количество всех колбэков в секунду, которые получает рабочий скрипт, и получите время, затрачиваемое только на вызов колбэков в течение 1 секунды. И это без их фактической обработки. Особенно это актуально в активные часы торгов, когда событий больше
Надо делать так, как надо. А как не надо - делать не надо.
Пользователь
Сообщений: Регистрация: 12.05.2020
22.07.2026 19:25:55
Цитата
Nikolay написал: Как-то не верится, что банальный разбор такой небольшой очереди будет столько, хоть и через очень неэффективную table.sremove. Возможна и она проблема. Позже сделаю тест с другой двойной очередью без table.sremove.
Очередь, реализованная таким образом, что в ней невозможны блокировки, если она используется только между двумя потоками (main и потоком коллбеков). Варианты реализации такой очереди на форуме выкладывались не один раз (в том числе Старателем), но так как реализация короткая, то я повторюсь: Код:
Код
---- Функции работы с очередями --
--- При использовании между потоком колбеков и потоком main очереди потокобезопасные. ---
--- Создать очередь
function QueueNew()
return {first = 1, last = 0 }
end
--- Записать в очередь --
function QueuePush(self, v)
if v == nil then error('!!! Ошибка. Параметр v функции QueuePush nil') end
local last = self.last + 1
self[last] = v
self.last = last
return last - self.first + 1 -- количество элементов в очереди ---
end
--- Читать очередь --
function QueuePop(self)
local first = self.first
if first > self.last then return nil end
local v, v1 = self[first], self[first + 1]
self.first = first + 1
self[first] = nil
v1 = type (v1) == 'string' and #v1 or 0
return v, v1 -- v1 -длина следующего элемента (если строка) ---
end
-- Текущий размер очереди ---
function QueueSize(self)
return self.last - self.first + 1
end