Блог им. ANTI_Finsov

Загрузка процессора на 100% от запущенного скрипта lua. Что делать?...

Коллеги, Всем добрый день!

Раньше не приходилось работать с lua.  Но здесь накатал небольшой скрипт в рамках которого происходит запросы текущей цены инструмента и запись её файл и столкнулся с проблемами производительности.
До запуска скрипта  доля нагрузки Quik-a на процессор составляла порядка 2%.
После запуска скрипта нагрузка на процессор увеличилась до 30%.   На рабочем компьютере все работает, но вот на виртуалке, где производительность ниже, всё виснет.
Ребят кто сталкивался с подобным и возможно ли оптимизация данной ситуации?

Скрипт скрипта прилагаю, но не думаю, что в нём проблема:

local stopped=false
local FileNameRead=getScriptPath().."\\poz.txt"
local FileNameWrite=getScriptPath().."\\data.txt"
local FileRead
local ID
local code
FileRead=io.open(FileNameRead,«r»)
local Read
code,ID=FileRead:read(4,"*n")
FileRead:close()
--message(code)
local ID_back=ID
local direct

function OnStop()
stopped=true
return 5000
end


function main()

local TableSI=AllocTable()
AddColumn(TableSI,1,«Дата»,true,QTABLE_DATE_TYPE,10)
AddColumn(TableSI,2,«Время»,true,QTABLE_TIME_TYPE,10)
AddColumn(TableSI,3,«Код»,true,QTABLE_STRING_TYPE,10)
AddColumn(TableSI,4,«Цена»,true,QTABLE_INT_TYPE,10)
AddColumn(TableSI,5,«Позиция»,true,QTABLE_INT_TYPE,10)

while stopped==false do
local ServerTime=tostring(getInfoParam(«SERVERTIME»))
local TradeDate=tostring(getInfoParam(«TRADEDATE»))
local SecCode=«SiH9»
local LastPrice = getParamEx(«SPBFUT», SecCode, «LAST»).param_value
local Pos=getFuturesHolding(«SPBFUT»,«SPBFUT00d8u»,SecCode,0)
if Pos~=nil then
Pos=Pos.totalnet
else
Pos=0
end
FileRead=io.open(FileNameRead,«r»)
local Read
code,ID,Read=FileRead:read(4,"*n","*n")
FileRead:close()
if(Read~=nil) then
if ID_back~=ID then
local Price
if (Read>0) then
Price=tonumber(LastPrice-LastPrice%1)*(-1)*Read
direct=1
elseif (Read<0) then
Price=tonumber(LastPrice-LastPrice%1)*math.abs(Read)
direct=-1
end
local FileWrite=io.open(FileNameWrite,«a»)
if Price~=nil then
FileWrite:write(tostring(Price).." ")
FileWrite:close()
end
sleep(1000)
ID_back=ID
end

end
end
message(«Скрипт остановлен»)
end

 

★9
33 комментария
У вас похоже sleep не там стоит. Поставьте его перед end, завершающем «while stoped»
А если это по задумке, то поставьте там хотя бы sleep( 0 ), это капитально снизит нагрузку на процессор.
Если sleep в цикле функции main не выполняется, то скрипт будет полностью съедать одно ядро процессора.
avatar
Работа с файлом в Мэйн в каждом цикле? Экспэнсив!
avatar
bocha, да в каждом, получается в этом проблема?
avatar
ANTI_Finsov, ну мэйн же просто крутится в безусловном цикле с задержкой. Сильно рачительные люди его стараются не нагружать, а условно инициированные действия совершать по колбэкам, ну или хотя бы по If через изменение Global
Попробуйте поэкспериментировать в этом направлении, должно помочь
avatar
bocha, да скорее всего в этом проблема
avatar
ANTI_Finsov, 
получается в этом проблема?
а чего гадать, закомментируете, да посмотрите
avatar
смирись… LUA машина кушает один поток и все.
avatar
самый тупой и простой метод отладки:

1. последовательно комментите функции и прогоняете скрипт.
2. смотрите, какая функция вызывает загруз проца
3. чешете репу
4. пишите на СЛ вопрос, если расчесали до крови, а толку — ноль
avatar
Сергей Симонов, проблему уже решил.  Слишком загрузил main. Вынес всё в отдельную функцию и проблема решена.
avatar
ANTI_Finsov, но функция запускается в main? или как?
avatar
Igr, да просто вынес её за main.
function main()
   while running do
     my_function()
      sleep(1000)
   end
end
avatar

ANTI_Finsov, а есть разница разве? она ж всё равно запускается а значит и всё что в этой функуции прописано запускается тоже

 

avatar
Igr, по идеи Вы правы. Но мне это помогло. Нагрузка снизилась с 30% до 3.
avatar
ANTI_Finsov, ну у вас же sleep был в этой функции, а теперь вы его вынесли за функцию, в этом была проблема по моему 
avatar

ANTI_Finsov, по моему не в этом ошибка, у вас sleep(1000) был после if, т.е возможно срабатывал только иногда, а когда нет то получается sleep(1000) вообще не работал, а значит main() выполнялся с частотой раз в миллисекунду  

мне так кажется, вот и загружал комп по полной 

avatar
Первое правило скриптов QLua: 
Функция Main() ВСЕГДА должна выглядеть примерно так:

function main()
   while running do
      sleep(Freq)
   end
end

где Freq — задержка в миллисекундах.

В зависимости от конкретных задач работа скрипта строится или на вызове определенных функций колбэками или на бесконечном цикле, определенном вне main(). В ряде случаев работа через колбэки может приводить к очень большой нагрузке на процессор, и сопровождаться значительной потерей производительности терминала.

Если и когда захотите одновременно запускать пару десятков скриптов (торговых роботов) — быстро поймете, что QLua Вам уже не подходит. Да и гибкости часто не хватает.
Если есть возможность — лучше сразу начинайте смотреть в сторону C#.

Да, совсем забыл!!!
quik2dde.ru/index.php

Это на тот случай, если все же хотите продолжить поглубже изучать QLua.

avatar

Prophetic, а где делать все вычисления если не в main? 

в колбеках же всё должно быть по минимуму, чисто брать инфу, значения переменных

avatar
Igr, В идеале — в отдельных функциях, которые вызываются колбэками.
Проблема обычно в том, что тот же колбэк «OnAllTrade», по которому мы определяем изменение цены последней сделки по интересующему нас инструменту, вызывается слишком часто, и при сложной логике мы не успеваем выполнить полный расчет до поступления следующего колбэка.
Когда я писал роботов на QLua, то вынужден был делать main() достаточно насыщенным различными командами, и это приводило к высокой нагрузке на процессор и терминал начинал сильно тормозить (не забываем, что и терминал и все скрипты работают в одном потоке).
Данная проблема частично решалась увеличением задержки в конце каждого цикла, но все равно со временем я «уперся в потолок» максимальной нагрузки, и вынужден был начать поиски способа перехода на C# (с параллельным изучением этого языка). Уже года 3 на QLua роботов не пишу. Теперь только на C#.
avatar
Prophetic, ну я цены из стакана беру, все расчёты сравнения в майн, когда в колбеках делал какие то расчёты вот тогда терминал подвисал не хило, когда перенёс всё в майн стало намного лучше, ну у меня 1 скрипт всего лишь работает, проц на 25% загружает, частота расчётов большая 
avatar
Igr, 
1. В самих колбэках конечно же ничего делать нельзя. Только проверку на то, что это именно тот колбэк который Вы ждете, и в случае положительного результата — вызов нужной функции.
2. В стакане нет информации о цене последней сделки по инструменту. только заявки спроса и предложения. Для ликвидных бумаг это не сильно принципиально, но во многих случаях это только заявки. Сделки — это совсем другая история. Но тут каждый сам решает, что ему нужно.
3. На нескольких скриптах (до 10), при правильной организации структуры, потери терпимы. Я больше десятка роботов держал с визуальными интерфейсами каждый. Но нужно помнить, что критическая ошибка в одном роботе может привести к крэшу всего терминала, со всеми вытекающими последствиями. При любых раскладах, логика работы робота за пределами процесса терминала — это только благо. Но пока лично не столкнетесь с этими проблемами, понять меня будет непросто.
avatar

Prophetic, 

1. но вот вызов нужной функции, ведь эта функция будет ыполнятся именно в колбэке, т.е. если функция выполняется долго то всё зависнит, так ведь? 

3. да всё логично, вполне понятно)  

 

а вообще скрипт написанный в клуа какое то преимущество имеет перед скриптом на си, т.е. луа же вписан в квик, не быстрее ли он действует чем сторонняя прога на си ?  

avatar
Igr, 
1. Досконально не проверял, но общая суть верна. Можете попробовать немного обмануть систему, запрещая выполнять какую-либо обработку данных в колбэке, пока не завершилась работа функции от предыдущего вызова.

Фактически — никаких преимуществ, с точки зрения производительности, нет. Только проблемы. Для HFT квик все равно не годится, а для всех прочих задач грамотный коннектор обеспечивает достаточное быстродействие, чтобы вообще не задумываться о скорости обмена информацией между роботом и терминалом.
Скажем так: QLua хорош на начальном этапе освоения роботостроения (в области биржевой торговли). Написав пару десятков роботов на этом языке, и освоив начальный уровень программирования на C#, можно достаточно быстро и без особых проблем перейти на С#.
Я потратил несколько месяцев на самостоятельное изучение C#, а примерно через полгода, после того как нашел коннектор, написал первого полноценного робота на этой платформе.
avatar

Prophetic, разве HFT не торгуют через квик? 

а через что? 

avatar
Igr, для HFT на первом месте стоит скорость получения данных и вывод собственных заявок на площадку. Ни одна связка квик-брокер-биржа не может обеспечить достаточную скорость (в нынешних реалиях).
Для HFT используют прямое подключение к бирже (например Plaza), вплоть до установки своих серверов в стойку, которая расположена максимально близко к серверам биржи. На сегодняшний день HFT — это удел достаточно серьезных игроков, которые вкладывают очень большие деньги в инфраструктуру. Для простых одиночек эта область стала практически недоступна (раньше, конечно, было не так).
avatar
Prophetic, а прямое подключение идёт без квика вообще? там же какой то должен быть терминал? 
avatar
Igr, Сами позиции вы можете контролировать через терминал, но робот работает совершенно отдельно. И не через QUIK. Во всяком случае не через клиентские терминал QUIK, с которым вы обычно работаете. Прямой коннект по протоколу биржи или через стандартизованный для этого дела протокол FIX
avatar
Igr, Да, без квика вообще. И терминала там никакого нет. Есть программный доступ к определенному набору данных (примерно таких же как в квике). Вы эти данные можете обрабатывать как угодно, а на биржу только отправляете транзакции на выставление или удаление заявок.
Это сильно упрощенное объяснение.
Вот, можете почитать, если интересно: https://www.moex.com/s444

avatar

Prophetic, понятно, спасибо 

но как это вообще выглядит, вот у вас робот написан на си под квик, как вы его подключаете к PLAZAII ?  а если на клуа писан его вообще не получится использовать? 

avatar
Igr, Робот, который написан на C# по квик, подключается к терминалу через специальную программу-коннектор (в моем случае QUIKSharp).
Что касается плазы, то до практической реализации я не доходил (не возникло необходимости), но предполагаю, что подходи будет похожим, т.е. надо будет написать программу-коннектор, которая будет подключаться к серверу и транслировать данные вашим роботам, а роботы, соответственно, будут получать данные от этого коннектора, обрабатывать их и через него же отправлять транзакции на сервер.
Робот, написанный на QLua никак нельзя подключить к плазе (во всяком случае, мне неизвестны такие способы).
avatar
Prophetic, спасибо 
avatar
Поток бесконечно выполняется не задерживаясь ни на милисекунду.
надо давать ему поспать когда нет работы )
avatar
Про устройство QLua и как оно функциклирует в рамках терминала QUIK можно почитать тут
quik2dde.ru/viewtopic.php?id=16
avatar
Вы пишете, что процессор загружен на 100%. В смысле весь процессор (а не одно ядро) загружен на 100%? У вас может одноядерный процессор? это экзотика по нашим временам
avatar

теги блога ANTI_Finsov

....все тэги



UPDONW
Новый дизайн