Upvalues и вопросы производительности
Дано: список событий(массив таблиц).
Задача: с делать фильтр по полям событий.
Решение 1(прямое, как палка):
function getEventVisible(event, filter) if filter.initiatorId then if filter.initiatorId ~= event.initiatorMissionID then return false end end if filter.targetId then if filter.targetId ~= event.targetMissionID then return false end end if filter.objectId then if event.initiatorMissionID ~= filter.objectId and event.targetMissionID ~= filter.objectId then return false end end if filter.weaponId then if filter.weaponId ~= event.weapon then return false end end if filter.eventType then if filter.eventType ~= eventEventType then return false end end if filter.initiatorCoalition then if filter.initiatorCoalition ~= DebriefingMissionData.getUnitCoalitionName(event.initiatorMissionID) then return false end end if filter.targetCoalition then if filter.targetCoalition ~= DebriefingMissionData.getUnitCoalitionName(event.targetMissionID) then return false end end return true end
Тут мы видим, что некоторые поля используются несколько раз. Это срочно нужно оптимизировать:
function getEventVisible2(event, filter) local initiatorId = filter.initiatorId local targetId = filter.targetId local objectId = filter.objectId local weaponId = filter.weaponId local eventType = filter.eventType local initiatorCoalition = filter.initiatorCoalition local targetCoalition = filter.targetCoalition local getUnitCoalitionNameFunc = DebriefingMissionData.getUnitCoalitionName local eventInitiatorId = event.initiatorMissionID local eventTargetId = event.targetMissionID local eventWeaponId = event.weapon local eventEventType = event.type local eventInitiatorCoalition = getUnitCoalitionNameFunc(eventInitiatorId) local eventTargetCoalition = getUnitCoalitionNameFunc(eventTargetId) if initiatorId then if initiatorId ~= eventInitiatorId then return false end end if targetId then if targetId ~= eventTargetId then return false end end if objectId then if eventInitiatorId ~= objectId and eventTargetId ~= objectId then return false end end if weaponId then if weaponId ~= eventWeaponId then return false end end if eventType then if eventType ~= eventEventType then return false end end if initiatorCoalition then if initiatorCoalition ~= eventInitiatorCoalition then return false end end if targetCoalition then if targetCoalition ~= eventTargetCoalition then return false end end return true end
Тут можно было бы откинуться на кресле и сделать глоток крепкого виски… Но чу! Это что же такое получается? Для фильтра, который один для всех событий, значения полей каждый раз извлекаются из таблицы filter. Непорядок. А ну-ка, где тут у нас upvalues?
function getFilterFunc(filter) local initiatorId = filter.initiatorId local targetId = filter.targetId local objectId = filter.objectId local weaponId = filter.weaponId local eventType = filter.eventType local initiatorCoalition = filter.initiatorCoalition local targetCoalition = filter.targetCoalition local getUnitCoalitionNameFunc = DebriefingMissionData.getUnitCoalitionName return function(event) local eventInitiatorId = event.initiatorMissionID local eventTargetId = event.targetMissionID local eventWeaponId = event.weapon local eventEventType = event.type local eventInitiatorCoalition = getUnitCoalitionNameFunc(eventInitiatorId) local eventTargetCoalition = getUnitCoalitionNameFunc(eventTargetId) if initiatorId then if initiatorId ~= eventInitiatorId then return false end end if targetId then if targetId ~= eventTargetId then return false end end if objectId then if eventInitiatorId ~= objectId and eventTargetId ~= objectId then return false end end if weaponId then if weaponId ~= eventWeaponId then return false end end if eventType then if eventType ~= eventEventType then return false end end if initiatorCoalition then if initiatorCoalition ~= eventInitiatorCoalition then return false end end if targetCoalition then if targetCoalition ~= eventTargetCoalition then return false end end return true end end
Ну а теперь…(ТЕСТЫ!!! ТЕСТЫ!!!)
Я не вижу ваши руки!
local events = DebriefingEventsData.getEvents() local filterFunc = getFilterFunc(filter_) print('~~~event count:', #events) local t = os.clock() for i = 1, 1000 do local result = {} for i, event in ipairs(events) do if getEventVisible(event, filter_) then table.insert(result, event) end end end print('~~~getEventVisible', os.clock() - t) t = os.clock() for i = 1, 1000 do local result = {} for i, event in ipairs(events) do if getEventVisible2(event, filter_) then table.insert(result, event) end end end print('~~~getEventVisible2', os.clock() - t) t = os.clock() for i = 1, 1000 do local result = {} for i, event in ipairs(events) do if filterFunc(event) then table.insert(result, event) end end end print('~~~filterFunc', os.clock() - t)
Результаты:
~~~event count: 1467
~~~getEventVisible 1.32
~~~getEventVisible2 2.583
~~~filterFunc 2.286
~~~event count: 1467
~~~getEventVisible 0.698
~~~getEventVisible2 2
~~~filterFunc 1.706
~~~event count: 1467
~~~getEventVisible 1.326
~~~getEventVisible2 2.581
~~~filterFunc 2.284
~~~event count: 1467
~~~getEventVisible 1.642
~~~getEventVisible2 2.203
~~~filterFunc 1.984
~~~event count: 1467
~~~getEventVisible 1.506
~~~getEventVisible2 2.061
~~~filterFunc 1.822
~~~event count: 1467
~~~getEventVisible 1.338
~~~getEventVisible2 2.577
~~~filterFunc 2.275
~~~event count: 1467
~~~getEventVisible 0.75999999999999
~~~getEventVisible2 2.013
~~~filterFunc 1.712
~~~event count: 1467
~~~getEventVisible 1.322
~~~getEventVisible2 2.576
~~~filterFunc 2.281
~~~event count: 1467
~~~getEventVisible 1.323
~~~getEventVisible2 2.574
~~~filterFunc 2.288
~~~event count: 1467
~~~getEventVisible 1.687
~~~getEventVisible2 2.234
~~~filterFunc 1.999
~~~event count: 1467
~~~getEventVisible 1.717
~~~getEventVisible2 2.087
~~~filterFunc 1.808
~~~event count: 1467
~~~getEventVisible 1.743
~~~getEventVisible2 2.109
~~~filterFunc 1.83
Ой, что это? Наш прекрасный оптимизированный код работает хуже неоптимизированного? Похоже, что так. Причем местами «неоптимизированная» версия быстрее «оптимизированной» почти в 2 раза!
Думаю, что в getEventVisible2()
создается слишком много локальных переменных, причем некоторые из них могут не понадобиться. А что не так с filterFunc()
? Там вроде локальных переменных совсем немного? Похоже, что доступ к «замороженным» переменным не так уж и быстр, либо Lua при вызове этой функции должна откуда-то загрузить все upvalues.
В общем, как обычно с оптимизацией — все нужно проверять.
leave a comment