Search Unity

InvokeRepeating vs Update - battle of efficiency

Discussion in 'Scripting' started by TJTown, Dec 3, 2013.

  1. TJTown

    TJTown

    Joined:
    Jul 12, 2013
    Posts:
    20
    If both were running at the same rate, which would be more efficient...

    Update running at 60 fps
    or
    InvokeRepeating("Invoke", 0, .01667f);

    If Update is more efficient, then there probably is a point at which you shouldn't use InvokeRepeating. Anyone have information on this? There doesn't seem to be anywhere I could find that talks about InvokeRepeating being used to update at 60fps or close to it. I am currently using it to update something at 50fps and several things to update at 30fps but was wondering if I could hurt performance by doing this.
     
    dan_ginovker and rothpletz5 like this.
  2. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    You can't count on Update running at 60fps, so it's less a matter of efficiency than using whatever function is most appropriate.

    --Eric
     
  3. TJTown

    TJTown

    Joined:
    Jul 12, 2013
    Posts:
    20
    I know that Update wont necessarily run at 60fps, but for the sake of argument, I was just comparing the two in optimal conditions.
     
    dan_ginovker likes this.
  4. Brian-Stone

    Brian-Stone

    Joined:
    Jun 9, 2012
    Posts:
    222
    InvokeRepeating doesn't do anything particularly special. It simply holds execution of the given method. by yielding on WaitForSeconds for the repeatRate time.

    In short, if the update loop is operating at a fixed rate of 60 times per second, and if InvokeRepeating is set to call the "Invoke" method every 1/60th of a second, then it will effectively be called 60 times per second.

    That isn't to say, however, that there isn't a lot more overhead required to call your "Invoke" method through InvokeRepeating. It's doing this calling through reflection, which is slow. (see: MethodInfo.Invoke Method)
     
    Last edited: Dec 3, 2013
    dan_ginovker likes this.
  5. TJTown

    TJTown

    Joined:
    Jul 12, 2013
    Posts:
    20
    Interesting. So to boost performance of a game, there is a point at which you should not use InvokeRepeating if you don't have to and just put it in the update. Would it be good practice to use InvokeRepeating to update at 30 fps instead of Update at ~60fps if the goal is to boost performance? Is there a better method?

    I've basically been separating out my Update code into InvokeRepeating and assigning an update rate that I think makes sense for that particular bit of code. For instance checking if a player is near an object at about 3fps, checking if a player's health is getting low at about 10fps, updating some GUI elements at 30fps, etc. Instead of updating all these things at the game's frame rate which runs at 60fps vsynced for no other reason than to boost performance.
     
  6. Errorsatz

    Errorsatz

    Joined:
    Aug 8, 2012
    Posts:
    555
    If you're already running code in Update, it seems like that would be more efficient for things even at 10 fps. It's only adding a single addition and comparison each frame, which seems like less overhead than an Invoke every six.
     
  7. Brian-Stone

    Brian-Stone

    Joined:
    Jun 9, 2012
    Posts:
    222
    You don't get away from doing simple Boolean checks by using InvokeRepeating. There's still a piece of code in the update loop that is saying, "Is it time to invoke this call? Yes, No?" Basically this is what InvokeRepeating is doing for you. But it's not free. There's a considerable amount of logic inside InvokeRepeating that gets executed on each iteration of the game loop.

    InvokeRepeating pseudo code (sort of)...
    Code (csharp):
    1.  
    2. /*
    3. void InvokeRepeating(string methodName, float time, float repeatRate)
    4. {
    5.    - Find the method called "methodName" in the calling assembly and store it.
    6.    - Wait "time" seconds by yielding on WaitForSeconds(time)
    7.    Loop until the user cancels this action
    8.    {
    9.       - Invoke the stored method
    10.       - Wait "repeatTime" seconds by yielding on WaitForSeconds(repeatRate)
    11.    }
    12.    - Remove the method info record from storage.
    13. }
    14. */
    15.  
    That may not be exactly how InvokeRepeating works, this is just my guess as to how it works, but the functionality is basically the same.

    So, if InvokeRepeating is so much less efficient than calling your method directly, then why would you want to use at all? Good question. The answer is that it helps make repetitive tasks easier to program. It simplifies your code, which makes your code easier to read. Simpler code that relies heavily on reuse of lower-level functions generally has fewer bugs and is generally more reliable. So, it's a trade off between reliability and execution efficiency. That's really the only thing that this is good for.**

    ** Actually, I have to strike that. The other thing that InvokeRepeating (and coroutines in general) are good for is to defer the processing of heavy-weight tasks. For example, if you are loading a very large file, or if you are processing a large chunk of data, which may take a considerable amount of time, then you need to break up that processing into chunks so that it doesn't block the game loop. You could, of course, spawn a new execution thread and do that heavy-weight work asynchronously, which can also take advantage of computers with multiple processors. Multi-threading is the natural way to solve those types of problems. However, coroutines effectively do the same thing as multiple threads, by deferring blocks of code for later execution, literally by jumping past them and then jumping back to them later on within the same thread of execution. When the additional overhead of calling methods through InvokeRepeating is added it up, it may be minuscule compared to the time that the entire heavy-weight operation needed to complete, which is effectively an efficiency gain as far as the user is considered, because his game didn't lock up for several seconds while this other processing was taking place.
     
    Last edited: Dec 3, 2013
  8. TJTown

    TJTown

    Joined:
    Jul 12, 2013
    Posts:
    20
    Is there a better way then to only perform certain tasks at a lower frame rate? In some cases I have lots of complicated things happening that I can only imagine would less intense to only execute a handful of times per second? Maybe Coroutines?
     
    dan_ginovker likes this.
  9. Errorsatz

    Errorsatz

    Joined:
    Aug 8, 2012
    Posts:
    555
    Coroutines can work, pretty easily. Just loop inside it with a yield each cycle. You can also trigger them from Update by adding up the elapsed time and checking when it hits a set limit.

    If you're talking about something that's only a few times a second though, then the overhead from InvokeRepeating is not going to be a big factor.
     
  10. Brian-Stone

    Brian-Stone

    Joined:
    Jun 9, 2012
    Posts:
    222
    If your concern is that you're going to run down the processor with all of these little tasks, then you really ought to run some tests and see exactly how slow it's actually going to be. Then work from there to optimize your process.

    If you have a complicated task that is going to eat up your frame rate, then consider doing that work inside of a coroutine or InvokeRepeating. Breaking that work into small chunks will take less time out of each game loop iteration, which will improve the overall efficiency of your program (Basically what Errorstaz said).

    If you have a very simple task that has to be repeated every frame, such as a clock updater, then you probably don't want to do that inside a coroutine or InvokeRepeating, There's going to be little to no gain in efficiency.

    But, don't let these be strict rules. Even if you lose execution efficiency, you may gain code simplicity and reliability by using coroutines and tools that use them. There is no right/wrong answer here. Once you start using coroutines regularly, you'll get a better idea of where they work best for you and where they kind of get in the way.
     
  11. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    This, one bazillion times. There's no point speculating because there are no global answers. Run some tests on your target device in your particular use case, and go with whatever performs best. No amount of speculation can do better than that.

    But also, how many of these things are running each frame? When optimising for execution speed, unless you're doing something exceedingly expensive in it there is no point looking at one-off code or code that runs on a small number of instances. Put the majority of your time into the code that's being executed the most, because that's where you'll get the most gain.

    For stuff that doesn't run often (lets draw a vague line in the sand and say < 100 times per frame, though this will vary depending on how expensive it is)
    I focus more on clear, understandable and maintainable code.
     
  12. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,616
    IMO, you shouldnt use InvokeRepeating if you need a higher rate than 0.1f

    IR is a good way to trigger functions that need to be constantly calling, but at a slower rate than update... things like AI, Lookup, Pathfinding, Target Assignment, etc


    As a guideline, I only put the following in the various updates

    Update:
    Input
    Translate/Rotation (non-physics)

    FixedUpdate:
    Physics

    LateUpdate:
    Camera movement
    Animation
     
    TooManySugar and Brendonm17 like this.
  13. Shbli

    Shbli

    Joined:
    Jan 28, 2014
    Posts:
    126
    Why you need to write the code in the update methode?

    For example to check if player health is less than a speicifc value, you can add that code actually into the part where you are decreasing the health

    For example I have the following
    Code (CSharp):
    1. void decreasePlayerHealth(float pValue) {
    2. health -= pValue;
    3. if (health < dangerAmount) {
    4. //do what you want
    5. }
    6. }
    I know it's old thread, hopefully someone find it useful.