Способы Повышения Производительности, методы оптимизации lua кода |
Здравствуйте, гость ( Вход | Регистрация )
Способы Повышения Производительности, методы оптимизации lua кода |
3.1.2009, 15:02
Сообщение
#1
|
|
RusHub team lead Группа: Модераторы Сообщений: 4 030 Регистрация: 20.6.2008 Из: г. Королёв (Моск. обл.) Пользователь №: 46 Спасибо сказали: 1708 раз |
Довольно давно на форуме летали мысли по созданию подобной темы. В данной теме предполагается раскрывать как общие методы оптимизации, которые применяются ко всем lua скриптам, так и методы оптимизации конкретных кусков кода.
Итак, немного слов о том как работает lua. LUA - это скриптовый язык программирования, то есть для работы ему не обязательна предварительная компиляция. "Загрузка" lua кода осуществляется интерпретатором. Существуют и скомпилированные lua коды. Компиляция осуществляется с помощью инструмента luac. Преимущества скомпилированного кода:
Основные методы оптимизации. При разработке программ на Lua нужно иметь в виду, что компилятор этого языка не обладает широкими способностями по оптимизации. С другой стороны, интерпретатор Lua реализован очень эффективно и чаще всего обеспечивает весьма приличную скорость работы. Если же нужна сверхвысокая производительность, то мы должны сами следить за тем, чтобы в коде не было множества лишних конструкций. Добиться этого будет легче, если мы будем придерживаться нескольких простых правил при написании кода на Lua.
Управление памятью Язык Lua автоматически управляет памятью при помощи сборщика мусора (garbage collector): интерпретатор периодически вызывает сборщик мусора, удаляющий объекты, для которых была выделена память (таблицы, userdata, функции, потоки и строки) и которые стали недоступными из Lua («мертвые» объекты). Интерпретатор Lua задает предел объема памяти (threshold), занимаемого данными. Как только занятый объем достигнет этого предела, запускается алгоритм, освобождающий память, занятую накопившимися «мертвыми» объектами. Затем устанавливается новый предел, равный двукратному объему памяти, занимаемой после очистки. Чтобы запустить сборку мусора немедленно, нужно программно установить предел занимаемой памяти в 0 (вызвав lua_setgcthreshold() из C или collectgarbage() из Lua). Чтобы остановить сборку мусора, устанавливаем этот предел в достаточно большое значение. Замечено, что в некоторых случаях память, занимаемая данными Lua, проявляет тенденцию к разрастанию, что может негативно сказаться на производительности программы. Чтобы избежать этого, лучше периодически вызывать сборку мусора принудительно. При написании программ на Lua обязательно учитывай то, каким образом интерпретатор Lua управляет распределением памяти. Сборка мусора в версии 5 — относительно затратная по производительности операция (используется алгоритм non-incremental mark and sweep). Она инициируется автоматически, когда объем выделенной памяти превышает двукратный объем памяти, оставшейся выделенной после предыдущей сборки мусора (объем выделенной памяти, при превышении которого произойдет следующая сборка мусора, также можно задавать программно). Значит, фактически, в достаточно большой динамической системе сборка мусора может быть запущена в произвольный момент времени, вызвав просадку по производительности. Чем больше памяти выделено под объекты Lua, тем дольше происходит сбор мусора. Итак, если мы, например, читаем в Lua данные из файла построчно и склеиваешь их в одну строку линейно, скорее всего, процесс будет продвигаться страшно медленно: после каждой следующей операции склейки объем занятой памяти, как минимум, удваивается и, соответственно, запускается процесс сборки мусора. Код создает новую строку (и присваивает ее переменной some_string), а строка, хранившаяся в some_string до склейки, остается в памяти до следующей сборки мусора: Код local some_string = some_string.."a"
|
|
|
3.1.2009, 18:19
Сообщение
#2
|
|
Самый главный активист :-D Группа: Модераторы Сообщений: 2 790 Регистрация: 29.6.2008 Из: г. Тула Пользователь №: 97 Спасибо сказали: 440 раз |
Сразу вопрос, интерпретатор переводит скрипт в бинарный вид один раз при загрузке или каждый раз при обращении к скрипту?
|
|
|
3.1.2009, 19:26
Сообщение
#3
|
|
RusHub team lead Группа: Модераторы Сообщений: 4 030 Регистрация: 20.6.2008 Из: г. Королёв (Моск. обл.) Пользователь №: 46 Спасибо сказали: 1708 раз |
Конечно же 1 раз при загрузке скрипта. Весь загруженный бинарный код находится в ОЗУ до тех пор пока скрипт не будет остановлен или перезапущен.
|
|
|
3.1.2009, 20:35
Сообщение
#4
|
|
Самый главный активист :-D Группа: Модераторы Сообщений: 2 790 Регистрация: 29.6.2008 Из: г. Тула Пользователь №: 97 Спасибо сказали: 440 раз |
А смысл ли его тогда компилировать?! Или если скрипт скомпилированный то в памяти он хранится не будет?
|
|
|
3.1.2009, 20:46
Сообщение
#5
|
|
RusHub team lead Группа: Модераторы Сообщений: 4 030 Регистрация: 20.6.2008 Из: г. Королёв (Моск. обл.) Пользователь №: 46 Спасибо сказали: 1708 раз |
я же написал чем отличается скомпилированный код от нескомпилированного.
1) Выполняется простейшая оптимизация. 2) Скомпилированный код быстрее загружается в память. |
|
|
11.1.2009, 19:43
Сообщение
#6
|
|
RusHub team lead Группа: Модераторы Сообщений: 4 030 Регистрация: 20.6.2008 Из: г. Королёв (Моск. обл.) Пользователь №: 46 Спасибо сказали: 1708 раз |
Вместо кода:
Код string.match(sData, "%b<>%s+(%S+)") можно писать код:Код sData:match"%b<>%s+(%S+)" Оптимизация состоит в использовании метода вместо функции, ну и конечно же меньше символов писать (в свою очередь интерпретатор загружает данный код чуть-чуть быстрее). И ещё обратите внимание на отсутствие внешних скобок Можно было написать так: Код sData:match("%b<>%s+(%S+)") однако, в данном случае внешние скобки можно опустить)))Скобки у аргумента функции (или метода) можно опускать только в том случае, когда аргумент один и этот аргумент представляет из себя постоянную строку без конкатенаций. |
|
|
15.1.2009, 19:39
Сообщение
#7
|
|
Самый главный активист :-D Группа: Модераторы Сообщений: 2 790 Регистрация: 29.6.2008 Из: г. Тула Пользователь №: 97 Спасибо сказали: 440 раз |
К вопросу об оптимизации. я пишу так(когда учился где то подсмотрел ):
Код function ChatArrival(tUser, sData) sData = string.sub(sData,1,-2) local _,_,CmdMain = string.find(sData, "%b<>%s+(%S+)") if CmdMain == "!startAR" then ... end end Некоторые делают более хитрый код через функции. какой самый оптимальный код для нахождения команды и последующего выполнения действий? |
|
|
15.1.2009, 21:08
Сообщение
#8
|
|
RusHub team lead Группа: Модераторы Сообщений: 4 030 Регистрация: 20.6.2008 Из: г. Королёв (Моск. обл.) Пользователь №: 46 Спасибо сказали: 1708 раз |
Самый оптимальный:
Код function ChatArrival(tUser, sData) if sData:find"%b<>%s!startAR|" then ... end end Однако чаще всего используется не одна команда, поэтому так: Код function ChatArrival(tUser, sData)
local CmdMain = sData:match"%b<>%s(%S+)|" if CmdMain then if CmdMain=="!startAR" then ... elseif ... end end end |
|
|
28.1.2009, 20:23
Сообщение
#9
|
|
Самый главный активист :-D Группа: Модераторы Сообщений: 2 790 Регистрация: 29.6.2008 Из: г. Тула Пользователь №: 97 Спасибо сказали: 440 раз |
Есть два почти одинаковых куска кода(практически по странице каждый) отличаются только таблицами. Будет ли оптимизация если вместо них использовать функцию типо func(tTable)
? Как часто можно использовать сборщик мусора? Вот например накидал за 5 минут лог маинчата каждое отправленное сообщение пока сохраняется сразу, при активном общении количество используемой памяти растёт в разы. Добавил сборщик мусора после каждого сохранения. Проблема решина, но целисообразно ли использовать такой код? |
|
|
28.1.2009, 21:01
Сообщение
#10
|
|
RusHub team lead Группа: Модераторы Сообщений: 4 030 Регистрация: 20.6.2008 Из: г. Королёв (Моск. обл.) Пользователь №: 46 Спасибо сказали: 1708 раз |
Безусловно, одинаковый по функциональности код следует заносить в отдельную функцию. Это не столько оптимизация, сколько хороший тон программирования, хотя небольшая оптимизация также присутствует в этом.
Сборщик мусора следует использовать как можно реже, так как данная операция ресурсоёмка. По большей части мусор накапливается из-за конкатенаций, потому что при складывании строк память выделяется на каждый кусок складываемой строки ( |
|
|
2.8.2010, 22:13
Сообщение
#11
|
|
Начинающий Группа: Пользователи Сообщений: 12 Регистрация: 15.7.2009 Пользователь №: 3 884 Спасибо сказали: 8 раз |
В lua внутри функций можно создавать новые. Причём этим функциям доступны локальные переменные создавшей.
Это позволяет легко создавать подобие класса. Код function new_object() local obj = {} local private_var = "private" obj.public_var = "public" function obj.public_fnc() return obj.public_var end local function private_fnc() return private_var end return obj end возможен даже синглтон Код single_object = (function()
local obj = {} local private_var = "private" obj.public_var = "public" function obj.public_fnc() return obj.public_var end local function private_fnc() return private_var end return obj end)() -- пустая пара скобок это вызов функции созданной в первой. |
|
|
23.9.2010, 10:09
Сообщение
#12
|
|
RusHub team lead Группа: Модераторы Сообщений: 4 030 Регистрация: 20.6.2008 Из: г. Королёв (Моск. обл.) Пользователь №: 46 Спасибо сказали: 1708 раз |
Частое действие в скриптах - это проверка сообщения чата на ввод пользователем какой-либо команды.
Практически в каждом скрипте делается данная проверка. Данное действие должно быть максимально оптимизировано. Наиболее оптимизированный вариант: Код function ChatArrival(tUser, sData) local iPos = #tUser.sNick + 4 if sData:sub(iPos, iPos):find('!', 1, true) then --поиск префикса команды -- дальнейшие действия (в том числе поиск и сравнение команд) end end В чём преимущество? В таком использовании метод find работает очень эффективно, так как, во-первых, регулярные выражения отключены, во-вторых, строки сравниваются блоками в памяти (такое сравнение практически мгновенное). Представим на мгновение что поиск команд осуществляется каждый раз, когда мы пишем сообщение в чат, да ещё и не один раз, а столько раз, в скольких скриптах используются команды. Предложенный вариант поиска основан на поиске единого префикса команд, ведь команды юзаются пользователями относительно редко, а сообщения чата практически никогда не начинаются с указанного префикса. Применяя предложенный вариант в своих скриптах, мы по максимуму оптимизируем события чата |
|
|
23.9.2010, 11:57
Сообщение
#13
|
|
Главный ра******й тут... Группа: Главные администраторы Сообщений: 1 727 Регистрация: 18.5.2008 Из: RF, 2la Пользователь №: 1 Спасибо сказали: 776 раз |
Я ошибаюсь, или оно будет искать во всем сообщении юзера восклицательный знак? Тогда срабатывания могут быть вполне частыми... Можно четко установить что никаких пробелов не должно быть перед командой и обрезать строку до одного символа, затем проверять совпадение символа с префиксом команд, так не оптимальнее?
|
|
|
23.9.2010, 13:11
Сообщение
#14
|
|
Белый Волк Группа: Пользователи Сообщений: 1 723 Регистрация: 11.9.2008 Из: г.Томск Пользователь №: 516 Спасибо сказали: 657 раз |
Цитата '!', #tUser.sNick + 3 Проверяет непосредственно за ником только, как я понял.
|
|
|
23.9.2010, 15:27
Сообщение
#15
|
|
RusHub team lead Группа: Модераторы Сообщений: 4 030 Регистрация: 20.6.2008 Из: г. Королёв (Моск. обл.) Пользователь №: 46 Спасибо сказали: 1708 раз |
Nickolya, ты прав. Так и нужно сделать
Подправил. |
|
|
24.9.2010, 4:53
Сообщение
#16
|
|
7 квадратиков Группа: Модераторы Сообщений: 793 Регистрация: 21.1.2009 Пользователь №: 1 895 Спасибо сказали: 301 раз |
Для универсальности можно ещё объявить local sPrefix = SetMan.GetString(29):sub(1,1) и использовать эту переменную.
Предложенный вариант поиска основан на поиске единого префикса команд, ведь команды юзаются пользователями относительно редко, а сообщения чата практически никогда не начинаются с указанного префикса. А как-же "громкие" ники, начинающиеся со знака, совпадающего с префиксом команд? Обращение к ним будет расценено как команда и пойдёт на дальнейшую проверку. Если таких пользователей несколько и они активно общаются, то дополнительная проверка будет проходить не так уж и редко. |
|
|
24.9.2010, 9:03
Сообщение
#17
|
|
RusHub team lead Группа: Модераторы Сообщений: 4 030 Регистрация: 20.6.2008 Из: г. Королёв (Моск. обл.) Пользователь №: 46 Спасибо сказали: 1708 раз |
Ну это так должно совпасть, чтобы ники начинались с префикса, да ещё и активно общались на хабе.
Это редкое явления. А оптимизация рассчитана на среднестатистический хаб. Потом, ну и что что ники начинаются с префикса, ведь как бы скрипт не был написан, он будет проверять возможные команды, а предложенный вариант написания более оптимизирован для большинства случаев. |
|
|
25.9.2011, 23:48
Сообщение
#18
|
|
Начинающий Группа: Пользователи Сообщений: 12 Регистрация: 15.7.2009 Пользователь №: 3 884 Спасибо сказали: 8 раз |
[*]Большинство команд протокола NMDC оканчивается на символ "|", который служит для разделения команд, когда они находятся в одном потоке или в одной строке. Поэтому эффективнее отсылать 1 раз единую строку с командами, нежели отсылать несколько раз отдельные команды. Код local t = { тоже самое касается и сообщений (если вместо команд отправляются сообщения)."$MyINFO $ALL nick1 $ $ $ $0$", "$MyINFO $ALL nick2 $ $ $ $0$", "$MyINFO $ALL nick3 $ $ $ $0$" } local sStr = "" for i, v in ipairs(t) do sStr = (sStr == "" and "" or sStr.."|")..v end Core.SendToAll(sStr) Ужос какой. Код local t = {
"$MyINFO $ALL nick1 $ $ $ $0$", "$MyINFO $ALL nick2 $ $ $ $0$", "$MyINFO $ALL nick3 $ $ $ $0$" } Core.SendToAll(table.concat(t, "|")) |
|
|
2.1.2012, 3:29
Сообщение
#19
|
|
KEEP CLEAR AT ALL TIMES Группа: Пользователи Сообщений: 141 Регистрация: 4.9.2011 Из: Беларусь, Минск Пользователь №: 9 667 Спасибо сказали: 3 раза |
В Сообщение #12 часть кода:
Код local iPos = #tUser.sNick + 4 Подскажите, пожалуйста, что означает число "4", каков его смысл? P.S.: если некорректно задавать здесь вопрос, я перенесу его в соотв. раздел. |
|
|
2.1.2012, 11:33
Сообщение
#20
|
|
Освоившийся участник Группа: Пользователи Сообщений: 391 Регистрация: 4.11.2009 Из: Дом Пользователь №: 4 923 Спасибо сказали: 239 раз |
#tUser.sNick - число символов в нике
4 - число символов до начала сообщения: Код <Ник> Сообщение| В данном случае длина ника составляет 3 символа, а смещение 4 переходит вперед по символам "<> ", пропуская их. Таким образом это будет позиция первого символа сообщения. ADD: Наиболее оптимизированный вариант: Код function ChatArrival(tUser, sData) local iPos = #tUser.sNick + 4 if sData:sub(iPos, iPos):find('!', 1, true) then --поиск префикса команды -- дальнейшие действия (в том числе поиск и сравнение команд) end end Есть код: Код function ChatArrival(tUser, sData) local iPos = #tUser.sNick + 4 local iLimit = 10000000 local sChar = '' local iStart = os.time() for i = 0, iLimit, 1 do sChar = sData:sub(iPos, iPos):find('!', 1, true) end Core.SendToAll("" .. os.difftime(os.time(), iStart)); iStart = os.time() for i = 0, iLimit, 1 do sChar = sData:sub(iPos, iPos) == '!' end Core.SendToAll("" .. os.difftime(os.time(), iStart)); end Есть результат: Цитата [10:27:50] *16 [10:27:50] *8 [10:27:50] 127.0.0.1 | ?? на хабе Находится <ВВВВВВВВВВВВВВВВВВВВ> test Есть вывод: Прямое сравнение быстрее любых find'ов. ADD: Другой вопрос, если префиксов несколько. Там или циклом или регуляркой. |
|
|
Похожие темы
Тема | Ответов | Автор | Просмотров | Последнее сообщение | |
---|---|---|---|---|---|
От: Способы Повышения Производительности От темы с ID: 1018 |
2 | Setuper | 6 632 | 2.2.2009, 3:13 Посл. сообщение: Setuper |
|
Сейчас: 23.11.2024, 2:20 |