Пытаюсь перенести обработку OnQuote и OnAllTrade в поток main. C OnAllTrade приходит ссылка на таблицу alltrade:
Код
function OnAllTrade(alltrade)
Сама таблица alltrade находится в стеке вызова этого коллбэка (очевидно, в стеке тоже ссылка, а сама таблица ещё где-нибудь?)
Если я эту ссылку на alltrade помещу в очередь и выйду из OnAllTrade, то не получится ли так, что после выхода из OnAllTrade эта таблица окажется мусором?
Аналогичный вопрос с OnQuote, которая вызывает getQuoteLevel2 и помещает возвращаемую ей таблицу (ссылку на неё) в ту же очередь для обработки в main.
Мне что, перед выходом из этих функций отключать сборку мусора, а после обработки всей очереди в main опять включать??
Смутно помнится, в документации на lua.org я видел, что жизнь таблицы из параметров функции гарантируется до выхода из этой функции (т.е. до выхода из OnAllTrade). Какая-то ерунда пока получается с этим переносом обработки таблиц в поток main...
Serge123 написал: Смутно помнится, в документации на lua.org я видел, что жизнь таблицы из параметров функции гарантируется до выхода из этой функции (т.е. до выхода из OnAllTrade). Какая-то ерунда пока получается с этим переносом обработки таблиц в поток main...
Жизнь локальных параметров прекращается с выходом из функции. А таблица, которую Вы передали через фактические параметры в функцию создана вне этой функции. поэтому она будет утилизирована лишь когда на нее не будет ссылок вообще. Когда вы ее указатель помещаете в очередь, а очередь существует вне функции, то таблица живет своей жизнью и дальше.
nikolz написал: getQuoteLevel2 лучше вызывать в main, чтобы не тормозить основной поток.
У Вас есть какой-нибудь скрипт, подтверждающий это?
Я когда дампил квик через procdump, постоянно видел, что вызовы qlua (типа SetEmptyCallback) из main стояли на входе в критическую секцию, а вот вызовы из главного потока - никогда.
Интересно: как можно проверить, что мой вариант скрипта, в котором я перенёс обработку стаканов и обезл. сделок в main, имеет смысл? Мне кажется, что добавилось лишней работы в потоке main с разбором очереди, а в потоке Квика работа уменьшилась неощутимо...
nikolz написал: getQuoteLevel2 лучше вызывать в main, чтобы не тормозить основной поток.
У Вас есть какой-нибудь скрипт, подтверждающий это?
Я когда дампил квик через procdump, постоянно видел, что вызовы qlua (типа SetEmptyCallback) из main стояли на входе в критическую секцию, а вот вызовы из главного потока - никогда.
Это написано в документации. Могу объяснить на коде почему это так. ----------------- Кроме того, если эта таблица создана глобально, то она будет всегда, если вы явно не присвоите ей nil. ------------------ Проверить можно на СИ. Могу рассказать как.
Serge123 написал: Интересно: как можно проверить, что мой вариант скрипта, в котором я перенёс обработку стаканов и обезл. сделок в main, имеет смысл? Мне кажется, что добавилось лишней работы в потоке main с разбором очереди, а в потоке Квика работа уменьшилась неощутимо...
Вы правильно мыслите. Если хотите получить эффект, то надо убирать sleep и использовать event.
nikolz написал: getQuoteLevel2 лучше вызывать в main, чтобы не тормозить основной поток.
У Вас есть какой-нибудь скрипт, подтверждающий это?
Я когда дампил квик через procdump, постоянно видел, что вызовы qlua (типа SetEmptyCallback) из main стояли на входе в критическую секцию, а вот вызовы из главного потока - никогда.
Вес функции qlua в основном стеке. Основной стек в главном потоке. стек main - это дополнительный стек. колбеки блокируют доступ main к глобальному стеку. Поэтому вызов функций qlua в колбеках не требует дополнительной синхронизации. Ее уже сделали при вызове колбека
Serge123, Относительно очистки памяти при выходе из функции или удалении таблицы, ----------------- Сборщик мусора работает по определенному алгоритму у которого есть настраиваемые параметры. ------------------- Чтобы увидеть уменьшение занятой памяти надо запустить сборщик принудительно. пример:
Код
local M=10000000
local t={}
count1 = collectgarbage("count") print("объем занятой памяти",count1//1);
for i=1, M do t[i]=i end
count1 = collectgarbage("count") print("объем занятой памяти массивом",count1//1);
t=nil -- удаляем массив
count1 = collectgarbage("count") print("объем занятой памяти после удаления массива ",count1//1);
collectgarbage("collect") count1 = collectgarbage("count") print("объем занятой памяти после сборщика массива ",count1//1);
os.exit()
результат:
Код
>D:/lua53/lua53.exe -e "io.stdout:setvbuf 'no'" "Test.lua"
объем занятой памяти 58.0
объем занятой памяти массивом 262202.0
объем занятой памяти после удаления массива 262199.0
объем занятой памяти после сборщика массива 50.0
>Exit code: 0
nikolz, ну вот да, поэтому у меня и вопрос, что больше тормозит главный поток квика -- вызов qlua функций из колбэков или из main с синхронизацией. Если число таких вызовов одинаково (что бывает, если main обрабатывает данные быстрее, чем они поступают), должно получиться, что тормозить больше будут вызовы из main, потому что число синхронизаций вырастет. Если же main не успевает за колбэками, и можно не все данные колбэков обрабатывать, то вызов qlua в main может быть быстрее, чем в колбэках, просто потому, что вызовов будет меньше, но накладные расходы на синхронизацию могут всё равно свести преимущество на нет. Поэтому и вопрос был, есть ли у Вас тест на это.
funduk написал: nikolz, ну вот да, поэтому у меня и вопрос, что больше тормозит главный поток квика -- вызов qlua функций из колбэков или из main с синхронизацией. Если число таких вызовов одинаково (что бывает, если main обрабатывает данные быстрее, чем они поступают), должно получиться, что тормозить больше будут вызовы из main, потому что число синхронизаций вырастет. Если же main не успевает за колбэками, и можно не все данные колбэков обрабатывать, то вызов qlua в main может быть быстрее, чем в колбэках, просто потому, что вызовов будет меньше, но накладные расходы на синхронизацию могут всё равно свести преимущество на нет. Поэтому и вопрос был, есть ли у Вас тест на это.
Рассмотрим два варианта В первом колбек читает стаканы и помещаются в очередь их вместе с clas и sec. main читает стаканы из очереди, при этом main еще читает из очереди clas и sec. -------------------- Во втором колбек помещает в очередь clas и sec main читает clas и sec и если надо, то читает стаканы. --------------------- Полагаю, что второй вариант бесспорно лучше. Второй вариант будет еще в разы лучше, если вместо sleep использовать event. -------------------- что не так?
Serge123, Придумал лишь одно объяснение Вашему примеру, где не изменяются ни время ни цена. Это возможно в режиме договорной сделки. Когда через биржу один крупный игрок продает конкретно другому игроку большой объем, например, сделка РЕПО. Но это лишь мое предположение. ------------------
Я провёл тест, который не выявил разницы. Может быть, getQuoteLevel2 не нужна синхронизация? Запустил два почти одинаковых скрипта, рисующих стакан каждый в своём потоке (настроено так, чтобы гуи рисовался в main скрипта, а не в главном потоке квика). Ниже часть кода одного из скриптов с комментами про замены, чтобы получить второй скрипт. Единственная стабильная разница, что вызов в main даёт на 1 секунду больше CPU time, хотя этот скрипт был запущен на 4 секунды позже (запускал в дневной клиринг, как видно на скрине, чтобы не было обновлений стакана на срочке)
Код
local cols, qt
local sent, received, updates = 0, 0, 0
function OnQuote(class_code, sec_code)
-- отслеживаем котировки только по указанному инструменту
if (class_code ~= CLASS) or (sec_code ~= SEC) then
return
end
qt = getQuoteLevel2(class_code, sec_code) -- во втором скрипте убрать эту строку
sent = sent + 1
end
local fmt = string.format
local function updateGrid()
local s = sent
if received < s then
received = s
fillCols(qt) -- во втором скрипте заменить на fillCols(getQuoteLevel2(CLASS, SEC))
updates = updates + 1
grid:BeginUpdate()
colors = cols.colors -- cols заполняется внутри fillCols, colors используется при рисовании стакана внутри grid.OnPrepareCanvas (не показано тут)
grid:SetCells(0,1,s) -- число вызовов OnQuote
grid:SetCells(0,2,updates) -- число отрисовок стакана
grid:SetCells(0,3,fmt('%.1f%%',(1-updates/s)*100)) -- процент потерь обновлений OnQuote
local m,ti,mti = VCL.IsMultiThread()
grid:SetCells(0,4,tostring(m)) -- System.isMultiThread
grid:SetCells(0,5,tostring(ti)) -- ThreadId
grid:SetCells(0,6,tostring(mti)) -- MainThreadId
for c = 0,2 do
for r,str in pairs(cols[c]) do
grid:SetCells(c,r,str)
end
end
grid:EndUpdate()
end
end
is_run = true
function main()
CreateAndShow()
while is_run do
sleep(1)
updateGrid()
app:ProcessMessages()
end
end
Я провёл тест, который не выявил разницы. Может быть, getQuoteLevel2 не нужна синхронизация? Запустил два почти одинаковых скрипта, рисующих стакан каждый в своём потоке (настроено так, чтобы гуи рисовался в main скрипта, а не в главном потоке квика). Ниже часть кода одного из скриптов с комментами про замены, чтобы получить второй скрипт. Единственная стабильная разница, что вызов в main даёт на 1 секунду больше CPU time, хотя этот скрипт был запущен на 4 секунды позже (запускал в дневной клиринг, как видно на скрине, чтобы не было обновлений стакана на срочке)
Код
Мы с Вам о принципиально разных решениях говорим. -------------------------------------- В вашем тесте - один инструмент, стакан которого вы рисуете. В этом случае Вы не заметите разницы, так как вызов функции getQuoteLevel2 существенно меньше рисования стаканов. ============== Моя постановка задачи для теста такая. ---------------------- Торгуется портфель инструментов. Поток main работает по событиям. Все события формируют очередь(может быть несколько очередей) ---------------- 1) В этом случае, если getQuoteLevel2 вызывать в колбеке, то в очередь попадет весь стакан и clas и sec. Т е время работы колбека всегда будет включать запрос стакана. ========= 2) Если в колбеке не запрашивать стакан, то в очередь запишется clas и sec. Т е время работы колбека будет меньше всегда, чем в первом случае. -------------------- В main будет вызываться функция getQuoteLevel2 . Она находится в глобальном стеке и обращение к ней уже синхронизировано в библиотеке QLUA. Но так как к ней никто вне main не обращается, то ее вызов не будет ничего тормозить и ничего блокировать не надо. ------------------ Если рынок очень активен, то в первом варианте Вы получите целую толпу стаканов, которые уже устарели. --------------------- Во втором варианте в очереди будет всего один колбек и в main будет прочитан самый последний стакан. -------------------- У меня робот работает именно так.
nikolz написал: так как вызов функции getQuoteLevel2 существенно меньше рисования стаканов.
А у Вас обработка сравнима по времени с getQuoteLevel2 и другими необходимыми Вам вызовами qlua? Я тестирую на том, чем сам потом пользоваться буду, для меня пока нет разницы. В очереди для стаканов не вижу смысла, кстати, я просто хэш-таблицу заюзаю с ключами по sec и содержимым sent,updates,received,стакан.
nikolz написал: так как вызов функции getQuoteLevel2 существенно меньше рисования стаканов.
А у Вас обработка сравнима по времени с getQuoteLevel2 и другими необходимыми Вам вызовами qlua? Я тестирую на том, чем сам потом пользоваться буду, для меня пока нет разницы. В очереди для стаканов не вижу смысла, кстати, я просто хэш-таблицу заюзаю с ключами по sec и содержимым sent,updates,received,стакан.
Обработка сравнима. Создаю потоки из пула системных потоков и в них запускаю Luajit (из всего, что пробовал это быстрее всех работает). В указанном ранее тесте в отдельные моменты создавалось максимум 11 потоков. ------------------ Я не рисую стаканы. Скальпингом не занимаюсь. Использовал стакан при реализации алгоритма закрытия позиции по стопу. Но в основном использую либо лучшие предложения либо по рынку, так как торгую лишь ликвидными акциями.