Results 1 to 15 of 15
  1. #1
    Iron Guard
    Join Date
    Sep 2010
    Location
    Slovakia
    Posts
    508

    Default foreach ArrayVariable() vs. for(i=0,i<Array.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 ?

    Code:
    foreach ArrayVariable(ArrayItem, Idx)
    or

    Code:
    for(i=0;i<ArrayVariable.Length;i++)
    Thank you.
    Czech and Slovak UDK site - www.udk-site.net-core.eu
    Space Shock project on STEAM GREENLIGHT

  2. #2
    Palace Guard
    Join Date
    Jan 2010
    Location
    Germany
    Posts
    3,945

    Default

    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.

  3. #3

    Default

    Only one way to find out.

    Code:
    function Test()
    {
        local float f;
    
        Clock(f);
    
        // Run a test
    
        UnClock(f);
    
        `log("Test length:" @ f);
    }

  4. #4
    Boomshot
    Join Date
    Aug 2011
    Posts
    2,366

    Default

    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.

  5. #5
    MSgt. Shooter Person
    Join Date
    Feb 2006
    Location
    In Potatoland
    Posts
    497

    Default

    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?

  6. #6
    MSgt. Shooter Person
    Join Date
    Nov 2011
    Posts
    42

    Default

    Code:
    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 for(): length 1213.6047
    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

  7. #7
    Banned
    Join Date
    Feb 2011
    Location
    BXL/Paris
    Posts
    2,169

    Default

    @Jephir: Your TestArray in defaultproperties is impressive - by hand..?
    Like Spoof said: ...it really depends on the situation.

    BTW. You forgot about ZERO...

  8. #8
    Iron Guard
    Join Date
    Sep 2010
    Location
    Slovakia
    Posts
    508

    Default

    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.
    Czech and Slovak UDK site - www.udk-site.net-core.eu
    Space Shock project on STEAM GREENLIGHT

  9. #9
    MSgt. Shooter Person
    Join Date
    Feb 2006
    Location
    In Potatoland
    Posts
    497

    Default

    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.

  10. #10
    Boomshot
    Join Date
    Aug 2011
    Posts
    2,366

    Default

    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

  11. #11

    Default

    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).

  12. #12
    Boomshot
    Join Date
    Aug 2011
    Posts
    2,366

    Default

    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.

  13. #13
    Veteran
    Join Date
    Sep 2006
    Location
    Newcastle, UK
    Posts
    6,971
    Gamer IDs

    Gamertag: ambershee

    Default

    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. -

  14. #14
    Boomshot
    Join Date
    Aug 2011
    Posts
    2,366

    Default

    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;
    }
    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)
        {
        }
    }
    Replace all the `trace() macros with `log() if you're not using my macros.

    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:

    Code:
    class CustomGame extends GameInfo;
    
    
    var SBProfiler Profiler;
    
    
    event PostBeginPlay()
    {
        super.PostBeginPlay();
    
        Profiler = Spawn(class'SBProfiler');
    }
    
    
    exec function ReRun()
    {
        Profiler.GotoState('Profiling', 'Begin');
    }
    Warning!
    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:

    Code:
          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
    What a difference! At full clock the raw foreach loops can break 100 million iterations per second

    But to put things in better perspective I add an extra line to each test...

    Code:
        d += d;
    ... and retry. This simply adds a little meat to the loop to give more practical results (abstract for loop removed):

    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
    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.

    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

  15. #15
    MSgt. Shooter Person
    Join Date
    Oct 2011
    Posts
    246

    Default

    Spoof, you really should get MVP for that post alone. Awesome work.


 

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Copyright ©2009-2011 Epic Games, Inc. All Rights Reserved.
Digital Point modules: Sphinx-based search vBulletin skin by CompletevB.com.