Nazgool 250 Опубликовано 14 Января 2013 Поделиться Опубликовано 14 Января 2013 (изменено) Очень интересная реализация Lua OOP представлена в комплекте Lua for Windows (для тех у кого есть) по адресу ...\5.1\lua\loop Там есть чему поучиться. Изменено 14 Января 2013 пользователем Gun12 1 Ссылка на комментарий
Shredder 49 Опубликовано 15 Января 2013 Поделиться Опубликовано 15 Января 2013 (изменено) Сравнил скорость работы реализации классов, которая здесь вчера обсуждалась: --вариант luabindclass "TestClass"function TestClass:__init()self.num = 100endfunction TestClass:set(n)self.num = nendfunction TestClass:get()return self.numend--вариант прототипного ООП в луа без наследованияPsevdoClass = {}function PsevdoClass:new()obj = {}setmetatable(obj, self)self.__index = selfreturn objendfunction PsevdoClass:initialize()self.num = 100endfunction PsevdoClass:set(n)self.num = nendfunction PsevdoClass:get()return self.numend--вариант "без класса"function s_set(_self, n)_self.num = nendfunction s_get(_self)return _self.numendfunction run()local a = TestClass()local timer1 = profile_timer()timer1:start()for i = 1, 1000000 doa:set(i)endtimer1:stop()_m.logf('##TestClass set is ' .. timer1:time());local b = {num = 100}local timer2 = profile_timer()timer2:start()for i = 1, 1000000 dotest.s_set(b,i)endtimer2:stop()_m.logf('##not class set is ' .. timer2:time());local timer5 = profile_timer()timer5:start()local c = PsevdoClass:new()c:initialize()for i = 1, 1000000 doc:set(i)endtimer5:stop()_m.logf('##PsevdoClass set is ' .. timer5:time());local timer3 = profile_timer()timer3:start()for i = 1, 1000000 dolocal n = a:get()endtimer3:stop()_m.logf('##TestClass get is ' .. timer3:time());local timer4 = profile_timer()timer4:start()for i = 1, 1000000 dotest.s_get(endtimer4:stop()_m.logf('##not class get is ' .. timer4:time());local timer6 = profile_timer()timer6:start()for i = 1, 1000000 doc:get()endtimer6:stop()_m.logf('##PsevdoClass get is ' .. timer6:time());end ##TestClass set is 421285.625 ##not class set is 55866.70703125 ##PsevdoClass set is 68698.90625 ##TestClass get is 450332.03125 ##not class get is 58677.0390625 ##PsevdoClass get is 65134.78125 Видим, что реализация PsevdoClass лишь немного уступает варианту "без класса", что вполне приемлемо. Если учесть, что большинство не экспортированных классов наследование не используют, то такой вариант может послужить заменой для различных менеджеров (surge_manager, task_manager, treasure_manager, travel_manager) и не только, т.к. правок кода придётся вносить минимум. Конечно, значительного прироста производительности это не даст, но зачем платить лишнее, если можно по-дешёвке взять. P.S. ColR_iT, спасибо, за то что так вот внезапно натолкнул меня на такой "полезный" вариант реализации ООП в луа. Изменено 15 Января 2013 пользователем Shredder Ссылка на комментарий
ColR_iT 171 Опубликовано 15 Января 2013 Поделиться Опубликовано 15 Января 2013 Это не то, чтобы "полезный" вариант, это единственный вариант реализации ООП в Lua. Что касается сабжа - неужто те, классы, которые реализуют различные менеджеры, так "тормозят"? Время их работы никчёмно, и разница в десять раз на одном миллионе итераций смотрится просто жалко, к тому же класс profile_timer считает время МИКРОсекундах, получается приблизительно полсекунды на весь процесс - это также не делает из этого бесполезной тратой времени и ресурсов. ИМХО, овчинка выделки не стоит. Ссылка на комментарий
Malandrinus 615 Опубликовано 15 Января 2013 Поделиться Опубликовано 15 Января 2013 Shredder, Конечно, значительного прироста производительности это не даст, но зачем платить лишнее, если можно по-дешёвке взять. Ненужная оптимизация - зло. Вот рядом жирные фризы от перебора всех предметов на каждом апдейте, неправильных моделей, создания огромных таблиц с одним и тем же содержимым при каждом входе в функцию, которая вызывается на каждом апдейте, горомоздкие (и часто ненадёжные) операции со строками, листинги десятков if ... elseif ... elseif вместо однострочной выборки из таблицы и прочее в этом духе. Положа руку на сердце, скажи, это всё уже изжили полностью? Если нет, то к чему вся эта мышиная возня вокруг микросекундных затрат на вызовы? Это не то что ненужно, но это даже вредно, поскольку в итоге делает код менее понятным при отсутствии видимой выгоды. При этом, ты тратишь своё время, которое мог бы потратить на более полезные вещи (см. выше какие). Т.е. ты конечно поступай как хочешь, но я бы рассуждал именно так. Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Shredder 49 Опубликовано 15 Января 2013 Поделиться Опубликовано 15 Января 2013 Чтобы изжить, то что ты перечислил и больше, нужно много времени, просматривать код, искать, анализировать, исправлять. Здесь же исправлений очень мало. Создать функцию, которая будет создавать такой класс, скажем NewClass в _g.script. И останется заменить только class "BlaBlaBla" на BlaBlaBla = NewClass() и соответственно конструкторы BlaBlaBla() на BlaBlaBla:new(). Это можно делать время от времени, в поисках того, что ты перечислил. Ты прав конечно, это капля в море, но она не требует особых затрат. ИМХО. Ссылка на комментарий
Desertir 202 Опубликовано 15 Января 2013 Поделиться Опубликовано 15 Января 2013 Раз поднялась тема про оптимизацию... Я давно думал об этом, но руки не дошли, да и все не упомнишь. Может есть желание у кого нибудь составить список проблемных мест в скриптах? Именно по файлам. В одну каску это почти нереально - прошелестить все скрипты, но вдруг уже знают глючные модули. ТЧ 1.0004. SAP и Trans mod github Ссылка на комментарий
ColR_iT 171 Опубликовано 15 Января 2013 Поделиться Опубликовано 15 Января 2013 Даже если и возникнет желание, давайте его обсуждать не здесь, создайте по соседству тему, кто желает, будем обсуждать там, а то мы и так здесь нафилософствовали... Далее, пожалуйста, по существу: конкретный вопрос - конкретный ответ. Ссылка на комментарий
Shredder 49 Опубликовано 16 Января 2013 Поделиться Опубликовано 16 Января 2013 Покумекал тут над "классами". Написал функцию на замену луабинд. Кому-то может будет полезно. Кто-то может укажет на недостатки или, не дай Бог, ошибки: function NewClass(ParentClass)local klass = {}if ParentClass thensetmetatable(klass, ParentClass)klass.__index = klassendfunction klass:new(...)obj = {}setmetatable(obj, self)self.__index = selfobj:__init(...)return objendif not ParentClass thenfunction klass:__init(...)endendreturn klassend --объявляем новый "класс"PsevdoClass = NewClass()--переопределяем конструкторfunction PsevdoClass:__init()self.num = 100end--добавляем нужные методыfunction PsevdoClass:set(n)self.num = nendfunction PsevdoClass:get()return self.numend--объявляем "класс-наследник"SubClass = NewClass(PsevdoClass)--переопределяем метод setfunction PsevdoClass:set(n)self.num = n * 100end--создаём "объекты"a = PsevdoClass:new()b =SubClass:new()a:set(50)b:set(50)print(a:get()) --вернёт 50print(b:get()) --вернёт 5000 Для меня в этой реализации весомым недостатком является отсутствие возможности вызывать перекрываемые методы внутри тех, которые идут им на замену. Но с другой стороны, я слабо себе представляю, где может понадобится наследование. В исходных кодах сталкера наследование не используется, только в случае экспортированных классов. Всё, на этом тему классов в lua больше не затрону. хотелось бы ещё сказать по поводу "проблемных мест". Всё-таки "косяков", существенно снижающих производительность, в кодах сталкера нет, по крайней мере я на такие места не натыкался. Тогда о чём там писать? Небольшой пример, так сказать, одно из недоразумений, которое я нашёл, копаясь в кодах ЗП: это файл работ главного смарта на Затоне (Скадовск). Там много логик анимпоинтов, практически одинаковых. Одна из них: [logic@zat_a2_animp_18]active = animpoint@zat_a2_animp_18suitable = {=check_npc_name(sim_default) =npc_in_zone(zat_a2_sr_noweap)} true, {=check_npc_name(stalker_raider) =npc_in_zone(zat_a2_sr_noweap)} true, {=check_npc_name(zat_b14_stalker_) =npc_in_zone(zat_a2_sr_noweap)} true, {=check_npc_name(artefact_hunter) =npc_in_zone(zat_a2_sr_noweap)} trueprior = 90 Её можно упростить: [logic@zat_a2_animp_18]active = animpoint@zat_a2_animp_18suitable = {=check_npc_name(sim_default:stalker_raider:zat_b14_stalker_:artefact_hunter) =npc_in_zone(zat_a2_sr_noweap)} true, falseprior = 90 Таких логик 22. Так к чему я это. Понятно, что такоё упрощение "капля в море", всязи с этим вопрос, стоит ли для таких вот "нанооптимизаций" создавать тему и там делиться найдеными "проблемными местами". Может по капле на лужу наскребём, а то и на речушку? С другой стороны, для новичков будет справочник "Так делать не надо" 1 Ссылка на комментарий
ColR_iT 171 Опубликовано 16 Января 2013 Поделиться Опубликовано 16 Января 2013 Shredder, а вот с оптимизацией примера логики проколочка... Это только затормозит работу. Причина проста - выборка из таблицы с одним элементом происходит быстрее, чем с несколькими. Опять же таки разница во времени ничтожна, поэтому это скорее не на оптимизацию похоже, а на приведение в божеский вид... Хотя где-где, а в логике есть что оптимизировать, например если взглянуть на функцию cfg_get_switch_conditions в файле xr_logic.script, то можно прийти к не до умению с вопросом: почему именно так? Ссылка на комментарий
Malandrinus 615 Опубликовано 16 Января 2013 Поделиться Опубликовано 16 Января 2013 ColR_iT, эта функция обычно вызывается один раз при загрузке логики. Нет особенного смысла оптимизировать такого рода код. Всякие фризы при загрузке по-любому вызываются не скриптами, а данными: звуками, моделями, текстурами. Скрипты оптимизировать если и имеет смысл, то там, где они вызываются часто, на каждом апдейте, внутри длинного цикла и т.п. и при этом действительно что-то делают: оперируют со строками к примеру. В качестве некоторого оффтопа. Я как-то провёл эксперимент. Удалил с уровня все объекты, очистил биндер актора, короче, убрал почти всю нагрузку, так или иначе связанную со скриптами или вызываемую скриптами. В итоге, fps конечно подрос, но не сказать чтобы прямо драматически. На динамике и в зависимости от настроек графики на большом уровне типа Кордона fps вырастал процентов на 20-30, а то и меньше. Основная нагрузка, получается, приходится на рендер. И это я ведь вообще всё убрал, а на самом деле вряд ли можно ожидать, что всякими оптимизациями можно убрать хотя бы 5-10% от скриптовой нагрузки. В итоге, условно говоря, можно ожидать от самой хардкорной оптимизации ну скажем 3-5% итогового выигрыша в fps. Разумеется, не всё так линейно и просто. Иногда оптимизировать можно и нужно, но в целом, как видно, оптимизация ради самого процесса оптимизации ничего не даёт, кроме потери времени. Если на самом деле выловили большую часть грубых алгоритмических косяков, то в чём тогда проблема то? Пользуйтесь на здоровье и не парьтесь. Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Shredder 49 Опубликовано 16 Января 2013 Поделиться Опубликовано 16 Января 2013 Я не понял, почему затормозит работу? Если верить описанию функции parse_condlist: -- Распарсивает строку src вида: -- {+infop1} section1 %-infop2%, {+infop3 -infop4} section2 ... -- в таблицу: -- { -- 1 = { infop_check = { 1 = {"infop1" = true} }, infop_set = { 1 = {"infop2" = false } }, section = "section1" }, -- 2 = { infop_check = { 1 = {"infop3" = true}, 2 = {"infop4" = false} }, infop_set = {}, section = "section2" }, -- } То в первом случае получим таблицу из 4 элементов, во втором из одного. В чем подвох, можно поподробнее? malandrinus, я так понимаю это ТЧ? Ты бы попробовал в ЗП такую чисту устроить. У меня к сожалению не получилось почистить спавн, я писал в сосденей теме по acdc, но никто не ответил. Чего я проверил, в ТЧ на баре онлайн только около 50 рестрикторов, в ЗП 260. Или они всегда онлайн? Кроме того, судя по логикам некоторых рестрикторов, там происходит обработка квестов. Ещё полно смарт_каверов. Вот как считаете, если убрать логику в скрипты и удалить рестрикторы, это может поднять производительность? Ссылка на комментарий
Viнt@rь 50 Опубликовано 16 Января 2013 Поделиться Опубликовано 16 Января 2013 (изменено) Подскажите пожалуйста, какой вариант менее затратный по времени? 1. NewClass.new 2. ... function NewClass:Get() return self.new end соответственно и присваивание в таком же духе ЗЫ это так, ради интереса и ИМХО первый вариант менее затратный, верно? Изменено 16 Января 2013 пользователем Viнt@rь GUI для конвертера от бардака(всего и вся в форматы сдк) Полезный утиль-"Utilits pack(mod)" Ссылка на комментарий
Malandrinus 615 Опубликовано 16 Января 2013 Поделиться Опубликовано 16 Января 2013 Shredder, в ТЧ на баре онлайн только около 50 рестрикторов, в ЗП 260.... если убрать логику в скрипты и удалить рестрикторы, это может поднять производительность? Это количество само по себе ни о чём не говорит. Во-первых, не все рестрикторы имеют логику. Многие, если не большинство, работают по своему основному назначению, рестрикторами т.е., и никакой скриптовой нагрузки не создают. Это, к примеру, все рестрикторы костров. Во-вторых, не такую уж и нагрузку создают эти рестрикторы, даже если и с логикой. Сама логика может быть большую часть времени с неактивной секцией. Если и активная, то там максимум стоит периодическая проверка на попадание какого-то непися или актора в этот рестриктор. Это достаточно малозатратная операция (к слову сказать, рестрикторы со сферическим шейпом в этом смысле намного предпочтительней боксов). Опять же, делается это с пониженной частотой, в ТЧ - всего 5 раз в секунду. Т.е. не так уж всё плохо в первом приближении. Конечно, надо предметно измерять, тогда и можно будет сказать точно. В ЗП я этого не измерял, поэтому с гарантией не скажу. Плагины Total Commander для работы с игровыми архивами: Архиваторный плагин (для работы с одиночным архивом): link1 link2 Системный плагин (для распаковки установленной игры): link1 link2 Ссылка на комментарий
Shredder 49 Опубликовано 17 Января 2013 Поделиться Опубликовано 17 Января 2013 Подскажите пожалуйста, какой вариант менее затратный по времени? Смысл второго варианта, если он вызывает первый вариант внутри себя, для чего такая обёртка? Ссылка на комментарий
ColR_iT 171 Опубликовано 17 Января 2013 Поделиться Опубликовано 17 Января 2013 Shredder, после этой функции будет вызвана ещё одна - pick_section_from_condlist, которая будет проверять эти самые условия и перебирать она будет все элементы, именно вот здесь, проверка единственного значения в таблице будет быстрее проверки из четырёх, ко всему в последствии, в саму функцию check_npc_name, передадутся четыре параметра, где вновь будет идти их перебор. ИМХО, как сказал Саша, всё это настолько мелочно, что бороться за производительность, жертвуя читабельностью кода, - глупо. Одна загрузка модели с анимациями займёт больше времени, чем 1М итераций с инициализацией переменной. Скорее, нужно просто сделать по "человечески" чтобы код был понятнее и глядя на него становилось ясно, что он делает, плюс по возможности использовать какие-то техники оптимизации кода, что тоже будет не плохо. Одним словом - нужно искать компромисс. malandrinus, да, я знаю, что она вызывается при инициализации схемы, т.е. в момент перехода на какую-то секцию. Почему я упоминул её - она максимально выбивается из общего вида, такое чувство, что её дописывали потом, при этом делал это уже посторонний человек. Непонятное именование переменных, минимум оптимизации - всё это можно было бы сделать иначе, культурнее и быстрее. --# Табличка соответствия что - чем парсить. local tbl_condition = { ["on_actor_dist_le"] = cfg_get_number_and_condlist, ["on_actor_dist_le_nvis"] = cfg_get_number_and_condlist, ["on_actor_dist_ge"] = cfg_get_number_and_condlist, ["on_actor_dist_ge_nvis"] = cfg_get_number_and_condlist, ["on_timer"] = cfg_get_number_and_condlist, ["on_game_timer"] = cfg_get_number_and_condlist, ["on_signal"] = cfg_get_string_and_condlist, ["on_actor_in_zone"] = cfg_get_string_and_condlist, ["on_actor_not_in_zone"] = cfg_get_string_and_condlist, ["on_info"] = cfg_get_condlist, ["on_actor_inside"] = cfg_get_condlist, ["on_actor_outside"] = cfg_get_condlist, ["on_npc_in_zone"] = cfg_get_npc_and_zone, ["on_npc_not_in_zone"] = cfg_get_npc_and_zone } function cfg_get_switch_conditions (ini, section, npc) --# Табличка будет хранить парсеные условия. local parse_tbl = {} --# Индекс условия в табличке parse_tbl. local num = 1 --# Разберём таблицу соответствий tbl_condition. for field, func in pairs(tbl_condition) do --# Здесь: --# field - поле, которое будем парсить; --# func - имя функции, которой будем парсить. --# Проверка того, что выбранный параметр существует в нашей секции. --# По сути нужна для оптимизации, чтобы не обрабатываеть лишнее. if section and ini:section_exist(section) and ini:line_exist(section, field) then --# Порядковый номер одноимённых условий. local i = 1 --# Распарсим поле field при помощи соответствующей функции func. local cond = func(ini, section, field, npc) --# Пока таблица cond существует, т.е. указанное поле парсится, будем вносить услвоия в таблицу parse_tbl. while cond ~= nil do --# Вносим условие в таблицу. parse_tbl[num] = cond --# Увеличиваем индекс. num = num + 1 --# Проверим есть ли одноимённые условия? cond = func(ini, section, field..i, npc) --# Посмотрим есть ли ещё одноимённые условия, вдруг их больше одного!? i = i + 1 end end end --# Возвращаем распарсенные услвоия в виде таблицы. return parse_tbl end Ссылка на комментарий
Shredder 49 Опубликовано 17 Января 2013 Поделиться Опубликовано 17 Января 2013 (изменено) Вот именно, что в оригинале мы будем иметь 4 условия, каждое из которых содержит =check_npc_name(sim_default) и =npc_in_zone(zat_a2_sr_noweap), и того получаем 2 * 4 = 8, это если уж совсем просто рассуждать После "оптимизации" будет одно условие, каждое содежит одну =check_npc_name(sim_default:stalker_raider:zat_b14_stalker_:artefact_hunter) и одну =npc_in_zone(zat_a2_sr_noweap). Т.е. получим 4 + 1 = 5. В любом случае утверждение "Это только затормозит работу" не верно. А если ещё немного модифицировать функцию check_npc_name, чтобы после первого совпадения был return, то и того лучше. Изменено 17 Января 2013 пользователем Shredder Ссылка на комментарий
Clayman 104 Опубликовано 17 Января 2013 Поделиться Опубликовано 17 Января 2013 к слову сказать, рестрикторы со сферическим шейпом в этом смысле намного предпочтительней боксов Интересно узнать, почему? Я все время думал наоборот, исходя из логики: объект бокса имеет меньше полигонов (которые нужно проверять) чем сфера. Ссылка на комментарий
ColR_iT 171 Опубликовано 17 Января 2013 Поделиться Опубликовано 17 Января 2013 (изменено) Clayman, какие полигоны в абстрактной сфере? Это всего лишь точка, вокруг которой программно описывается сфера с указанным радиусом, визуального отображения нет, т.е. модель отсутствует. Изменено 17 Января 2013 пользователем ColR_iT Ссылка на комментарий
Clayman 104 Опубликовано 17 Января 2013 Поделиться Опубликовано 17 Января 2013 (изменено) Теперь понятно, а бокс как программно проверяется? Ну просто интересно, раз-уж поднял тему ) Как можно "нарисовать" параллелепипед, если известны координаты его центра, длина, ширина и высота? Здесь разница в алгоритмах построения не более, очевидно, что сферу "построить" легче. ColR_iT Изменено 17 Января 2013 пользователем ColR_iT Ссылка на комментарий
ColR_iT 171 Опубликовано 17 Января 2013 Поделиться Опубликовано 17 Января 2013 Ребята, предлагаю прекратить полемику, сильно смахивающую на оффтоп, касательно данной темы и либо закончить, либо открыть соседнюю тему по данному вопросу. Ссылка на комментарий
Рекомендуемые сообщения
Создайте аккаунт или авторизуйтесь, чтобы оставить комментарий
Комментарии могут оставлять только зарегистрированные пользователи
Создать аккаунт
Зарегистрировать новый аккаунт в нашем сообществе. Это несложно!
Зарегистрировать новый аккаунтВойти
Есть аккаунт? Войти.
Войти