myDC.ru

Здравствуйте, гость ( Вход | Регистрация )

 
2 страниц V  < 1 2  
Ответить в данную темуНачать новую тему

> Способы Повышения Производительности, методы оптимизации lua кода

MIKHAIL
сообщение 2.1.2012, 22:57
Сообщение #21


KEEP CLEAR AT ALL TIMES
****

Группа: Пользователи
Сообщений: 141
Регистрация: 4.9.2011
Из: Беларусь, Минск
Пользователь №: 9 667
Спасибо сказали: 3 раза




Как вариант – использовать модуль, который будет следить за пользователями с "нужным(ми)" префиксами, а также учитывал статистически какие команды, сообщения (правильные или неправильные) вводятся... и, используя эти данные, выбирал (переключался на) наиболее оптимальный вариант поиска для всевозможных случаев (или, по крайней мере, учесть те случаи, которые возможны при использовании тех скриптов, которые уже есть) . А данные от этого модуля могли бы использовать все скрипты.
still_dreaming.gif
Go to the top of the page
+Quote Post
Enyby
сообщение 2.1.2012, 23:10
Сообщение #22


Освоившийся участник
*****

Группа: Пользователи
Сообщений: 391
Регистрация: 4.11.2009
Из: Дом
Пользователь №: 4 923
Спасибо сказали: 239 раз




А? Ты че-то не то пишешь. Или не туда. Тут речь о префиксах команд. Т. е. о наименее ресурсоёмком определение, что именно прислал пользователь - команду или текстовое сообщение в чат.
"!regme" - это команда
"Привет всем!" - это сообщение.
В первом случае скрипт должен отработать и что-то сделать, перехватив вывод команды в чат, а во-втором, пропустить сообщение в чат, ничего не выполняя, с наименьшими затратами ресурсов.

ADD:
Продолжая тему оптимизации.
Тест
Код:
Код
function ChatArrival(tUser, sData)
  local iPos = #tUser.sNick + 4
  local iLimit = 10000000
  local sChar = '!'
  local iChar = sChar:byte(1);
  local iCounter = 0;
  
  local iStart = os.time()
  iCounter = 0
  for i = 0, iLimit, 1 do
    if sData:sub(iPos, iPos):find('!', 1, true) then
        iCounter = iCounter + 1
    end
  end
  Core.SendToAll(iCounter .. " " .. os.difftime(os.time(), iStart));
  
  iStart = os.time()
  iCounter = 0
  for i = 0, iLimit, 1 do
    if sData:sub(iPos, iPos) == '!' then
       iCounter = iCounter + 1
    end
  end
  Core.SendToAll(iCounter .. " " .. os.difftime(os.time(), iStart));
  
  iStart = os.time()
  iCounter = 0
  for i = 0, iLimit, 1 do
    if sData:byte(iPos) == iChar then
       iCounter = iCounter + 1
    end
  end
  Core.SendToAll(iCounter .. " " .. os.difftime(os.time(), iStart));
end

Результат:
Код
[22:18:22] *10000001 19
[22:18:22] *10000001 11
[22:18:22] *10000001 10
[22:18:22] 127.0.0.1 | ??    на хабе    Находится <ВВВВВВВВВВВВВВВВВВВВ> !say test
[22:19:11] *0 18
[22:19:11] *0 11
[22:19:11] *0 9
[22:19:11] 127.0.0.1 | ??    на хабе    Находится <ВВВВВВВВВВВВВВВВВВВВ> test

Вывод:
Самый быстрый способ - использование byte, для получения кода конкретного символа.


UPD: Исправил ошибку в коде.

PS что интересно, так это четко видная буферизация вывода. В клиент все пришло одновременно, независимо от того, что отдельные строки отсылались с разрывом порядка 10 секунд.


Спасибо сказали:
Go to the top of the page
+Quote Post
MIKHAIL
сообщение 2.1.2012, 23:11
Сообщение #23


KEEP CLEAR AT ALL TIMES
****

Группа: Пользователи
Сообщений: 141
Регистрация: 4.9.2011
Из: Беларусь, Минск
Пользователь №: 9 667
Спасибо сказали: 3 раза




Это с идеальной точки зрения...
Некоторые пишут:
Цитата
"Привет!"

А некоторые:
Цитата
"!Всем привет!"
"!С наступившим Новым 2012 Годом!"

И так далее... надо и такое учитывать,)
Enyby, Вы так "жёстко" определили где команда, а где сообщение. Это понятно, правильно. Но ведь есть разные случаи жизни, кто-то ошибается с вводом команды... и получается сообщение... по разным причинам. Или кто-то подбирает команды... а в результате могут получатся и команды, и сообщения.

P.S.:
Код
[22:07:31] 127.0.0.1 | ??    на хабе    Находится <ВВВВВВВВВВВВВВВВВВВВ> test

Уточните, пожалуйста, что это?
Go to the top of the page
+Quote Post
Enyby
сообщение 2.1.2012, 23:29
Сообщение #24


Освоившийся участник
*****

Группа: Пользователи
Сообщений: 391
Регистрация: 4.11.2009
Из: Дом
Пользователь №: 4 923
Спасибо сказали: 239 раз




Ну и что. На 10 млн проверок (!) нужно порядка 10 секунд, на не самой быстрой машине. Т. е. порядка 1 млн проверок в секунду. Это не существенно. У вас юзеров за спам забанит намного раньше.

ADD:
Цитата(MIKHAIL @ 2.1.2012, 22:11) *
Enyby, Вы так "жёстко" определили где команда, а где сообщение. Это понятно, правильно. Но ведь есть разные случаи жизни, кто-то ошибается с вводом команды... и получается сообщение... по разным причинам. Или кто-то подбирает команды... а в результате могут получатся и команды, и сообщения.

А так и есть. Все варианты не предугадаешь. Так что и не стоит даже заморачиваться. Завтра кошка будет по клавиатуре бегать, так вы скрипт анти кошка писать будете? big_smile.gif
Цитата(MIKHAIL @ 2.1.2012, 22:11) *
Код
[22:07:31] 127.0.0.1 | ??    на хабе    Находится <ВВВВВВВВВВВВВВВВВВВВ> test

Уточните, пожалуйста, что это?

Это вывод в ДС клиенте сообщения в чат. Временной штамп, ип, страна, локация/провайдер, ник и само сообщение.
Чтоб было понятнее - запустите тестовый скрипт и отправьте сообщение.
Правда хаб подвисает на время брожения в циклах. Так что это нормально.

ADD:
В общем стоит задача отсеять "возможно команды" от точно "не команд". Все что началось не с префикса - командой быть не может. А если есть префикс, то тогда нужно регулярку, захватывать текст и проверять что это, известная команда или отсебятина. В любом случае, это будет менее ресурсоёмко, чем проверка каждого сообщения регуляркой.


Спасибо сказали:
Go to the top of the page
+Quote Post
AirKobra45
сообщение 14.3.2012, 19:07
Сообщение #25


Активный участник
***

Группа: Пользователи
Сообщений: 94
Регистрация: 2.10.2010
Из: Якутия, г.Нерюнгри
Пользователь №: 7 820
Спасибо сказали: 21 раз




И так далее... надо и такое учитывать,)

Ну так после отсеивания возможной команды следует проверка на саму команду. Так что если там и кошка пробежала то ничего страшного, выведется в чат как сообщение! А уж если команда то тогда уж и действие и return true скрипту в руки!

Всё выше описанное участниками форума делается примерно так
Код
function ChatArrival(tUser, sData)
  local iPos = #tUser.sNick + 4
  if sData:sub(iPos, iPos):find('!', 1, true) then --поиск префикса команды

                        -- дальнейшие действия (в том числе поиск и сравнение команд)
        local cmd = sData:sub(iPos+1,-2):match"(%S+)" --забираем то что после префикса
    if cmd == "команда" then -- Вот и само сравнение команды
      --Действие
              Core.SendToAll("<"..tUser.sNick.."> ".."Скрипт работает!") --Например так!
    return true  --Не даём команде попасть в чат.
    end
  end
end
Go to the top of the page
+Quote Post
alex82
сообщение 12.3.2013, 4:53
Сообщение #26


Местный
*******

Группа: Неактивированные
Сообщений: 908
Регистрация: 26.12.2008
Пользователь №: 1 574
Спасибо сказали: 1406 раз




А вот небольшое замечание, касающееся хаба PtokaX.

Как известно, скриптовые функции, отправляющие данные юзерам (Core.SendToAll, Core.SendToUser, и.т.д.) не требуют наличия в конце строки символа |, и, при необходимости, добавляют его сами. Удобно, не спорю. Но давайте заглянем в исходный код:
Код
    if(sData[szLen-1] != '|') {
        memcpy(g_sBuffer, sData, szLen);
        g_sBuffer[szLen] = '|';
        g_sBuffer[szLen+1] = '\0';
        UserSendCharDelayed(u, g_sBuffer, szLen+1);
    } else {
        UserSendCharDelayed(u, sData, szLen);
    }

На этом куске функции Core.SendToUser нам вырисовывается такая картина маслом: если функция получила строку, завершающуюся символом |, она просто отправляет данные юзеру, ну а если символа | в конце нет, то вся строка копируется в буфер, затем к ней добавляется | и завершающий нулевой байт, и только после этого она отсылается юзеру.

Вывод достаточно прост: если при отправке данных из скриптов вы будете сами добавлять | в конце, эти скрипты будут работать чуток быстрее.


Спасибо сказали:
Go to the top of the page
+Quote Post
mariner
сообщение 12.3.2013, 7:23
Сообщение #27


Местная ТехПоддержка
**********

Группа: Администраторы
Сообщений: 1 875
Регистрация: 18.7.2008
Из: Моск. Обл, г. королев, район Болшево
Пользователь №: 221
Спасибо сказали: 220 раз




Тут должен был быть пост с вопросом : "зачем пепяке с++ при таком коде?" Но его тут не будет.


Спасибо сказали:
Go to the top of the page
+Quote Post
Setuper
сообщение 12.3.2013, 15:26
Сообщение #28


RusHub team lead
**************

Группа: Модераторы
Сообщений: 4 030
Регистрация: 20.6.2008
Из: г. Королёв (Моск. обл.)
Пользователь №: 46
Спасибо сказали: 1708 раз




Это не единственный грабли. Если присмотреться к исходникам, то там их можно очень много найти. Я уже не говорю о "макаронности" кода.
По всей видимости у них действует правило: "Работает - не трогай!", и поэтому рефакторинг кода никогда не делается.


Спасибо сказали:
Go to the top of the page
+Quote Post
MIKHAIL
сообщение 24.3.2013, 19:17
Сообщение #29


KEEP CLEAR AT ALL TIMES
****

Группа: Пользователи
Сообщений: 141
Регистрация: 4.9.2011
Из: Беларусь, Минск
Пользователь №: 9 667
Спасибо сказали: 3 раза




Прочитав критическое замечание (Сообщение #26) от alex82 и будучи новичком в С++, все же задам вопрос: а какие варианты решения возможны в данном случае?
Предположу, что автор Птоки таким образом обходит всевозможные варианты ошибок, связанные с некорректным завершением отсылаемых команд/сообщений, т.е. в конце всегда должен быть символ "|".
P.S.: читая со стороны одну лишь критику, для непосвящённого довольно сложно почерпнуть что-то полезное.
Go to the top of the page
+Quote Post
Setuper
сообщение 24.3.2013, 21:10
Сообщение #30


RusHub team lead
**************

Группа: Модераторы
Сообщений: 4 030
Регистрация: 20.6.2008
Из: г. Королёв (Моск. обл.)
Пользователь №: 46
Спасибо сказали: 1708 раз




проблема не в символе, а в лишнем копировании данных

не смотрел реализацию функции UserSendCharDelayed, но возможно стоило написать так:

Код
UserSendCharDelayed(u, sData, szLen);
if(sData[szLen-1] != '|') {
  UserSendCharDelayed(u, "|", 1);
}


Спасибо сказали:
Go to the top of the page
+Quote Post
Alexey
сообщение 29.3.2013, 18:15
Сообщение #31


7 квадратиков
*******

Группа: Модераторы
Сообщений: 793
Регистрация: 21.1.2009
Пользователь №: 1 895
Спасибо сказали: 301 раз




Цитата(alex82 @ 12.3.2013, 4:53) *
Вывод достаточно прост: если при отправке данных из скриптов вы будете сами добавлять | в конце, эти скрипты будут работать чуток быстрее.


Вот что по поводу этого совета говорит PPK:
Раскрывающийся текст
Цитата(PPK)
[05:17] <PPK> he is wrong
[05:17] <PPK> adding pipe in lua is slower that when ptokax add it laughing.gif
[05:19] <PPK> example:
sMsg = "This is a test"
slow way -> Core.SendToAll((2, sMsg.."|")
fast way -> Core.SendToAll((2, sMsg)
[15:27:31] <PPK> function OnStartup()
starttime = os.time()
Core.SendToAll("Start: "..tostring(starttime))

sMsg = "This is a test"

for i = 1, 20000000 do
Core.SendToProfile(2, sMsg.."|")
end

endtime = os.time()
Core.SendToAll("End: "..tostring(endtime))
Core.SendToAll("Diff: "..tostring(endtime-starttime))
end
[15:27:57] <PPK> x64 ptokax + 4 GB memory (result of this script is memory usage close to 4 GB)
[15:28:07] <PPK> first test was with sMsg = "This is a test|"
[15:28:19] <PPK> that should be fastest -> taken 12 second
[15:28:30] <PPK> second test was with sMsg = "This is a test"
[15:28:49] <PPK> that should be slower, but again take only 12 second
[15:29:19] <PPK> and thirt test was with sMsg = "This is a test"
and Core.SendToProfile(2, sMsg.."|") (previous two without adding pipe here)
[15:29:23] <PPK> and this one take 16 seconds
[15:29:49] <PPK> because lua is on string operation allocate new memory, copy string to that new memory and hash that string


В беседу включился sphinx и провёл свои тесты:
Раскрывающийся текст
Цитата(sphinx)
[15:51:49] <sphinx> function OnStartup()
local iStart = os.time()
sMsg = "This is a test"
for i = 1, 10000000 do
Core.SendToProfile(2, sMsg.."|")
end
Core.SendToAll( tostring (os.difftime(os.time(), iStart)))
end
[15:52:00] <sphinx> This takes 6 seconds
[15:52:22] <sphinx> And Core.SendToProfile(2, sMsg) takes only 5 big_smile.gif
[15:56:27] <sphinx> But... If I set Core.SendToProfile(2, "This is a test|") it takes 4 seconds big_smile.gif
[15:57:59] <sphinx> And in real script I do need that string operation, so you right - it will be slower to add a pipe


Если вкратце: добавлять | с помощью конкатенации — плохая идея, не стоит так делать. Если обойтись без конкатенации, то ускорение в их тестах едва заметно и результаты часто совпадают с результатами без |.


Спасибо сказали:
Go to the top of the page
+Quote Post
Setuper
сообщение 30.3.2013, 15:36
Сообщение #32


RusHub team lead
**************

Группа: Модераторы
Сообщений: 4 030
Регистрация: 20.6.2008
Из: г. Королёв (Моск. обл.)
Пользователь №: 46
Спасибо сказали: 1708 раз




Ну да, что в Lua идет копирование всей строки при конкатенации, что в коде - тоже копирование всей строки.
Действительно, в Lua кроме копирования при каждом создании строки ещё и рассчитывается её хеш код.

Другое дело что в хабе лишнее копирование строк можно было бы исключить, что, однако, PPK не делает big_smile.gif


Спасибо сказали:
Go to the top of the page
+Quote Post
Iskandark
сообщение 19.10.2014, 19:07
Сообщение #33


Активный участник
***

Группа: Пользователи
Сообщений: 61
Регистрация: 24.10.2008
Из: Moscow
Пользователь №: 875
Спасибо сказали: 0 раз




Сообщение 23.9.2010, 11:09 от Setuper

Цитата(Setuper @ 23.9.2010, 11:09) *
Частое действие в скриптах - это проверка сообщения чата на ввод пользователем какой-либо команды.
Практически в каждом скрипте делается данная проверка.
Данное действие должно быть максимально оптимизировано.

Наиболее оптимизированный вариант:

Код
function ChatArrival(tUser, sData)
  local iPos = #tUser.sNick + 4
  if sData:sub(iPos, iPos):find('!', 1, true) then --поиск префикса команды

    -- дальнейшие действия (в том числе поиск и сравнение команд)

  end
end


В чём преимущество?
В таком использовании метод find работает очень эффективно, так как, во-первых, регулярные выражения отключены, во-вторых, строки сравниваются блоками в памяти (такое сравнение практически мгновенное). Представим на мгновение что поиск команд осуществляется каждый раз, когда мы пишем сообщение в чат, да ещё и не один раз, а столько раз, в скольких скриптах используются команды.

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

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


Возник вопрос. Использование сравнения более производительно, чем использование метода find?
какой код более производительный?

Код
function ChatArrival(tUser, sData)
  local iPos = #tUser.sNick + 4
  if sData:sub(iPos, iPos) == '!'  then --поиск префикса команды
    -- дальнейшие действия (в том числе поиск и сравнение команд)
  end
end


или

Код
function ChatArrival(tUser, sData)
  local iPos = #tUser.sNick + 4
  if sData:sub(iPos, iPos):find('!', 1, true) then --поиск префикса команды
    -- дальнейшие действия (в том числе поиск и сравнение команд)
  end
end
Go to the top of the page
+Quote Post
Setuper
сообщение 20.10.2014, 10:24
Сообщение #34


RusHub team lead
**************

Группа: Модераторы
Сообщений: 4 030
Регистрация: 20.6.2008
Из: г. Королёв (Моск. обл.)
Пользователь №: 46
Спасибо сказали: 1708 раз




Да, действительно, find - это лишнее, ведь у нас остался 1 символ.
Простое сравнение уместнее.


Спасибо сказали:
Go to the top of the page
+Quote Post

2 страниц V  < 1 2
Ответить в данную темуНачать новую тему
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

Collapse

> Похожие темы

  Тема Ответов Автор Просмотров Последнее сообщение
No New Posts От: Способы Повышения Производительности
От темы с ID: 1018
2 Setuper 6 276 2.2.2009, 3:13 Посл. сообщение: Setuper

 



RSS Сейчас: 28.3.2024, 13:34