Я люблю Lua. I love Lua.

Upvalues и вопросы производительности

Posted in Uncategorized by ilovelua on Июль 23, 2013

Дано: список событий(массив таблиц).
Задача: с делать фильтр по полям событий.

Решение 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.
В общем, как обычно с оптимизацией — все нужно проверять.

Реклама

Вызов анонимной Lua функции из С++

Posted in Uncategorized by ilovelua on Июнь 20, 2011