I had seen this mentioned on the forms previously, but didn’t give it much notice as I wasn’t focusing on the performance of my project until very recently. I am in the middle of a large project with multiple LARGE terrains, huge viewing distances, massive amounts of trees, grass and other detail objects, etc etc. Plenty of things going on to slow down even a good rig. But after careful debugging I was shocked to find that these things had nothing to do with my slow performance. In fact far and above anything else this is what killed my performance…
function Update()
yep. good ol’ function Update() that occurs in all your trusty javascript files and runs an uncounted number of times per second. There were very few scripts that actually needed to run this quickly (in fact only one, a script I use to calculate various game world timings). In every other case I replaced the update function with an invoke repeating function set to run every 100th or 10th of a second, depending on the usage.
function Start(){
InvokeRepeating("MyFunction",0.0,0.01)
}
function MyFunction(){
.
.
}
by using invoke repeating in the start function it essentially mirrors the same behavior as update, but at a more controllable and reasonable rate. this single change took me from 35fps to 65 fps. O_O
Again, this is something I’ve read about here, but just did not believe enough at the time to go through and implement. I wish I had done this sooner. I don’t think I’ll ever use function update() again except in very specific circumstances. It may not be the best idea to have the default javascript setup use a function that can so easily get out of control. In fact I was thinking I had simply hit the ceiling of unity’s useable performance bandwidth… how shocked I was to discover it was simply my own shoddy coding!
(now if I could only figure out my particle effect slowdowns I could be back up to 90fps easily)
Keith, that seems to work exactly the same, but with invokerepeating I believe you have some additional functionality… for example you can cancel the invoke at any time with one line of code, or even invoke a different function instead.
@Morning yes you’re right it does only one once per frame!, I meant to say an “uncounted number of times per second”. and have edited it above. but actually I do use this for all my user input scripts as well. you don’t need to get input at the rate that Update() is running. 1/100th of a second with invoke repeating seemed plenty fast for me, and I’m sure you could drop it even lower depending on what kind of game you’re building. The only thing I found it gave me issues with is non-physics based gameobject movement that gets updated every frame, such as a cloud moving across the sky… even so I was still able to use this by increasing the speed of the invoke.
Update() has very specific behaviour, where what you’re suggesting has partially undefined behaviour. For instance, we know how Update(), LateUpdate() and FIxedUpdate() behave as the framerate changes, but what happens to functions called from InvokeRepeating? At what stage during a frame are InvokeRepeating()-called functions actually called? How accurate is the timing of InvokeRepeating? Do you have any control over smoothing out when the invocations are called?
It seems to me that a large part of the benefit you saw from this could have come from the functions which were being called once per frame (30+ times per second) which you now call only 10 times per second, thus reducing their workload by more than 60%. I’m interested to know if you’d have got a similar performance boost by simply early-returning from the related update functions on every 2nd and 3rd frame.
You also mention “more controllable” as a benefit. I’m interested to know exactly what you mean by that, assuming there’s more to it than just specifying your own update rate. If controllability is important, I’d strongly suggest something along the lines of creating a “managed update” system - make an interface IManagedUpdateable and a script UpdateManager. Scripts implementing IManagedUpdateable register themselves with the UpdateManager and use a function ManagedUpdate() instead of Update(), and include properties defining when they should update. The UpdateManager then calls ManagedUpdate() on registered objects as it deems necessary, and you have a very fine degree of control. For instance, if you’ve got 100 scripts that you want to run every 10th frame, you can make sure that 10 of them are run reach frame, instead of potentially having all 100 run on the same frame, smoothing out the workload.
It’s not exact same thing. For a start, with invoke you can stagger the timing so they all don’t start at the same time and don’t update at the same time. This is a much larger speed increase with more control. Also, when the game needs to restart after dying or whatever, you can have a function to fire them all off again - a Reset() function you can sendmessage to.
Ah yes this is true, and I haven’t had the need to test this in conjunction with LateUpdate() or FixedUpdate() yet. So I’m not exactly sure how they would interact. Also at least to this point my scripts do not require the delicate type of updateManager you describe, which seems like a very good idea for anyone running massively heavy scripts that may affect each others performance. Still this fix is SO SIMPLE to apply, and the performance benefits are potentially huge. I’m sure anyone not using InvokeUpdate() would be able to quickly see the benefit of using it in many cases over Update().
And I do still stand by my assertion that it is more controllable. You can cancel, or re-time the firing at will, as well as use multiple invokes on separate functions. All with only a couple lines of code. without the complexity of writing a custom management system if you don’t require that complexity.
You can’t use Invoke for anything that requires Time.deltaTime or Time.deltaFixedTime (FixedUpdate). This is basically important for frame rate independent movement, when you want an object to move 15 units per second forward you do
Stuff like that can’t be done in invoke, unless you have an Update Method which creates a sum of delta Time of previous frames though question is how accurate this may or may not be.
@Op:
Well as others pointed out, your code may does to much to often in your code, i.e. recalculating the same stuff over and over even though you only need it once. You could also get similar results with coroutines (and WaitForSeconds) or something like
No, because simply having Update called every frame has a fair amount of overhead whether you do anything inside it or not. It’s a clear benefit to remove Update when you can, assuming the replacement isn’t called even more often. InvokeRepeating is easily more CPU-efficient for things that only need to be called at relatively infrequent set intervals (and since it runs on time, it’s just as framerate-independent as using Time.deltaTime). However I’m less sure about the usefulness of calling InvokeRepeating 100 times per second; I’d be inclined to just use Update in that case.
As for coroutines, they do generate garbage, so are less efficient than InvokeRepeating. If you have a situation where either a coroutine or InvokeRepeating would do essentially the same thing, use InvokeRepeating. However coroutines are really quite different things and are highly useful in cases where InvokeRepeating is not, such as scheduling a series of events.
Basically, all 3 methods have their uses. I would certainly recommend InvokeRepeating, as well as coroutines, and even Update where appropriate. Just make sure you’re using them appropriately.
Yeah, but even the impact of that will vary drastically depending on how your game/scenes are architected. Hence I’m one who’d prefer to do some quick tests than make definitive claims about how a hypothetical change will impact performance.
For instance, if you’ve got hundreds or thousands of instances of a particular script using Update() for their own individual GameObjects, then simple early-returning them is still going to have a significant hit because you’re still calling all of the Update()s and you’re performing the early-return check individually on every one of them. On the other hand, if you have a single script looping over the same GameObjects applying the same functionality, then an early return will be negligible because doing one check per frame will then save you all of the work at once. (Mind you that’s an example only, if I were to actually do that I’d stagger the load over frames instead of doing it all at once, meaning that the Update() call wasn’t redundant at all… but that’s another topic.)
I really felt the difference between Coroutine and InvokeRepeating today:
I just added a Ping-Visualization to a game.
In the meaning of showing a ghost of your character/reticle or whatever is delayed by your ping to show you how the server will see your movements.
I struggled with IENumerator coroutine just to realize that it can be called only once per frame or smth. This made the time resolution, = delayed movement of the ghost, choppy
but with InvokeRepeating @ 0.01s it works like a charm and has a great accuracy (measured by stopping the time between an initial single movement of the delayed object and the reunion with its ghost).
some values:
0.5013971s for 0.5s delay
0.05030441s for 0.05s
its interesting to see how a certain ping actually looks on the screen as there are no games im aware of that offer you to test-view the ping delay
ANYHOW, some thoughts:
I’ve seen a lot of gunscripts where the Instantiation is handled in Update. In detailed timeanalysis these scripts produce inconsistent ROFs. Even more inconsistency if the framerate is low compared to the ROF: e.g. the first and second frame may fire a bullet but for the third frame the timer is not ready and the next shot follows at frame 4…
Of course, it is hardly noticable at 60fps if shots come in varying frame intervals of ±1 frame when there are 5 frames in between. But essentially its an inconsistency that could be solved by using InvokeRepeating as Instantiation mechanism. But there is practically no gain because the stuff you want to hit with those bullets is bound to FixedUpdate anyway…
So Instantiating in Fixed Update would make more sense if the frame rate could suddenly drop below your ROF essentially decreasing your ROF. But conversion of the Input (e.g. KeyDown for semiauto guns) would be needed as they are only true in Update() and can be missed by fixed timestep.
anyhow, just timing stuff i thought about recently…
I haven’t done this in Unity, but the first game I wrote involved firing a gun, and I also wanted to solve the issue with framerate variability impacting when bullets were fired. It was actually pretty easy, even without resorting to external functions - instead of checking whether your shoot delay timer has reached zero you let it go below zero, and instead of checking “am I allowed to fire a bullet now” you check “when was I last allowed to fire a bullet”. That will give you a zero or negative time, and you can use that time to move the bullet forward the appropriate amount upon instantiation. And if you do this in a while loop instead of an if statement, you can accurately fire multiple bullets every frame. And by knowing where you were aiming last frame, you can smoothly interpolate the aiming direction as you fire, so you can have smooth arcs of projectiles even if the player is spinning around like a madman firing hundreds of bullets a second.
@angrypenguin, Nice! thats a better solution than InvokeRepeating applicable for raycast and non-raycast(slow moving) bullets!
-So basically i start a fireTimer as soon as i get the KeyPressed/KeyDown (+instantiating first shot when allowed)
(in Detail: this signal can of course only be gathered during a renderframe, thats a inevitable maximal buttonpressed to startshooting delay of zero to 16ms for 60fps depending on when, during a renderframe, you pressed the button. This is unnoticeable except for pro-drummers and even pressing down the spring of the keyboard button after your brain told your finger to hit it will take longer than that)
-Then checking each frame if fireTimer is big enough to fit a rofTime (e.g. rofTime would be 0.05F for 50ms between shots)
-calculate the foreshot time in seconds that the yet unfired shot would have had if instantiated at the right time
-calculate the right lerp factor based on the render time of last frame (only for muzzle position)
-instantiate it with the right lerp muzzle position and rotation between this and last frame and foreshot offset
As im in office with no access to Unity i’ll try to do it in browser, pls correct me if im wrong i feel like this could be much easier by subtracting rofTime earlier or precalc the ‘while’ cyclecount rather than comparing and calculating shotsmissed every cycle (see 2nd try).
while(fireTimer > rofTime){
//# bullet instantiation moments that we have missed
int shotsMissed = (int)fireTimer/rofTime;
//time that has passed after the bullet should have been instantiated
float foreshot = (shotsMissed-1)*rofTime + fireTimer%rofTime;
//interpolation Factor to Lerp pos and rot of the MUZZLE! between last and current renderframe
float instantiationLerpOffset = foreshot/Time.deltaTime;
//interpolates the spawn pos and rot with a Lerp between this and last frames states
MyInterpolatedInstantiationFunction(foreshot, instantiationLerpOffset);
fireTimer -= rofTime; //we now have fired one shot and have to adjust the fireTimer
}
example:
assume fireTime is 2.8s and rofTime is 1s as we enter the ‘while’ for the first time. Also renderTime is somehow 5s (renderTime will logically always be bigger than the foreshot time because the ‘while’ gets called every renderFrame)
we get:
-2 shots should have been fired
-foreshot time is 1 + 0.8s, this means the oldest shot should have been fired 1.8s ago
-1.8s is 36% of 5s so the Lerp factor for this shot is 0.36 (lerping FROM current TO Last Frame, not reverse)
-The bullet gets instantiated at the Muzzles lerped rotation and position between this and last frame
-and gets an initial offset position based on the bullets speed (if its not a raycast bullet) and foreshotTime. But be aware that targets between the offset position and the muzzle might get ignored if the offset is big enough (raycast between!)
If this is right its a completely frameRateIndependent(despite the intitial trigger impulse) way of achieving bullet-instantiation-accuracy like angrypenguin did
2nd try:
int shotsMissed = (int)(fireTimer/rofTime); //# shot instantiations missed until this frame
float smallestForeshot = fireTimer%rofTime; //time difference from now to the time the LAST bullet should have been instantiated
fireTimer -= shotsMissed * rofTime; //subtract before shotsMissed gets decreased
while(shotsMissed > 0){ //until all missed shots are fired
shotsMissed--;
float foreshot = shotsMissed*rofTime + smallestForeshot; //calc foreshot of current shot
float lerpFactor = foreshot/Time.deltaTime; //calc lerp factor
//call costum Instantiation
MyInterpolatedInstantiationFunction(foreshot, lerpFactor);
}
//testing this function could be done by forcing unity to render with very low fps, 0.5-5fps
//the shots should maintain a consistent ROF visible by
//-impact points of shots fired during rotations of the weapon
//-or bullet-to-bullet distance for non-raycast bullets