Hello. What is better to use ? I think what is faster or when should I use first and when should I use second option ?
orCode:foreach ArrayVariable(ArrayItem, Idx)
Thank you.Code:for(i=0;i<ArrayVariable.Length;i++)
Hello. What is better to use ? I think what is faster or when should I use first and when should I use second option ?
orCode:foreach ArrayVariable(ArrayItem, Idx)
Thank you.Code:for(i=0;i<ArrayVariable.Length;i++)
I think the general foreach iterator for dynamic arrays is to be preferred, especially if you need to iterate each item anyway and can omit the Idx variable. The second option is probably more suited if you need to operate on the counter yourself.
Our Loop, which art in source code, hallowed be thy keyword.
Thy condition come, thy instruction be done, in RAM as it is in cache.
Increment us this day our daily counter,
and forgive us our typos, as we also have forgiven our compilers.
And lead us not to the nullpointer but deliver us from bugs.
For thine is the API, the GUI, and the CLI while(true).
Semicolon;Please don't send me questions about how to do something in the UDK via PM. That is better discussed in the forums and we only have limited PM storage.
Only one way to find out.
Code:function Test() { local float f; Clock(f); // Run a test UnClock(f); `log("Test length:" @ f); }
Unreal Development Kit Game Programming with UnrealScript: Beginner’s Guide
Stubborn Horse Studios - Angel Mapper
To have said goodbye to things!
foreach should be faster simply because there are less elements for the VM to process. It also has a more elegant syntax imo.
But it really depends on the situation.
I'm betting on foreach too, simply because direct reference to ArrayItem will probably compile to something cleaner than somearray[i] in bytecode. So who has the engine handy right now and will do the profiling?![]()
UDK:
Angels Fall First
UT3:
Vehicle Set Replacement Mutator / XS Vehicles - UT3 edition / Angels Fall First
UT2004:
XS Vehicles / freeIK / Weapons
ScriptLog: Test for(): length 1213.6047Code:var array<int> TestArray; exec function TestFor() { local float f; local int i; Clock(f); for (i = 0; i < TestArray.Length; i++) { `log(TestArray[i]); } UnClock(f); `log("Test for(): length" @ f); } exec function TestForEach() { local float f; local int item; Clock(f); foreach TestArray(item) { `log(item); } UnClock(f); `log("Test foreach(): length" @ f); } defaultproperties { TestArray=(1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9) }
ScriptLog: Test foreach(): length 1013.0920
ScriptLog: Test for(): length 1030.6737
ScriptLog: Test foreach(): length 916.9355
ScriptLog: Test for(): length 581.6815
ScriptLog: Test foreach(): length 541.4322
Game restarted.
ScriptLog: Test foreach(): length 1018.8654
ScriptLog: Test for(): length 1029.2618
ScriptLog: Test foreach(): length 1020.3928
ScriptLog: Test for(): length 888.6221
ScriptLog: Test foreach(): length 540.8835
ScriptLog: Test for(): length 611.4195
@Jephir: Your TestArray in defaultproperties is impressive - by hand..?
Like Spoof said: ...it really depends on the situation.
BTW. You forgot about ZERO...
Oh. So many replies. Thank you. I didn't know about Clock(). It is very handy. I will test many things with this. So foreach is little faster and better if i want to iterate over all elements.
@Jephir: Your TestArray in defaultproperties is impressive - by hand..?![]()
+1. TestArray is really impressive.
Very, very interesting. We could use some further tests with increasing complexity of operations inside the loop but if it's really reliably a difference of ~10% then I should perhaps consider changing some of our heaviest per-tick loops.
UDK:
Angels Fall First
UT3:
Vehicle Set Replacement Mutator / XS Vehicles - UT3 edition / Angels Fall First
UT2004:
XS Vehicles / freeIK / Weapons
Only problem with those results is they are mostly timing the log function, which probably accounts for 99% of each loop. 1000ms is a long time for that amount of data... I was expecting something closer to 0.01![]()
for and foreach themselves would never be your performance bottleneck, feel free to use them. just as everyone said, it depends on situations.
for me, I prefer foreach, in most time it saves me a variable(to declare, variable fore-declare rule really sucks in UnrealScript).
Well, I have some interesting results for you. The raw speed of foreach is more than 10 times faster than a straight for loop.
I'll post my profiler later today, don't have time atm.
Indeed. I would remove those logs as logging is particularly slow and is fudging your results.
- Please do not send me questions regarding programming or implementing things in UDK via Private Message. I do not have time to respond and they are much better answered in the forums. -
Here's the profiler code. I make no apologies for the way this forum butchers my lovely tabbing
Code:class DTKProfiler extends Actor abstract; /*--------------------------------------------------------------------------*\ NOTES Important: Disable any turbo features of the cpu before profiling, or comparisons between competing tests will be grossly distorted. \*--------------------------------------------------------------------------*/ var int DefaultPasses; var float DefaultInterval; const StringPad = " "; struct ProfileResult { var int Passes; var float Total, Min, Max, Mean; var string Desc; }; /*--------------------------------------------------------------------------*\ Delegates \*--------------------------------------------------------------------------*/ delegate ProfileTest(); /*--------------------------------------------------------------------------*\ Override Report() if you need to extract additional information from the result. Important: see Format() for coercion bug \*--------------------------------------------------------------------------*/ function Header() { `trace(); `trace( Format("Description", 16) @ Format("x", 5) @ Format("Total") @ Format("Min") @ Format("Mean") @ Format("Max") ); `trace(); } function Report( ProfileResult r ) { `trace( Format(r.Desc, 16) @ Format(string(r.Passes), 5) @ Format(string(r.Total)$"ms") @ Format(string(r.Min)$"ms") @ Format(string(r.Mean)$"ms") @ Format(string(r.Max)$"ms") ); } /*--------------------------------------------------------------------------*\ Profiles TestFunc over multiple passes to calc unbiased results. Important: you can easily trigger a runaway loop exception by abusing the passes parameter, especially if the target function contains large loops of its own. \*--------------------------------------------------------------------------*/ function ProfileResult Profile( delegate<ProfileTest> testFunc, optional string desc, optional int passes = DefaultPasses ) { local int i; local float t; local ProfileResult r; r.Passes = passes; r.Min = MaxInt; while( i++ < passes ) { t = 0; // unnecessary? Clock(t); testFunc(); UnClock(t); r.Total += t; if( t < r.Min ) r.Min = t; if( t > r.Max ) r.Max = t; } r.Mean = r.Total / passes; r.Desc = desc; return r; } /*--------------------------------------------------------------------------*\ String formatting Important: UnrealScript bug? For convenience Format() coerces the first parameter to string. However, if the second parameter is omitted then the first parameter isn't coerced at all, causing odd behaviour. /evil monkey points at Tim Sweeney \*--------------------------------------------------------------------------*/ function string Format( coerce string s, optional int minChars = 12, optional int maxChars = 999 ) { if( Len(s) < minChars ) s = Left(StringPad, minChars - Len(s)) $ s; if( Len(s) > maxChars ) s = Left(s, maxChars); return s; } /*--------------------------------------------------------------------------*\ Default properties \*--------------------------------------------------------------------------*/ DefaultProperties { DefaultPasses = 10; DefaultInterval = 1.0; }Replace all the `trace() macros with `log() if you're not using my macros.Code:class SBProfiler extends DTKProfiler; /*--------------------------------------------------------------------------*\ Test data \*--------------------------------------------------------------------------*/ const DataLen = 10000; var array<int> Data; /*--------------------------------------------------------------------------*\ Fill the buffer with random data \*--------------------------------------------------------------------------*/ function InitialiseData() { local int i; Data.Length = 0; // clear previous runs for( i = 0; i < DataLen; ++i ) { Data.AddItem(Rand(100)); } } /*--------------------------------------------------------------------------*\ Override Header() and Report() to add "millions of iterations per second" \*--------------------------------------------------------------------------*/ function Header() { `trace(); `trace( Format("Description", 16) @ Format("x", 5) @ Format("Total") @ Format("Min") @ Format("Mean") @ Format("Max") @ Format("Iterations/sec", 16) ); `trace(); } function Report( ProfileResult R ) { `trace( Format(r.Desc, 16) @ Format(string(r.Passes), 5) @ Format(string(r.Total)$"ms") @ Format(string(r.Min)$"ms") @ Format(string(r.Mean)$"ms") @ Format(string(r.Max)$"ms") @ Format(string((Data.Length * (1000.0 / R.Mean)) / 1000000)$"m", 16) ); } /*--------------------------------------------------------------------------*\ Profiling \*--------------------------------------------------------------------------*/ auto state Profiling { Begin: // Prep... DefaultPasses = 10; InitialiseData(); Sleep(2.0); // Initial delay for engine to settle down Header(); // Begin... Sleep(DefaultInterval); Report(Profile(Test_ForLoop, "ForLoop")); Sleep(DefaultInterval); Report(Profile(Test_ForLoopOptimal, "ForLoopOptimal")); Sleep(DefaultInterval); Report(Profile(Test_ForLoopAbstract, "ForLoopAbstract")); Sleep(DefaultInterval); Report(Profile(Test_DoUntil, "DoUntil")); Sleep(DefaultInterval); Report(Profile(Test_While, "While")); Sleep(DefaultInterval); Report(Profile(Test_WhileDescending, "WhileDescending")); Sleep(DefaultInterval); Report(Profile(Test_ForEachWithIndex, "ForEachWithIndex")); Sleep(DefaultInterval); Report(Profile(Test_ForEach, "ForEach")); } /*--------------------------------------------------------------------------*\ Tests Note: compiler will complain about unused locals, ignore it \*--------------------------------------------------------------------------*/ function Test_ForLoop() { local int i, d; // standard for loop for( i = 0; i < Data.Length; i++ ) { d = Data[i]; } } function Test_ForLoopOptimal() { local int i, d; // pre-increment and cached length for( i = 0; i < DataLen; ++i ) { d = Data[i]; } } // remaining tests also cache length... function Test_ForLoopAbstract() { local int i; // raw, no array access for( i = 0; i < DataLen; ++i ) { } } function Test_DoUntil() { local int i, d; // optimal do-until loop (warning: unsafe for empty arrays) do { d = Data[i]; } until( ++i >= DataLen ); } function Test_While() { local int i, d; // (fudged) ascending while loop i = -1; while( ++i < DataLen ) { d = Data[i]; } } function Test_WhileDescending() { local int i, d; // descending while loop i = DataLen; while( --i >= 0 ) { d = Data[i]; } } function Test_ForEachWithIndex() { local int i, d; foreach Data(d, i) { } } function Test_ForEach() { local int d; foreach Data(d) { } }
To use it in your own projects simply modify the template of SBProfiler, and substitute your own test functions. The code should be clear enough to follow, or post a question here. Spawning an instance of SBProfiler will auto-run the tests after a short delay. To re-run the tests you can add an exec somewhere useful. For example:
Warning!Code:class CustomGame extends GameInfo; var SBProfiler Profiler; event PostBeginPlay() { super.PostBeginPlay(); Profiler = Spawn(class'SBProfiler'); } exec function ReRun() { Profiler.GotoState('Profiling', 'Begin'); }
You really must disable any cpu turbo features (EIST speedstep on Intel chips) because it will cause havoc. Clock/Unclock had me chasing my tail for a good half-hour before I realised my cpu was changing clock speed in the middle of the tests, and sometimes causing Unclock to return negative results. After disabling EIST the results were pretty solid.
Results running underclocked at 1.6Ghz on an Intel i5-2500k:
What a difference! At full clock the raw foreach loops can break 100 million iterations per secondCode:Description x Total Min Mean Max Iterations/sec ForLoop 10 25.5344ms 2.5336ms 2.5534ms 2.5732ms 3.9163m ForLoopOptimal 10 22.9611ms 2.2565ms 2.2961ms 2.3357ms 4.3552m ForLoopAbstract 10 14.0934ms 1.2668ms 1.4093ms 1.4648ms 7.0955m DoUntil 10 12.8266ms 1.2668ms 1.2827ms 1.3064ms 7.7963m While 10 15.1623ms 1.5043ms 1.5162ms 1.5439ms 6.5953m WhileDescending 10 14.5684ms 1.4252ms 1.4568ms 1.4648ms 6.8642m ForEachWithIndex 10 2.0586ms 0.1979ms 0.2059ms 0.2375ms 48.5771m ForEach 10 1.7815ms 0.1584ms 0.1781ms 0.1979ms 56.1335m
But to put things in better perspective I add an extra line to each test...
... and retry. This simply adds a little meat to the loop to give more practical results (abstract for loop removed):Code:d += d;
Foreach is still way out infront and the iterator of choice. You can also see some fluctation in the ForLoop results - these tests really need to be run several times before drawing conclusions.Code:Description x Total Min Mean Max Iterations/sec ForLoop 10 24.4655ms 2.3753ms 2.4465ms 2.4545ms 4.0874m ForLoopOptimal 10 23.8321ms 2.3753ms 2.3832ms 2.4545ms 4.1960m DoUntil 10 16.1520ms 1.5835ms 1.6152ms 1.6627ms 6.1912m While 10 18.2897ms 1.8211ms 1.8290ms 1.9002ms 5.4676m WhileDescending 10 18.5273ms 1.8211ms 1.8527ms 1.9002ms 5.3975m ForEachWithIndex 10 5.7799ms 0.5542ms 0.5780ms 0.6334ms 17.3014m ForEach 10 5.5423ms 0.5542ms 0.5542ms 0.5542ms 18.0429m
I should also point out that this is only suitable for testing isolated examples. Proper profiling of your project should be done through the Game Profiler tools in UDK.
Have fun and let me know if you find any bugs![]()
Last edited by Spoof; 08-21-2012 at 01:17 PM. Reason: minor code edit
Spoof, you really should get MVP for that post alone. Awesome work.
Bookmarks