Ускоряем скрипт и выкидываем sleep

Страницы: 1
RSS
Ускоряем скрипт и выкидываем sleep
 
Добрый день, Всем
С момента появления VMLua в КВИКЕ неоднократно рассказывал о том, что применение event вместо sleep
не только экономит ресурсы процессора, но и позволяет не пропускать события в колбеках и максимально бысто на них реагировать.
говорил об этом например здесь:
https://forum.quik.ru/forum17/topic8426/
---------------------
К сожалению, кроме бессмысленного хамства некоторых посетителей форума, ничего конкретного никто не написал.
---------------
Но вот наконец-то появился вменяемый чел paluke .
и после моей попытки в очередной раз объяснить преимущество event
https://forum.quik.ru/messages/forum10/message75435/topic8600/#message75435






он все же решил проверить это и убедился, что это так действительно:  

Код
Просто проверка концепции:
Кодw32 = require("w32")

run = true
evt = false

function OnInit()
  evt = w32.CreateEvent(nil, 0, 0, nil)
end

function OnStop()
 run = false
 w32.SetEvent(evt)
end

function main()
  while run do
     w32.WaitForSingleObject(evt, 1000000)
  end
  w32.CloseHandle(evt)
end

В колбеках вызываете SetEvent - main сразу просыпается.
С чем Всех и поздравляю.
 
Присоединяюсь.
Не нужно иметь много образования чтобы понять что техника с ожиданием событий эффективна.
А реализация не так сложна, как кажется на первый взгляд.
 
Эту возможность надо в квик добавить.
 
Для моих целей сообщения тоже оказались заметно быстрее, чем sleep. На скрине для каждой пары неродных для квика стаканов левый отрисован с ожиданием через сообщения, правый - через sleep. Внизу стаканов статистика потерь, они на правых стаканах больше. Kernel time и cpu time тоже меньше на потоке, который ждёт событий.
Поскольку рисование стаканов идёт в main скрипта, нельзя использовать WaitForMultipleObjects для событий из OnQuote и OnStop, нужно MsgWaitForMultipleObjectsEx, чтобы main включался на событиях GUI для стаканов. Её нет в w32, пришлось подключать через cffi-lua. Инструкция по сборке написана тут. Если сравнивать время исполнения между w32 и cffi-lua, то конечно же w32 быстрее (SetEvent на 12%, CreateEvent на 23%).
Важные части кода:

Код
local w32 = require "w32-ext"
local ffi = require "cffi"
local SetEvent = w32.SetEvent
local MsgWaitForMultipleObjectsEx = w32.MsgWaitForMultipleObjectsEx
local cb_t = {'EDM4','SiM4','CRM4','CNYRUB_TOM'}
local e_quote, e_stop
local is_run = true

function OnQuote(class_code, sec_code)
  local sent = cb_t[sec_code]
  if sent then
    cb_t[sec_code] = sent + 1
    SetEvent(e_quote)
  end
end
function OnStop()
    is_run = false
    SetEvent(e_stop)
end
function main()
  e_stop = w32.CreateEvent(nil, 0, 0, nil)
  e_quote = w32.CreateEvent(nil, 0, 0, nil)
  local events = ffi.new("HANDLE[2]", {e_quote, e_stop})
  local WAIT_TIMEOUT = w32.WAIT_TIMEOUT
  while is_run do
    local res = MsgWaitForMultipleObjectsEx(2, events, 500, 7423, 0) -- QS_ALLINPUT == 7423
    if res == 1 then break end
      if res == WAIT_TIMEOUT then
         -- по таймауту обработать сообщения, например, о смене foreground окна
      else
         -- обновить стаканы
      end
    end
  end
end

w32-ext.lua:
Код
local w32 = require("w32")
local ffi = require "cffi"
ffi.cdef [[
    typedef unsigned int UINT;
    typedef unsigned long DWORD;
    typedef long long LRESULT;
    typedef long long LPARAM;
    typedef unsigned long long WPARAM;
    //typedef void* HWND; // in windows
    typedef intptr_t HWND; // uintptr_t not ok as it appends 'ULL', LPARAM also ok
    typedef intptr_t HANDLE;

    void SetLastError(DWORD);
    //LRESULT SendMessageA(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
    HWND GetForegroundWindow();
    DWORD MsgWaitForMultipleObjectsEx(DWORD nCount, const HANDLE *pHandles, DWORD dwMilliseconds, DWORD dwWakeMask, DWORD dwFlags);
]]

w32.SetLastError = ffi.C.SetLastError
w32.GetForegroundWindow = ffi.C.GetForegroundWindow
w32.MsgWaitForMultipleObjectsEx = ffi.C.MsgWaitForMultipleObjectsEx
-- those calls trigger 127 error on first invocation
w32.SetLastError(0)

return w32
 
Цитата
funduk написал:
Для моих целей сообщения тоже оказались заметно быстрее, чем sleep.
У меня возник вопрос: если используется упрощённый вариант работы с событиями
Код
CreateEventA(NULL, false, false, NULL);
WaitForSingleObject
SetEvent
и оба коллбэка OnAllTrade и OnQuote почти одновременно выдали
Код
SetEvent(evt)
При вторичной выдаче SetEvent(evt) эта установка события не выполнится?
Теперь вызывается main и начинает их обрабатывать событие: она проверяет и обрабатывает общую очередь от обоих коллбэков. Это правильная работа с событиями?

Как надо по-минимуму изменить параметры CreateEventA, WaitForSingleObject и SetEvent, чтобы main знала, какой именно коллбэк выдал SetEvent(evt)?
 
И чтобы событие создавалось обоими коллбэками, если даже они это сделали почти одновременно.
 
Насколько я понял, надо использовать параметр lpName в CreateEventA: коллбэки создают события со своими именами. Осталось уточнить: что будет, если оба коллбэка успеют создать события, а main ещё не запустилась для их обработки. Не будет ли потеряно второе событие?
 
Цитата
Serge123 написал:
Насколько я понял, надо использовать параметр lpName в CreateEventA: коллбэки создают события со своими именами. Осталось уточнить: что будет, если оба коллбэка успеют создать события, а main ещё не запустилась для их обработки. Не будет ли потеряно второе событие?
createEvent создает не событие, а объект "событие)
упрощенно это значит, что OS выделяет для флага события ячейку и вернет его имя
Имя флага всегда уникально.
-----------------
Далее мы либо устанавливаем Set  флаг или сбрасываем  Reset.
Wait.. - это как бы аналог условного цикла проверки состояния флага c заданием времени ожидания.
Проверку делает OC а поток бездействует пока Wait
------------
Если событий много то используем wait дя нескольких событий либо более сложные функции ожидания (семафоры и т д)
=================
Если есть желание изучить механизмы синхронизации потоков рекомендую книгу:

 
 
Насколько я понял, т.к. в моём случае параметр dwFlags у CreateEventA равен 0, то ResetEvent не требуется. Похоже, когда Виндовс по установленному коллбэком флагу события запускает main, то Виндовс сбрасывает этот флаг, поэтому в принципе возможна потеря возникновения события. Ясно что события не буферизуются. Ожидание множества событий тут по-моему не очень подходит, т.к. main должна отрабатывать при возникновении хотя бы одного события.

Такую книжку Рихтера (только в синей ламинированной обложке) я купил давно, тогда я ещё не программировал на Си. Пока что меня не интересует общая теория семафоров, светофоров и шлагбаумов: я сейчас конкретно переписываю скрипт с Луа на Си dll, отвлекаться на эксперименты неохота. Пока у меня обработка событий на Си происходит в коллбэках и я собираюсь перенести её в main с использованием очереди и объектов событий, как это у меня работает в Луа.
 
Насколько я понял из https://learn.microsoft.com/ru-ru/windows/win32/api/synchapi/nf-synchapi-setevent , Виндовс не суммирует установку событий во флаге события, если несколько раз вызвать SertEvent. Поэтому main должна проверять, не был ли вызван другой коллбэк, установка события от которого могла быть потеряна.
 
Тут возникла новая проблема: если обработчик OnAllTrade на Си уже не будет обрабатывать этот вызов, а будет что-то записывать в очередь и делать выход, то как гарантировать, что память под таблицу alltrade не будет освобождена до обработки этой таблицы в функции main, которая тоже находится в dll? Совсем отключить сборку мусора, или есть что-то ещё?
 
Цитата
Serge123 написал:
Тут возникла новая проблема: если обработчик OnAllTrade на Си уже не будет обрабатывать этот вызов, а будет что-то записывать в очередь и делать выход, то как гарантировать, что память под таблицу alltrade не будет освобождена до обработки этой таблицы в функции main, которая тоже находится в dll? Совсем отключить сборку мусора, или есть что-то ещё?
могу лишь рассказать как я решил эту проблему.
У меня колбеки обрабатываются не только в main, но и в других, новых потоках.
Так как все колбеки вызываются последовательно, то нет надобности делать много Event
У меня один event  (см мои скрипты на форуме)  и очередь.
Т е вызвал терминал колбек. В колбеке в очередь записывается номер колбека и входные параметры.
Если очередь пустая, то устанавливается флаг события.
В функции main  , если очередь пустая wait ждет событие, иначе проводится обработка очереди.
Если колбеки не требуют сложных вычислений, то они обрабатываются внутри main.
Для сложной обработки, например , в портфеле множество бумаг, то для принятия решения для каждой бумаги вызывается новый поток из пула потоков ОC.
В этом потоке я запускаю LuasJIT (могу запустить python, terra,julia и др)  вместо lua, что обеспечивает ускорение вычислений на порядок, по сравнению с вычислениями в main.
 
Цитата
nikolz написал:
могу лишь рассказать как я решил эту проблему.
К сожалению, в этом ответе не сказано о решении этой проблемы...
А именно: как в main (которая сидит в dll) обработать таблицу alltrade, если при завершении работы OnAllTrade (которая тоже сидит в dll) стек её вызова (в котором сидит ссылка на табл. alltrade) должен очиститься?
Я вижу такие костыли:
- запретить сборку мусора (до 18:45).
- dll из OnAllTrade как-то связывается с Lua и Lua дублирует ссылки на alltrade в массив локальных переменных области видимости этого файла lua. Потом dll как-то даёт знать, что каким-то из этих переменных можно присвоить nil. Как это сделать, пока не знаю. М.б. это делается через (light)userdata, но я с этим не связывался.

Как из Си заставить Lua продублировать ссылку на таблицу, если эта ссылка сидит в стеке? Как из Си вызвать скрипт Lua, чтобы он получил этот стек вызова OnAllTrade? Иначе придётся коллбэки делать на Lua и только main на Си, а этого не хочется из-за какой-то потери скорости.
 
Имел в виду, что для МЕНЯ это не решение проблемы, т.к. я не знаю, как применять LuaJIT и прочие штуки...
 
Цитата
Serge123 написал:
Цитата
nikolz написал:
могу лишь рассказать как я решил эту проблему.
К сожалению, в этом ответе не сказано о решении этой проблемы...
А именно: как в main (которая сидит в dll) обработать таблицу alltrade, если при завершении работы OnAllTrade (которая тоже сидит в dll) стек её вызова (в котором сидит ссылка на табл. alltrade) должен очиститься?
Я вижу такие костыли:
- запретить сборку мусора (до 18:45).
- dll из OnAllTrade как-то связывается с Lua и Lua дублирует ссылки на alltrade в массив локальных переменных области видимости этого файла lua. Потом dll как-то даёт знать, что каким-то из этих переменных можно присвоить nil. Как это сделать, пока не знаю. М.б. это делается через (light)userdata, но я с этим не связывался.

Как из Си заставить Lua продублировать ссылку на таблицу, если эта ссылка сидит в стеке? Как из Си вызвать скрипт Lua, чтобы он получил этот стек вызова OnAllTrade? Иначе придётся коллбэки делать на Lua и только main на Си, а этого не хочется из-за какой-то потери скорости.
Попробую объяснить.
------------------
Не важно, что и где сидит.   dll - это код программы.  Для кода не нужна синхронизация потоков.
Синхронизация потоков нужна при обращении к данным.
----------------
Попробую снова объяснить как Ваша проблема решается на моей программе.
ОЧЕРЕДЬ и EVENT - эти элементы и решают проблему.
------------------
Поясняю на примере:
1) Терминал вызывает OnAllTrade и передает в стеке указатель на таблицу, которая является новой строкой таблицы alltrade.
--------------------
2) В OnAllTrade указатель на полученную таблицу новой строки записывается в очередь  
Cохраняется в другой таблице КАРЛ ! !
-----------------------
3) OnAlltrade заканчивает работу.
Если очередь при этом содержит лишь один элемент, то устанавливается флаг EVENT.
----------------------------------
4)  Если функция main была в состоянии WAIT, то она начинает обрабатывать элемент очереди
И о чудо, Карл!!!  там есть указатель на таблицу  новой строки alltrade,
при этом  onAllTrade давно уже завершилась или может быть даже снова вызвана с новой таблицей новой строки.  
Ты понимаешь, Карл? Снова хоть тысячу раз!!!
--------------------------
Если функция main обрабатывала очередь, то она и продолжает это делать,
а новые вызовы onAllTrade подкидывают новые таблицы  новых строк alltrade  в очередь.
--------------------------
И это все.
Нет никаких проблем в работе моей программы,
Осталась лишь тебе понять то, что я написал Карл!!!
 
 
Цитата
nikolz написал:
Нет никаких проблем в работе моей программы,Осталась лишь тебе понять то, что я написал Карл!!!
Я не Карл...
Это уже какая-то патология...
 
Цитата
Serge123 написал:
Цитата
nikolz написал:
Нет никаких проблем в работе моей программы,Осталась лишь тебе понять то, что я написал Карл!!!
Я не Карл...
Это уже какая-то патология...
Тогда не волнуйтесь.
Я вам раньше уже ответил, что мой вариант решает вашу проблему.
Но Вы стали утверждать обратное, так как ничего не поняли.
--------------------
Я на вашем примере написал ликбез для карла и других читателей.
Вы можете это не читать.  
Я же Вас не заставляю, а Вы меня не нанимали.
---------------------
Вместо "спасибо" за то, что я вам разжевал  бесплатно что-то и в рот положил,
вы надулись как рыба луна.
вот это действительно патология.
----------------------------
Я просто погулять вышел, ты понял Карл!!!
.
Страницы: 1
Читают тему
Наверх