Optimisation Discussions Advises Experiences Ideas Etc

Discussion of all aspects of the game engine, including development of new and existing features.

Moderator: Forum Moderators

enclave
Posts: 936
Joined: December 15th, 2007, 8:52 am

Re: Optimisation Discussions Advises Experiences Ideas Etc

Post by enclave »

gfgtdf wrote:Hmm this does suprise me a little. Of course [store_locations] interally uses wesnoth.get_locations so it cannot be faster than that. And [store_locations] also have to store the locations and the terrain codes in wml vaiables. But i still expected this difference to be neglectable, this test though, shows that it takes nearly twice as long.
well maybe i done mistake somewhere, you may try to test it on your machine.. plus it might be different on different version of wesnoth and different operating system.. I assume. Also maybe if u own a latest processor the results will show something like "1,1,1,1" for all these tests :D so the difference would be neglectable... ;)

At the moment I stuck with my tests a little bit.. maybe..
My code should be:

Code: Select all

wesnoth.fire("store_locations",
   { x=xbegin-xend, y=ybegin-yend, { "not", { terrain="_off^_usr" }}, variable="ns_cube"})
	wesnoth.fire("store_locations",
   { find_in="ns_cube", terrain="M*,M*^*", variable="ns_mountains"})
And I need to convert it into wesnoth.get_locations and the problem is that if I do
local loc_storage1 = wesnoth.get_locations { x=xbegin-xend, y=ybegin-yend, { "not", { terrain="_off^_usr" }} }
I can not do local loc_storage2 = wesnoth.get_locations { find_in="loc_storage2", terrain="M*,M*^*"}
Any simple ideas? Or the only way to count how many mountains was inside the loc_storage1 is to use for i,... if loc_storage1[i].terrain == Mm..... of course I have no idea how to extract elements from the table.. I will read a lot of helps.. but not sure if it's there.. might need to google lua programming language in internet to find out.. unless somebody could help? How to count amount of mountains, including impassable imountains, snow mountains etc.. (M*,M*^*) from the lua table which has many different terrains?
I realized I could work around it and shorten the top 2 rows into 1, so instead of

Code: Select all

wesnoth.fire("store_locations",
   { x=xbegin-xend, y=ybegin-yend, { "not", { terrain="_off^_usr" }}, variable="ns_cube"})
	wesnoth.fire("store_locations",
   { find_in="ns_cube", terrain="M*,M*^*", variable="ns_mountains"})
it could use just

Code: Select all

wesnoth.get_locations { x=xbegin-xend, y=ybegin-yend, { "not", { terrain="_off^_usr" }}, terrain="M*,M*^*"}
not sure why in WML i used 2 lines.. maybe because the second line was part of MACRO that was one of 3... in one of them I was counting how many mountains, in the other hills, in another forest... doesn't change the fact that I don't know how to work with lua tables..
User avatar
Pentarctagon
Project Manager
Posts: 5526
Joined: March 22nd, 2009, 10:50 pm
Location: Earth (occasionally)

Re: Optimisation Discussions Advises Experiences Ideas Etc

Post by Pentarctagon »

If you're running into problems, the Lua Labs or WML Workshop(depending on the problem) would be a better place to ask than this thread.
99 little bugs in the code, 99 little bugs
take one down, patch it around
-2,147,483,648 little bugs in the code
enclave
Posts: 936
Joined: December 15th, 2007, 8:52 am

Re: Optimisation Discussions Advises Experiences Ideas Etc

Post by enclave »

Well I converted whole WML into LUA, without using wesnoth.fire and there is no speed gain.. they seem to perform the same despite lua cycles usually 20 times faster.. and wesnoth.get_locations is at least 2 times faster.. here are 3 cycles while and 2 wesnoth.get_locations and the code takes LONG to perform.. on a map like conquest- Jel'wan
So the only untested part and I assume the bottleneck is this code:

Code: Select all

	local NS_HIT_X = helper.rand(xbegin..".."..xend)
	local NS_HIT_Y = helper.rand(ybegin..".."..yend)
vs

Code: Select all

{VARIABLE_OP NS_HIT_X rand $xbegin..$xend}
{VARIABLE_OP NS_HIT_Y rand $ybegin..$yend}
plus i found some other issues in code.. and I will have a new question in my lua topic..
PS. corrected some issues and now it looks like lua actually does have a speed gain.. speed gain depends on conditions.. So there is a tiny tiny gain if part of the code with random generation is used, and a massive huge gain if it is not used.. also in some circumstances it is still slower than wml.. and i dont understand why. tired for today, everything was buggy as a nightmare
Last edited by enclave on June 23rd, 2017, 2:13 am, edited 1 time in total.
gfgtdf
Developer
Posts: 1432
Joined: February 10th, 2013, 2:25 pm

Re: Optimisation Discussions Advises Experiences Ideas Etc

Post by gfgtdf »

In wesnoth 1.12 helper.rand is indeed expected to be slightly slower than the wml counterpart ([set_variable] rand=). This has changed in wesnoth 1.13 where wesnoth.random was added and the wesnoth.set_vaiable implementation was moved to lua.
Scenario with Robots SP scenario (1.11/1.12), allows you to build your units with components, PYR No preperation turn 1.12 mp-mod that allows you to select your units immideately after the game begins.
User avatar
Pentarctagon
Project Manager
Posts: 5526
Joined: March 22nd, 2009, 10:50 pm
Location: Earth (occasionally)

Re: Optimisation Discussions Advises Experiences Ideas Etc

Post by Pentarctagon »

I'd also recommend putting all the code for your tests in your first post as well, so people can see it without needing to scroll through the entire thread. Tests that have a significant difference in performance between the implementations(macro vs fire_event, WML vs lua, etc) might also make decent unit tests.
99 little bugs in the code, 99 little bugs
take one down, patch it around
-2,147,483,648 little bugs in the code
enclave
Posts: 936
Joined: December 15th, 2007, 8:52 am

Re: Optimisation Discussions Advises Experiences Ideas Etc

Post by enclave »

Testing helper.rand vs math.random vs math.random wesnoth.synchronize_choice vs wesnoth.fire("set_variable", {rand...}) with a rule: to save result as wml variable.
Please have a look at codes, I could make mistakes.. specially in synchronize_choice.

Code: Select all

#define RAND1
[lua]
code = <<
	for j=0,65000,1 do
		local NS_HIT_X = helper.rand("10..100")
		wesnoth.set_variable("wml_rand",NS_HIT_X)
	end
>>
[/lua]
#enddef

#define RAND2
[lua]
code = <<
	for j=0,65000,1 do
		local NS_HIT_X = math.random(10,100)
		wesnoth.set_variable("wml_rand",NS_HIT_X)
	end
>>
[/lua]
#enddef

#define RAND3
[lua]
code = <<
	for j=0,65000,1 do
		local NS_HIT_X = math.random(10,100)
		wesnoth.set_variable("wml_rand",wesnoth.synchronize_choice(function() return { value = NS_HIT_X } end).value)
	end
>>
[/lua]
#enddef

#define RAND4
[lua]
code = <<
	for j=0,65000,1 do
	wesnoth.fire("set_variable", {name="wml_rand",rand="10..100"})
	end
>>
[/lua]
#enddef
helper.rand 1908,1922,1914,1964
math.random 150,181,177,170
math.random wesnoth.synchronize_choice 1077,1066,1053,1044
wesnoth.fire("set_variable", {rand...}) 1560,1556,1580,1579

very interesting.. I guess math random has a disadvantage of inability to select random from non-number.. can't pick a string from list of strings.. etc and is faster.. even if synchronized after.. (if i used synchronization code correctly)

So we have some conclusion thoughts.. 1) do not use helper.rand if you are on wesnoth 1.12 and if you need fast speed, where you cycle through random generated numbers.. 2) better use wesnoth.fire instead.. because WML [set_variable] rand is quicker than lua helper.rand (according to my previous tests wesnoth.fire is same speed as WML corresponsing tag) 3) If you don't need synchronization for some reason (and only dealing with numbers).. for example using random generation for animation, use math.random its 10 times faster than any alternative. 4) and if you need synchronization and not afraid to mess with synchronization and need speed, use math.random, then synchronize the result.. you will still win some 50% speed, am i right?

Now testing math.random wesnoth.set_variable 162,164,169,159
vs math.random local 40,54,45,48

So we get some more speed if we don't save it as WML variable (looks like saving variable consumes 120 units of speed in 65k loop), so helper.rand local could be 120 units speed faster than helper.rand wesnoth.set_variable if you don't need to save it as WML.. but it will still be quite significantly slower than wesnoth.fire("set_variable", {rand...}) variable..
not sure how helper.rand local would be compared vs wesnoth.fire("set_variable", {rand...}) wesnoth.get_variable, assume helper would still be slower a bit..
enclave
Posts: 936
Joined: December 15th, 2007, 8:52 am

Re: Optimisation Discussions Advises Experiences Ideas Etc

Post by enclave »

Well I finally found what was slowing lua code down...
And I don't understand why...

Code: Select all

		local ns_check_if_wrong_terrain = #wesnoth.get_locations  { x=NS_HIT_X, y=NS_HIT_Y, { "not", { terrain="M*,W*,M*^*,W*^*,Ai*,Ai*^*,Rrc,Q*,X*,Q*^*,X*^*,Iwr,Rp^Ecf,Rp^Dr,_off^_usr"}}}
		if ns_check_if_wrong_terrain>0 then
			wesnoth.set_terrain(NS_HIT_X, NS_HIT_Y, "Mm")
		end
This lua code above is much MUCH much MUCH slower than WML version of it:

Code: Select all

[store_locations]
   x,y=$NS_HIT_X,$NS_HIT_Y
      [not]
         terrain=M*,W*,M*^*,W*^*,Ai*,Ai*^*,Rrc,Q*,X*,Q*^*,X*^*,Iwr,Rp^Ecf,Rp^Dr,_off^_usr
      [/not]
   variable=ns_check_if_wrong_terrain
[/store_locations]
   [if]
      [variable]
         name=ns_check_if_wrong_terrain.length
         greater_than=0
      [/variable]
   [then]
      {MODIFY_TERRAIN Mm $NS_HIT_X $NS_HIT_Y}
   [/then]
   [/if]
Tested on Jel'wan (conquest minus add-on) map. In a small cycle (lua is 400 speed units faster WITHOUT this code, and 600 speed units slower WITH this code). BUT if map is covered with Gg everywhere lua code is faster again. On 2p Arcanclave Citadel (default era map) lua code is faster with or without this code.. So something makes it very slow on complicated maps like Jel'wan!
Any ideas? (I will post 65k loop of both codes in a minute...)

Test results without a loop... performed 1 time on a single cell.. filters are slow..

Code: Select all

#define LOCS1
[lua]
code = <<
local x1 = 10
local y1 = 10
	local ns_check_if_wrong_terrain = #wesnoth.get_locations  { x=x1, y=y1, { "not", { terrain="M*,W*,M*^*,W*^*,Ai*,Ai*^*,Rrc,Q*,X*,Q*^*,X*^*,Iwr,Rp^Ecf,Rp^Dr,_off^_usr"}}}
>>
[/lua]
#enddef

#define LOCS2
[lua]
code = <<
local x1 = 10
local y1 = 10
	local ns_check_if_wrong_terrain = #wesnoth.get_locations  { x=x1, y=y1, { "not", { terrain="M*,W*,M*^*,W*^*,Ai*,Ai*^*,Rrc,Q*,X*,Q*^*,X*^*,Iwr,Rp^Ecf,Rp^Dr,_off^_usr"}}}
		wesnoth.set_variable("ns_check_if_wrong_terrain", ns_check_if_wrong_terrain)
>>
[/lua]
#enddef

#define LOCS3
[lua]
code = <<
local x1 = 10
local y1 = 10
wesnoth.fire("store_locations",
   { x=x1, y=y1, { "not", { terrain="M*,W*,M*^*,W*^*,Ai*,Ai*^*,Rrc,Q*,X*,Q*^*,X*^*,Iwr,Rp^Ecf,Rp^Dr,_off^_usr"}},  variable="ns_check_if_wrong_terrain"})
>>
[/lua]
#enddef

#define LOCS4
[store_locations]
x,y=10,10
[not]
terrain=M*,W*,M*^*,W*^*,Ai*,Ai*^*,Rrc,Q*,X*,Q*^*,X*^*,Iwr,Rp^Ecf,Rp^Dr,_off^_usr
[/not]
variable=ns_check_if_wrong_terrain
[/store_locations]
#enddef
10.10 is Gs^Ft (forested grass)
{LOCS1} 44,42,36,40wesnoth.get_locations
{LOCS2} 40,46,36,48wesnoth.get_locations set_variable
{LOCS3} 49,39,39,78wesnoth.fire("store_locations"..)
{LOCS4} 36,44,40,38[store_locations]

10.10 is Gg (plain grassland)
{LOCS1} 36,45,43,45wesnoth.get_locations
{LOCS2} 38,46,35,41wesnoth.get_locations set_variable
{LOCS3} 39,42,23,42wesnoth.fire("store_locations"..)
{LOCS4} 46,45,29,33[store_locations]

Other complicated terrains like impassable or villages shown same speeds as forested grass test.

So now I will compare the wesnoth.get_locations without set_variable in a 100 times loop vs wesnoth.fire("store_locations"..), 65k loop just never seem to end..

Code: Select all

local ns_check_if_wrong_terrain = wesnoth.get_locations  { x=x1, y=y1, { "not", { terrain="M*,W*,M*^*,W*^*,Ai*,Ai*^*,Rrc,Q*,X*,Q*^*,X*^*,Iwr,Rp^Ecf,Rp^Dr,_off^_usr"}}}
vs

Code: Select all

wesnoth.fire("store_locations",
   { x=x1, y=y1, { "not", { terrain="M*,W*,M*^*,W*^*,Ai*,Ai*^*,Rrc,Q*,X*,Q*^*,X*^*,Iwr,Rp^Ecf,Rp^Dr,_off^_usr"}},  variable="ns_check_if_wrong_terrain"})
10.10 is Gs^Ft (forested grass) 100 times loop
{LOCS1} 2019,2080,2031 wesnoth.get_locations
{LOCS3} 2035,2036,2045 wesnoth.fire("store_locations"..)
{LOCS4} 2023,2027,2032 [store_locations] via {REPEAT}

10.10 is Gg (plain grassland) 100 times loop (normal Jel'wan map as it comes)
{LOCS1} 2028,2036,2071 wesnoth.get_locations
{LOCS3} 2027,2066,2075 wesnoth.fire("store_locations"..)
{LOCS4} 2032,2015,2036 [store_locations] via {REPEAT}

And now a stupid thing :) Whole map is covered with Gg and we are doing same 100 times loop test would color it into red if was allowed because it needs attention...
10.10 is Gg (plain grassland) 100 times loop (whole map is only Gg)
{LOCS1} 632,628,628wesnoth.get_locations
{LOCS3} 614,624,622wesnoth.fire("store_locations"..)
{LOCS4} 610,615,627[store_locations] via {REPEAT}

So.. idk.. no comments. Just look the bottom 2 tests where the only difference was the whole map was Gg and in the other one it wasnt.. ?????
enclave
Posts: 936
Joined: December 15th, 2007, 8:52 am

Re: Optimisation Discussions Advises Experiences Ideas Etc

Post by enclave »

Very very very strange behaviour... when testing wesnoth.get_locations 1v1 against [store_locations] it shows same speed.. but when used together with "other lua" it becomes slower.. I don't understand it!!!!! And "other lua" is much faster than "other wml" when I remove "wesnoth.get_locations" from it. And wesnoth.fire("store_locations"..) also becomes slower for some reason. I see no point to try to optimise WML using lua anymore, at least when dealing with terrains.

Example is this code, try it on map like "Jel'wan" or "Valeria", can be found in New Setters add-on.

Lua version, slower than WML: (shortcut with: {LUA_SETTLERS_FILL_MAP_WITH_MISSING_TERRAINS 8 1 0 1 0 1 1} )

Code: Select all

#define LUA_SETTLERS_FILL_MAP_WITH_MISSING_TERRAINS SIZE COMPARISON1 REPEAT_FOREST COMPARISON2 REPEAT_HILLS COMPARISON3 REPEAT_MOUNTAINS
{VARIABLE ns_size {SIZE}}
{VARIABLE ns_comparison1 {COMPARISON1}}
{VARIABLE ns_comparison2 {COMPARISON2}}
{VARIABLE ns_comparison3 {COMPARISON3}}
{VARIABLE ns_repeat_forest {REPEAT_FOREST}}
{VARIABLE ns_repeat_hills {REPEAT_HILLS}}
{VARIABLE ns_repeat_mountains {REPEAT_MOUNTAINS}}
[lua]
code = <<
local comparison1 wesnoth.get_variable("ns_comparison1")
local comparison2 wesnoth.get_variable("ns_comparison2")
local comparison3 wesnoth.get_variable("ns_comparison3")
local w,h,b = wesnoth.get_map_size()
local ns_size = wesnoth.get_variable("ns_size")
local ns_repeat_mountains = wesnoth.get_variable("ns_repeat_mountains")

local ybegin = 1
local yend = ns_size

    while (ybegin<=h)
	do
			local xbegin = 1
			local xend = ns_size
			while (xbegin<=w)
			do
local ns_mountains = #wesnoth.get_locations  { x=tostring(xbegin).."-"..tostring(xend), y=tostring(ybegin).."-"..tostring(yend), terrain="M*,M*^*"}
 if ns_mountains<tonumber(wesnoth.get_variable("ns_comparison3")) then
	for j=0,tonumber(ns_repeat_mountains),1 do
	  local NS_HIT_X = math.random(xbegin,xend)
      NS_HIT_X = wesnoth.synchronize_choice(function() return { value = NS_HIT_X } end).value
	  local NS_HIT_Y = math.random(ybegin,yend)
      NS_HIT_Y = wesnoth.synchronize_choice(function() return { value = NS_HIT_Y } end).value
	  	
		local ns_check_if_wrong_terrain = #wesnoth.get_locations  { x=NS_HIT_X, y=NS_HIT_Y, { "not", { terrain="M*,W*,M*^*,W*^*,Ai*,Ai*^*,Rrc,Q*,X*,Q*^*,X*^*,Iwr,Rp^Ecf,Rp^Dr,_off^_usr"}}}
		if ns_check_if_wrong_terrain>0 then
			wesnoth.set_terrain(NS_HIT_X, NS_HIT_Y, "Mm")
		end
	end
 end	
			xbegin = xbegin + ns_size
			xend = xend + ns_size
			 if xend > w then 
				xend = w
			 end
			end

	ybegin = ybegin + ns_size
	yend = yend + ns_size
	 if yend > h then 
		yend = h
	 end
    end
>>
[/lua]
{CLEAR_VARIABLE ns_size}
{CLEAR_VARIABLE ns_comparison1}
{CLEAR_VARIABLE ns_comparison2}
{CLEAR_VARIABLE ns_comparison3}
{CLEAR_VARIABLE ns_repeat_forest}
{CLEAR_VARIABLE ns_repeat_hills}
{CLEAR_VARIABLE ns_repeat_mountains}
#enddef
WML version, faster than LUA: (shorcut with: {WML_SETTLERS_FILL_MAP_WITH_MISSING_TERRAINS 8 (less_than=1) 0 (less_than=1) 0 (less_than=1) 1})

Code: Select all

#define WML_SETTLERS_FILL_MAP_WITH_MISSING_TERRAINS SIZE COMPARISON1 REPEAT_FOREST COMPARISON2 REPEAT_HILLS COMPARISON3 REPEAT_MOUNTAINS
[store_map_dimensions]
variable=ns_map_size
[/store_map_dimensions]
##
{VARIABLE ybegin 1}
{VARIABLE yend {SIZE}}
##
[while]
[variable]
name=ybegin
less_than_equal_to=$ns_map_size.height
[/variable]
[do]
	{VARIABLE xbegin 1}
	{VARIABLE xend {SIZE}}
	##
	[while]
	[variable]
	name=xbegin
	less_than_equal_to=$ns_map_size.width
	[/variable]
	[do]
	##
	[store_locations]
	x=$xbegin-$xend
	y=$ybegin-$yend
	[not]
	terrain=_off^_usr
	[/not]
	variable=ns_cube
	[/store_locations]
	##
	{FIND_IF_ANY_MOUNTAINS_ADD_MOUNTAINS {COMPARISON3} {REPEAT_MOUNTAINS}}
	##
	{VARIABLE_OP xbegin add {SIZE}}
	{VARIABLE_OP xend add {SIZE}}
	[/do]
	[/while]
{VARIABLE_OP ybegin add {SIZE}}
{VARIABLE_OP yend add {SIZE}}
[/do]
[/while]
##
{CLEAR_VARIABLE ns_cube}
{CLEAR_VARIABLE xend}
{CLEAR_VARIABLE xbegin}
{CLEAR_VARIABLE ybegin}
{CLEAR_VARIABLE yend}
#enddef



#define FIND_IF_ANY_MOUNTAINS_ADD_MOUNTAINS COMPARISON REPEATS
[store_locations]
find_in=ns_cube
terrain=M*,M*^*
variable=ns_mountains
[/store_locations]
##
[if]
[variable]
name=ns_mountains.length
{COMPARISON}
[/variable]
[then]
{REPEAT {REPEATS} (
{VARIABLE_OP NS_HIT_X rand $xbegin..$xend}
{VARIABLE_OP NS_HIT_Y rand $ybegin..$yend}
########################
[store_locations]
x,y=$NS_HIT_X,$NS_HIT_Y
[not]
terrain=M*,W*,M*^*,W*^*,Ai*,Ai*^*,Rrc,Q*,X*,Q*^*,X*^*,Iwr,Rp^Ecf,Rp^Dr,_off^_usr
[/not]
variable=ns_check_if_wrong_terrain
[/store_locations]
[if]
[variable]
name=ns_check_if_wrong_terrain.length
greater_than=0
[/variable]
[then]
########################
{MODIFY_TERRAIN Mm $NS_HIT_X $NS_HIT_Y}
########################
[/then]
[/if]
{CLEAR_VARIABLE ns_check_if_wrong_terrain}
########################
)}
[/then]
[/if]
##
{CLEAR_VARIABLE ns_mountains}
{CLEAR_VARIABLE NS_HIT_X}
{CLEAR_VARIABLE NS_HIT_Y}
#enddef
LUA speed on Jel'wan is: 2125
WML speed is: 1488
LUA speed without wesnoth.get_locations: 24
Spoiler:
WML speed without store_locations: 415
Spoiler:
it all just makes no sense.
PS. after looking at my WML code I shortened it and it became even faster, while lua remains crap - 2 times slower!!!!.
enclave
Posts: 936
Joined: December 15th, 2007, 8:52 am

Re: Optimisation Discussions Advises Experiences Ideas Etc

Post by enclave »

Ok, I found it.. :D
This is silly but..
I can't believe its trully working..!

I replaced:

Code: Select all

		local ns_check_if_wrong_terrain = #wesnoth.get_locations  { x=NS_HIT_X, y=NS_HIT_Y, { "not", { terrain="M*,W*,M*^*,W*^*,Ai*,Ai*^*,Rrc,Q*,X*,Q*^*,X*^*,Iwr,Rp^Ecf,Rp^Dr,_off^_usr"}}}
		if ns_check_if_wrong_terrain>0 then
			wesnoth.set_terrain(NS_HIT_X, NS_HIT_Y, "Mm")
		end
with

Code: Select all

local bool ns_check_if_wrong_terrain = wesnoth.match_location(NS_HIT_X, NS_HIT_Y, { terrain="M*,W*,M*^*,W*^*,Ai*,Ai*^*,Rrc,Q*,X*,Q*^*,X*^*,Iwr,Rp^Ecf,Rp^Dr,_off^_usr"})
		if ns_check_if_wrong_terrain == false then
			wesnoth.set_terrain(NS_HIT_X, NS_HIT_Y, "Mm")
		end
and instead of my fastest speed that I could achieve with WML: 1000
it does it on the fly, with speed of 14 !!!!!!!!!!! all you can do is blink and its done!

When you need to check the specific coordinate, DO NOT USE wesnoth.get_locations, use wesnoth.match_location or whatever else, but NOT wesnoth.get_locations!
enclave
Posts: 936
Joined: December 15th, 2007, 8:52 am

Re: Optimisation Discussions Advises Experiences Ideas Etc

Post by enclave »

Tried to do [fire_event] using {REPEAT} macro 2000 times and speed result was 200.. Which is not extremely bad.. It means that you may sometimes mix lua with fire_events.. but if you need huge speed boost I would not recommend fire_events firing more than 2000 times.. If you plan to make a fast code that calculates more than 2000 things via fire events then better find the other way, don't use fire_event.. use pure lua. Even 1000 fire events may significantly slow things down.

Just for comparison, lua can perform quite a big code with speed of 20.. VERY FAST. (it may include more than 2000 calculations of 1 line or longer)
1000 fire events of tiny TINY TINY code (like 1 line size) would be around 5 times slower, around speed of 100.. and I guess (not sure) that every 1000 speed is 1 second. 1 second is something you will most likely notice! (like a little lag or delay between actions..) I think that less than 200 would be unnoticed for human eye in most cases and around 500 would be acceptable. But keep in mind that every user has different computer.. if an AMD 6 core may show it as speed of 200.. some strong i7 may perform it with speed of 1, maybe... and some old dual core AMD or single core Pentium.. would give you a much bigger delay I assume.. Never tested this, only assuming, as I can not test it, or rather say don't want to test it now.. But will test it some day and put here for comparison.. It should be interesting.. how different devices or operating systems perform same calculations.
enclave
Posts: 936
Joined: December 15th, 2007, 8:52 am

Re: Optimisation Discussions Advises Experiences Ideas Etc

Post by enclave »

Optimization problems when converting macros into fire events..
All seems simple.. you have a lot of code.. you have macros inside macros.. you want to cut the savefile size or increase performance, you know what you are doing.. you done it a lot of times.. but!

From time to time.. your game turns into a mess!

It is known that when you fire event from some event.. then the fired event inherits the parent event variables...
For example:

Code: Select all

[event]
name=capture
first_time_only=no
{FIRE_EVENT message_which_side_lost_a_village} ## keep in mind that this macro {FIRE_EVENT} doesn't exist by default, you need to create it.
[/event]

[event]
name=message_which_side_lost_a_village
first_time_only=no
[message]
message="fire_event_1: side $owner_side lost a village"
[/message]
[/event]
This will work perfectly fine.. same as if you would just done it WITHOUT fire event like below:

Code: Select all

[event]
name=capture
first_time_only=no
[message]
message="without fire event: side $owner_side lost a village"
[/message]
[/event]
Yeah, nothing changes.. you could convert macros into fire event.. and use $owner_side inside your fired event.. same as inside macro.. COOL!

BUT if you write this:

Code: Select all

[event]
name=capture
first_time_only=no
[message]
message="without fire event: side $owner_side lost a village at $x1,$y1
[/message]
{FIRE_EVENT message_which_side_lost_a_village} 
[/event]

[event]
name=message_which_side_lost_a_village
first_time_only=no
[message]
message="fire_event_1: side $owner_side lost a village at $x1,$y1
[/message]
[/event]
What do you think you will get? CRAP.. lot of crap.. specially if your code is not only delivering a silly message..
The message without fire event will be saying "without fire event: side 2 lost a village at 13,14"
The message inside fired event will be saying "fire_event_1: side 2 lost a village at 0,0"

So fire events inherit something from parent event.. BUT some things, like $x1 $y1 get eaten.. and turned into ZEROES.
When you convert your code into fire events, make sure to replace all coordinates with your own variables!!!
For example this should work fine:

Code: Select all

[event]
name=capture
first_time_only=no
{VARIABLE xx $x1}
{VARIABLE yy $y1}
{FIRE_EVENT message_which_side_lost_a_village} 
[/event]

[event]
name=message_which_side_lost_a_village
first_time_only=no
[message]
message="fire_event_1: side $owner_side lost a village at $xx,$yy
[/message]
{CLEAR_VARIABLE xx}
{CLEAR_VARIABLE yy}
[/event]
But trust me, this may create a lot of headache if you ignore this.. Stderr.txt will not show any errors, owner_side will be working fine.. side_number will be working fine.. you will be wondering what went wrong.. and this is as simple as stupid as this.. $x1=0 $y1=0
So when you convert your macros into fire events, make sure to change $x1 and $y1, do NOT reply on them!!
Good luck! :)
User avatar
Sapient
Inactive Developer
Posts: 4453
Joined: November 26th, 2005, 7:41 am
Contact:

Re: Optimisation Discussions Advises Experiences Ideas Etc

Post by Sapient »

Try adding this to your FIRE_EVENT definition:

Code: Select all

[primary_unit]
    x,y=$x1,$y1
[/primary_unit]
http://www.wesnoth.org/wiki/User:Sapient... "Looks like your skills saved us again. Uh, well at least, they saved Soarin's apple pie."
enclave
Posts: 936
Joined: December 15th, 2007, 8:52 am

Re: Optimisation Discussions Advises Experiences Ideas Etc

Post by enclave »

Another test of WML against lua in speed comparison.
Storing locations and loops are the main subjects of comparison..


So codes below will be doing exactly the same thing and they are coded quite like a mirror, they will remove all leaders from map and then put them back on map with distance between them set according to settings, with only 1 rule that the leaders should appear on land, not water or impassable.

WML

Code: Select all

[event]
name=start
first_time_only=yes
{TT_BEGIN} ##timer begin
##############################
{VARIABLE ns_min_dist $NS_preload_options_shuffled_sides_minimal_distance}
##########################
						[if]
						[variable]
						name=ns_min_dist
						not_equals=0
						[/variable]
						[then]
[store_locations]
[not]
terrain=W*,W*^*,Xv,_off^_usr
[/not]
variable=ns_test_locations
[/store_locations]
	[if]
	[variable]
	name=ns_test_locations.length
	greater_than=$ns_min_dist
	[/variable]
	[then]
		[store_unit]
		kill=yes
		[filter]
		canrecruit=yes
		[/filter]
		variable=ns_all_kings_stored
		[/store_unit]
{VARIABLE ns_king_rand_start_counter 0}
{FOREACH ns_test_locations ntl_i}
[if]
[variable]
name=ns_king_rand_start_counter
less_than=$ns_all_kings_stored.length
[/variable]
[then]
	[store_locations]
	x,y=$ns_test_locations[$ntl_i].x,$ns_test_locations[$ntl_i].y
		[and]
	[not]
	[filter]
	canrecruit=yes
	[/filter]
	radius=$ns_min_dist
	[/not]
		[/and]
	variable=ns_test_inner_locations
	[/store_locations]
		[if]
		[variable]
		name=ns_test_inner_locations.length
		greater_than=0
		[/variable]
		[then]
			[unstore_unit]
			x,y=$ns_test_locations[$ntl_i].x,$ns_test_locations[$ntl_i].y
			variable=ns_all_kings_stored[$ns_king_rand_start_counter]
			animate=no
			[/unstore_unit]
			{VARIABLE_OP ns_king_rand_start_counter add 1}
			{CLEAR_VARIABLE ns_test_inner_locations}
		[/then]
		[else]
			{CLEAR_VARIABLE ns_test_inner_locations}
		[/else]
		[/if]
[/then]
[/if]
{NEXT ntl_i}
	[/then]
	[/if]
							[/then]
							[/if]
{CLEAR_VARIABLE ns_all_kings_stored}
{CLEAR_VARIABLE ns_test_locations}
{CLEAR_VARIABLE ns_test_inner_locations}
{CLEAR_VARIABLE ns_king_rand_start_counter}
{TT_END} ##timer end
[/event]
LUA

Code: Select all

[event]
name=start
first_time_only=yes
{TT_BEGIN} ##timer begin
[lua]
code = <<
local ns_min_dist = wesnoth.get_variable("NS_preload_options_shuffled_sides_minimal_distance")
if ns_min_dist ~= 0 then
	local ns_test_locations = wesnoth.get_locations { {"not", { terrain = "W*,W*^*,Xv,_off^_usr" } }}
	local w,h,b = wesnoth.get_map_size()
	if (w > ns_min_dist) or (h>ns_min_dist) then
		wesnoth.fire("store_unit",{kill="yes", {"filter", {canrecruit=true}},variable="ns_all_kings_stored"})
		local ns_king_rand_start_counter = 0
		for i, ns_pairs_locations in ipairs(ns_test_locations) do
			if ns_king_rand_start_counter<wesnoth.get_variable("ns_all_kings_stored.length") then
				local ns_test_inner_locations = wesnoth.match_location(ns_pairs_locations[1], ns_pairs_locations[2], { radius=ns_min_dist , { "filter", { canrecruit=true } } })
				if ns_test_inner_locations == false then
					wesnoth.fire("unstore_unit",{animate=no,x=ns_pairs_locations[1],y=ns_pairs_locations[2],variable="ns_all_kings_stored["..ns_king_rand_start_counter.."]"})
					ns_king_rand_start_counter = ns_king_rand_start_counter + 1
				end
			end
		end
	end
end
>>
[/lua]
{TT_END} ##timer end
[/event]
And the result speeds were:
WML - 17k (I assume it equals to 17 seconds) Really slow, waiting clock appears, gives a feeling that game is lagging and will crash or never unfreeze.
lua - 0.7k (less than a second then..) Really quick.. doesn't give you time to think about anything really..
So 20 times faster with lua..
User avatar
Ravana
Forum Moderator
Posts: 2949
Joined: January 29th, 2012, 12:49 am
Location: Estonia
Contact:

Re: Optimisation Discussions Advises Experiences Ideas Etc

Post by Ravana »

For the record, I consider the experiment of releasing preprocessed ageless success. My testing showed 3x faster loading, and player feedback has been positive.
enclave
Posts: 936
Joined: December 15th, 2007, 8:52 am

Re: Optimisation Discussions Advises Experiences Ideas Etc

Post by enclave »

Ravana wrote:For the record, I consider the experiment of releasing preprocessed ageless success. My testing showed 3x faster loading, and player feedback has been positive.
Hi Ravana, thats great!
Maybe you could tell in a little bit more details then what exactly the situation was before and what has changed now? 3x faster loading is a massive speed gain! How did you manage to achieve that?
(For those who doesn't know, we are talking about add-on called "ageless", it has a massive amount of custom units and images, therefore it has a size of more than 50MB, in other words it's HUGE! One of the biggest add-ons at least!)
Post Reply