Перейти к контенту

Рекомендуемые сообщения

Очень интересная реализация Lua OOP представлена в комплекте Lua for Windows (для тех у кого есть) по адресу ...\5.1\lua\loop

Там есть чему поучиться.

Изменено пользователем Gun12
  • Нравится 1
Ссылка на комментарий

Сравнил скорость работы реализации классов, которая здесь вчера обсуждалась:



--вариант luabind
class "TestClass"
function TestClass:__init()
self.num = 100
end

function TestClass:set(n)
self.num = n
end

function TestClass:get()
return self.num
end

--вариант прототипного ООП в луа без наследования
PsevdoClass = {}
function PsevdoClass:new()
obj = {}
setmetatable(obj, self)
self.__index = self
return obj
end

function PsevdoClass:initialize()
self.num = 100
end

function PsevdoClass:set(n)
self.num = n
end

function PsevdoClass:get()
return self.num
end

--вариант "без класса"
function s_set(_self, n)
_self.num = n
end

function s_get(_self)
return _self.num
end

function run()
local a = TestClass()
local timer1 = profile_timer()
timer1:start()
for i = 1, 1000000 do
a:set(i)
end
timer1:stop()
_m.logf('##TestClass set is ' .. timer1:time());

local b = {num = 100}
local timer2 = profile_timer()
timer2:start()
for i = 1, 1000000 do
test.s_set(b,i)
end
timer2: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 do
c:set(i)
end
timer5:stop()
_m.logf('##PsevdoClass set is ' .. timer5:time());

local timer3 = profile_timer()
timer3:start()
for i = 1, 1000000 do
local n = a:get()
end
timer3:stop()
_m.logf('##TestClass get is ' .. timer3:time());


local timer4 = profile_timer()
timer4:start()
for i = 1, 1000000 do
test.s_get(B)
end
timer4:stop()
_m.logf('##not class get is ' .. timer4:time());

local timer6 = profile_timer()
timer6:start()
for i = 1, 1000000 do
c:get()
end
timer6: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, спасибо, за то что так вот внезапно натолкнул меня на такой "полезный" вариант реализации ООП в луа.

Изменено пользователем Shredder
Ссылка на комментарий

Это не то, чтобы "полезный" вариант, это единственный вариант реализации ООП в Lua.

Что касается сабжа - неужто те, классы, которые реализуют различные менеджеры, так "тормозят"? Время их работы никчёмно, и разница в десять раз на одном миллионе итераций смотрится просто жалко, к тому же класс profile_timer считает время МИКРОсекундах, получается приблизительно полсекунды на весь процесс - это также не делает из этого бесполезной тратой времени и ресурсов. ИМХО, овчинка выделки не стоит.

Ссылка на комментарий

Shredder,

Конечно, значительного прироста производительности это не даст, но зачем платить лишнее, если можно по-дешёвке взять.

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

 

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий

Чтобы изжить, то что ты перечислил и больше, нужно много времени, просматривать код, искать, анализировать, исправлять. Здесь же исправлений очень мало. Создать функцию, которая будет создавать такой класс, скажем NewClass в _g.script. И останется заменить только class "BlaBlaBla" на BlaBlaBla = NewClass() и соответственно конструкторы BlaBlaBla() на BlaBlaBla:new(). Это можно делать время от времени, в поисках того, что ты перечислил.

Ты прав конечно, это капля в море, но она не требует особых затрат. ИМХО.

Ссылка на комментарий

Раз поднялась тема про оптимизацию... Я давно думал об этом, но руки не дошли, да и все не упомнишь. Может есть желание у кого нибудь составить список проблемных мест в скриптах? Именно по файлам. В одну каску это почти нереально - прошелестить все скрипты, но вдруг уже знают глючные модули.

ТЧ 1.0004. SAP и Trans mod

github

Ссылка на комментарий

Даже если и возникнет желание, давайте его обсуждать не здесь, создайте по соседству тему, кто желает, будем обсуждать там, а то мы и так здесь нафилософствовали...

Далее, пожалуйста, по существу: конкретный вопрос - конкретный ответ.

Ссылка на комментарий

Покумекал тут над "классами". Написал функцию на замену луабинд. Кому-то может будет полезно. Кто-то может укажет на недостатки или, не дай Бог, ошибки:


function NewClass(ParentClass)
local klass = {}
if ParentClass then
setmetatable(klass, ParentClass)
klass.__index = klass
end
function klass:new(...)
obj = {}
setmetatable(obj, self)
self.__index = self
obj:__init(...)
return obj
end

if not ParentClass then
function klass:__init(...)
end
end
return klass
end

 


--объявляем новый "класс"
PsevdoClass = NewClass()
--переопределяем конструктор
function PsevdoClass:__init()
self.num = 100
end
--добавляем нужные методы
function PsevdoClass:set(n)
self.num = n
end
function PsevdoClass:get()
return self.num
end

--объявляем "класс-наследник"
SubClass = NewClass(PsevdoClass)
--переопределяем метод set
function PsevdoClass:set(n)
self.num = n * 100
end

--создаём "объекты"
a = PsevdoClass:new()
b =SubClass:new()
a:set(50)
b:set(50)
print(a:get()) --вернёт 50
print(b:get()) --вернёт 5000

 

Для меня в этой реализации весомым недостатком является отсутствие возможности вызывать перекрываемые методы внутри тех, которые идут им на замену. Но с другой стороны, я слабо себе представляю, где может понадобится наследование. В исходных кодах сталкера наследование не используется, только в случае экспортированных классов. Всё, на этом тему классов в lua больше не затрону.

хотелось бы ещё сказать по поводу "проблемных мест". Всё-таки "косяков", существенно снижающих производительность, в кодах сталкера нет, по крайней мере я на такие места не натыкался. Тогда о чём там писать? Небольшой пример, так сказать, одно из недоразумений, которое я нашёл, копаясь в кодах ЗП:

это файл работ главного смарта на Затоне (Скадовск). Там много логик анимпоинтов, практически одинаковых. Одна из них:


[logic@zat_a2_animp_18]
active = animpoint@zat_a2_animp_18
suitable = {=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)} true
prior = 90

Её можно упростить:


[logic@zat_a2_animp_18]
active = animpoint@zat_a2_animp_18
suitable = {=check_npc_name(sim_default:stalker_raider:zat_b14_stalker_:artefact_hunter) =npc_in_zone(zat_a2_sr_noweap)} true, false
prior = 90

Таких логик 22. Так к чему я это. Понятно, что такоё упрощение "капля в море", всязи с этим вопрос, стоит ли для таких вот "нанооптимизаций" создавать тему и там делиться найдеными "проблемными местами". Может по капле на лужу наскребём, а то и на речушку? :) С другой стороны, для новичков будет справочник "Так делать не надо"

 

 

 

 

  • Нравится 1
Ссылка на комментарий

Shredder, а вот с оптимизацией примера логики проколочка... Это только затормозит работу. :)

Причина проста - выборка из таблицы с одним элементом происходит быстрее, чем с несколькими. Опять же таки разница во времени ничтожна, поэтому это скорее не на оптимизацию похоже, а на приведение в божеский вид...

Хотя где-где, а в логике есть что оптимизировать, например если взглянуть на функцию cfg_get_switch_conditions в файле xr_logic.script, то можно прийти к не до умению с вопросом: почему именно так?

Ссылка на комментарий

ColR_iT, эта функция обычно вызывается один раз при загрузке логики. Нет особенного смысла оптимизировать такого рода код. Всякие фризы при загрузке по-любому вызываются не скриптами, а данными: звуками, моделями, текстурами. Скрипты оптимизировать если и имеет смысл, то там, где они вызываются часто, на каждом апдейте, внутри длинного цикла и т.п. и при этом действительно что-то делают: оперируют со строками к примеру.

 

В качестве некоторого оффтопа.

Я как-то провёл эксперимент. Удалил с уровня все объекты, очистил биндер актора, короче, убрал почти всю нагрузку, так или иначе связанную со скриптами или вызываемую скриптами. В итоге, fps конечно подрос, но не сказать чтобы прямо драматически. На динамике и в зависимости от настроек графики на большом уровне типа Кордона fps вырастал процентов на 20-30, а то и меньше. Основная нагрузка, получается, приходится на рендер. И это я ведь вообще всё убрал, а на самом деле вряд ли можно ожидать, что всякими оптимизациями можно убрать хотя бы 5-10% от скриптовой нагрузки. В итоге, условно говоря, можно ожидать от самой хардкорной оптимизации ну скажем 3-5% итогового выигрыша в fps.

Разумеется, не всё так линейно и просто. Иногда оптимизировать можно и нужно, но в целом, как видно, оптимизация ради самого процесса оптимизации ничего не даёт, кроме потери времени. Если на самом деле выловили большую часть грубых алгоритмических косяков, то в чём тогда проблема то? Пользуйтесь на здоровье и не парьтесь.

 

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий

Я не понял, почему затормозит работу?

Если верить описанию функции 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. Или они всегда онлайн? Кроме того, судя по логикам некоторых рестрикторов, там происходит обработка квестов. Ещё полно смарт_каверов. Вот как считаете, если убрать логику в скрипты и удалить рестрикторы, это может поднять производительность?

Ссылка на комментарий

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

1. NewClass.new

2. ...

function NewClass:Get()

return self.new

end

 

соответственно и присваивание в таком же духе

 

ЗЫ это так, ради интереса и ИМХО первый вариант менее затратный, верно?

Изменено пользователем Viнt@rь
Ссылка на комментарий

Shredder,

в ТЧ на баре онлайн только около 50 рестрикторов, в ЗП 260.

...

если убрать логику в скрипты и удалить рестрикторы, это может поднять производительность?

Это количество само по себе ни о чём не говорит. Во-первых, не все рестрикторы имеют логику. Многие, если не большинство, работают по своему основному назначению, рестрикторами т.е., и никакой скриптовой нагрузки не создают. Это, к примеру, все рестрикторы костров. Во-вторых, не такую уж и нагрузку создают эти рестрикторы, даже если и с логикой. Сама логика может быть большую часть времени с неактивной секцией. Если и активная, то там максимум стоит периодическая проверка на попадание какого-то непися или актора в этот рестриктор. Это достаточно малозатратная операция (к слову сказать, рестрикторы со сферическим шейпом в этом смысле намного предпочтительней боксов). Опять же, делается это с пониженной частотой, в ТЧ - всего 5 раз в секунду.

Т.е. не так уж всё плохо в первом приближении. Конечно, надо предметно измерять, тогда и можно будет сказать точно. В ЗП я этого не измерял, поэтому с гарантией не скажу.

 

Плагины Total Commander для работы с игровыми архивами:

Архиваторный плагин (для работы с одиночным архивом): link1 link2

Системный плагин (для распаковки установленной игры): link1 link2

 

Ссылка на комментарий

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

Смысл второго варианта, если он вызывает первый вариант внутри себя, для чего такая обёртка?

Ссылка на комментарий

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

 

Ссылка на комментарий

Вот именно, что в оригинале мы будем иметь 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, то и того лучше.

Изменено пользователем Shredder
Ссылка на комментарий

к слову сказать, рестрикторы со сферическим шейпом в этом смысле намного предпочтительней боксов

Интересно узнать, почему? Я все время думал наоборот, исходя из логики: объект бокса имеет меньше полигонов (которые нужно проверять) чем сфера.

Ссылка на комментарий

Clayman, какие полигоны в абстрактной сфере? Это всего лишь точка, вокруг которой программно описывается сфера с указанным радиусом, визуального отображения нет, т.е. модель отсутствует.

Изменено пользователем ColR_iT
Ссылка на комментарий

Теперь понятно, а бокс как программно проверяется? Ну просто интересно, раз-уж поднял тему :))

 

Как можно "нарисовать" параллелепипед, если известны координаты его центра, длина, ширина и высота?

Здесь разница в алгоритмах построения не более, очевидно, что сферу "построить" легче.

ColR_iT

Изменено пользователем ColR_iT
Ссылка на комментарий

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

Ссылка на комментарий

Создайте аккаунт или авторизуйтесь, чтобы оставить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

Создать аккаунт

Зарегистрировать новый аккаунт в нашем сообществе. Это несложно!

Зарегистрировать новый аккаунт

Войти

Есть аккаунт? Войти.

Войти
  • Недавно просматривали   0 пользователей

    • Ни один зарегистрированный пользователь не просматривает эту страницу.
×
×
  • Создать...