Search Unity

Performance question: OverlapSphereNonAlloc v.s. OverlapSphere

Discussion in 'Scripting' started by iSleepzZz, Sep 18, 2020.

  1. iSleepzZz

    iSleepzZz

    Joined:
    Dec 23, 2012
    Posts:
    206
    I understand that the NonAlloc version will be better in most cases because it does not build the array creating some allocations and therefore making the garbage collector do more work. (bad performance)

    However, here is my question...

    I am in need of calling this OverlapSphere a few times, or even just one time per second.
    Nevertheless, if I use OverlapSphere, it's all easy to do. But if I were to use the NonAlloc version, I would need to simply create a
    Code (CSharp):
    1. Collider[]
    array and once I'm done with that, I would need to clear the array using
    Code (CSharp):
    1. theArray = new Collider[]
    sooooo is there really ANY different in performance than just using the normal OverlapSphere method? Isn't this doing the same thing?

    Any information on this topic would be greatly appreciated, thanks!
     
  2. KyleOlsen

    KyleOlsen

    Joined:
    Apr 3, 2012
    Posts:
    237
    You can reuse the same array over and over with the NonAlloc versions, no 'clearing' required.
     
    Bunny83 likes this.
  3. iSleepzZz

    iSleepzZz

    Joined:
    Dec 23, 2012
    Posts:
    206
    Unfortunately that is not possible in my application.
    I am getting players around me in a circle so the array will need to be cleared out each time because a player can be inside get hit then walk away and it should not keep hitting them.
     
  4. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    4,002
    No, you don't seem to understand how it's supposed to be used. Keep in mind that the size of an array can not be changed once it's created. So you have to define the array size when you create it. All you have to ensure is that the array size is large enough to hold your worst case number of elements.

    OverlapSphereNonAlloc will just fill the array from the beginning with as many elements it has found and returns the number of elements. So there's no issue in reusing the same array as long as you use the count that was returned as a limit.

    Unity has implemented other nonalloc methods which can actually take a List. A generic List does nothing else. It has an internal array which is usually larger than the actual element count. It also stores the actual count in an internal int variable. When you add or remove elements the List reuses the same array and just adds the elements in the yet free spots in the array. Though the List will automatically create a new, larger array if the capacity of the array is exceeded. Most List implementations will simply double the current capacity.

    As usual the example given in the OverlapSphereNonAlloc documentation is completely misleading and wrong. You should do something like:

    Code (CSharp):
    1. public class ExampleClass : MonoBehaviour
    2. {
    3.    int maxColliders = 10;
    4.    Collider[] hitColliders = new Collider[maxColliders];
    5.    void ExplosionDamage(Vector3 center, float radius)
    6.    {
    7.        int numColliders = Physics.OverlapSphereNonAlloc(center, radius, hitColliders);
    8.        for (int i = 0; i < numColliders; i++)
    9.        {
    10.            hitColliders[i].SendMessage("AddDamage");
    11.        }
    12.    }
    13. }
    Keep in mind that if the array size is set to 10 you can only receive 10 elements maximum. So if there are 11 or 25 elements, you only get 10 of those. So you have to ensure you choose a reasonable large enough array for your usecases.

    One final note that isn't that important but I think should be mentioned:
    When you just reuse the array like I've shown it's possible that old elements stay in the array. This is no problem for the processing since we only iterate up to numColliders. For example if one time 4 elements has been found you will just process those first 4 elements. If another time you get zero elements the for loop will not run at all because 0 is not smaller than 0. However those 4 elements will still stick around in our array. This might be an issue for garbage collection as all the referenced objects can't be garbage collected when they have been destroyed. The generic List class will actually set the unused elements to null / default values to ensure nothing remains in the unused space. Of course you can clean up your array yourself. In the case that you just need to iterate through your collection once (like in my example) you could simply set the current element to null as you iterate through the list. If you need the collection more than once, there are several approaches. You could simply clean up the array in regular intervals (InvokeRepeating or coroutine) or just remember the last element count in a member variable and before you do the next OverlapSphereNonAlloc you just iterate through the old elements and set them to null. If the array isn't that large you could simply set all elements to null

    Code (CSharp):
    1. void ExplosionDamage(Vector3 center, float radius)
    2. {
    3.     for(inf i = 0; i < hitColliders.Length; i++)
    4.         hitColliders[i] = null;
    5.     int numColliders = Physics.OverlapSphereNonAlloc(center, radius, hitColliders);
    6.     // [ ... ]
    7. }
     
  5. iSleepzZz

    iSleepzZz

    Joined:
    Dec 23, 2012
    Posts:
    206
    Hi thanks for all the information but unfortunately missing the mark here.
    Most likely my explanation skills.
    I understand the difference between the two however,
    I am just curious since my use case is a repeating call of it happening 4 times a second, it will be a necessity for me to clear the array and assign that a brand new empty array to it before each call because a player could easily move outside the sphere radius.
    My main point is, is it still more optimal for me to use the NonAlloc version even though i am essentially clearing the array to a brand new empty array before each call. To me, it seems exactly the same as the normal method call performance.
    Hopefully im explaining this nicely.
    Thanks
     
  6. Antony-Blackett

    Antony-Blackett

    Joined:
    Feb 15, 2011
    Posts:
    1,778
    Bunny83 is correct. You can do it without new arrays every time.

    You don't need to clear the array at all. The function returns how many things were hit by the overlap so just iterate your array up to that count. all the most recently hit objects in the array are always at the front, so even if there is old data in the array you can safely ignore it.

    Code (csharp):
    1.  
    2. int numOverlaps = Physics.OverlapSphereNoAlloc(center, radius, hitColliders);
    3. for( int i = 0; i < numOverlaps; ++i )
    4. {
    5.   Collider iWasJustOverlappedCollider = hitColliders[i];
    6.   // more logic....
    7. }
    8.  
     
    Last edited: Sep 19, 2020
    glenneroo and Rallix like this.
  7. iSleepzZz

    iSleepzZz

    Joined:
    Dec 23, 2012
    Posts:
    206
    Awwwh! I get it now!
    I was under the impression that the NonAlloc will ADD to the buffer (hitCollider).
    I was mistaken! It actually OVERWRITES all the array.
    Amazing, so yes i will never need to empty the array.
    Awesome, this is great news. Thanks everyone!
     
    rc82, KyleOlsen and Antony-Blackett like this.