Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. Dismiss Notice

Just iterating over GetEntities results takes more time than anything else?

Discussion in 'Entity Component System' started by dgoyette, Nov 3, 2018.

  1. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    4,113
    I'm just starting to experiment with Hybrid ECS. What I'm finding is that just doing a foreach over a GetEntities call is taking up the majority of the time it takes my System to run. Is this typical? Or am I doing something wrong? My test scene has 2000 rigidbodies in it for stress testing.

    upload_2018-11-3_10-2-38.png

    Here's the code for the system, which is pretty simple: Loop over some entities, and call AddForce. I tried changing it from a foreach to a for loop over the results of GetEntities, but `ComponentGreoupEnumerator get_Current` still takes up all that time. What am I doing wrong?

    Code (CSharp):
    1.     [UpdateBefore(typeof(FixedUpdate))]
    2.     public class RiftForceSystem : ComponentSystem
    3.     {
    4.         protected override void OnUpdate()
    5.         {
    6.             int riftIndex = 0;
    7.             foreach (var riftEntity in GetEntities<RiftGroup>())
    8.             {
    9.                 var riftPosition = riftEntity.RiftEntity.gameObject.transform.position;
    10.                 var riftStrength = riftEntity.RiftEntity.RiftConfiguration.BaseRadius * 50;
    11.                 var riftBitPosition = (1 << riftIndex);
    12.  
    13.                 foreach (var rigidbodyEntity in GetEntities<RigidbodyGroup>())
    14.                 {
    15.                     if ((rigidbodyEntity.RigidbodyEntity.RiftVisibility | riftBitPosition) == 1)
    16.                     {
    17.                         rigidbodyEntity.Rigidbody.AddForce((riftPosition - rigidbodyEntity.Rigidbody.worldCenterOfMass) * riftStrength, ForceMode.Impulse);
    18.                     }
    19.                 }
    20.  
    21.                 riftIndex++;
    22.             }
    23.         }
    24.  
    25.     }
    Thanks.
     
  2. Ofx360

    Ofx360

    Joined:
    Apr 30, 2013
    Posts:
    155
    I guess an optimization you could do here is store your rigidbodygroup entities in a variable outside of the foreach loop. That way you’re not calling GetEntities so much.

    You could also switch over to using ComponentGroup
     
  3. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    4,113
    Thanks for the ComponentGroup suggestion. That's quite a bit faster. It still seems weird that merely iterating over a collection is more expensive than the other operations I'm doing (like AddForce). It makes me wonder how Systems that handles hundreds of thousands of objects can possibly keep up.

    upload_2018-11-3_16-36-33.png
     
  4. Micz84

    Micz84

    Joined:
    Jul 21, 2012
    Posts:
    436
    You are iterating over Rigidbody unit monobehaviour based component. It will not have linear memory access and can't be burst compiler.
     
  5. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    4,113
    Thanks, that's a helpful way to frame it. I've tried to restructure my Entity and System so that it only contains primitive types. Is there a way to tell whether my system is running with the burst compiler while it's running? I don't see anything related to my system in the Burst Inspector window, but I don't know if I should see anything there.

    Also, is there a list of datatypes that are allowed in my entity if I want Burst Compiler support? I see a list of data types here, which I assume is what I want? https://docs.unity3d.com/Packages/com.unity.burst@0.2/manual/index.html

    If so, does that mean my entity can't have a Vector3 on it, and instead I need to use float3?

    Finally, is this all moot if I'm using RaycastCommand within my system? Or does RaycastCommand work with Burst?
     
  6. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,624
    Burst compiler works with jobs; your original code is not within a job therefore the burst compiler won't work on it.

    To get burst compiler to work, all you need to do is add the [BurstCompile] attribute on a valid job and it'll work (if it doesn't work, it's going to throw errors telling you why.)

    It will work fine with Vector3 but it's probably good practice to use float3, they support implicit casting anyway.
     
    Last edited: Nov 4, 2018
  7. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    4,113
    Okay, thanks, I'm still getting used to this. I thought somehow that ECS automagically jobified some things, and that those things would automagically use burst. But I see what you're saying. If I want Burst, then my ECS system should execute a job to do its work, and that portion will potentially be burst compiled if I write it correctly.

    In that case, is it possible to do a raycast in a job? I know that RaycastCommand jobifies raycasting, which is what I've been using. But I don't know if RaycastCommand is burst-compatible, or whether I can perform a raycast within a job I write.
     
  8. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,624
    RaycastCommand is just a job and returns a job handle that can be passed as a dependency to your own jobs. I don't know if it's burst compiled, depends on Unitys implementation but it probably is.

    Code (CSharp):
    1. var handle = RaycastCommand.ScheduleBatch(commands, results, 1);
    You can pass results as it's a NativeArray to your own jobs to work on.
     
    Last edited: Nov 4, 2018
  9. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    4,113
    Thanks to everyone for the help on this. I started trying out Hybrid ECS thinking it would lead to improved performance, but I see now that it's really the job system that yields the performance boosts. My original MonoBehaviour based code was already using RaycastCommand to perform raycasts via the Job system, and it doesn't seem that switching to Hybrid ECS itself offers any meaningful performance improvements. At this point I don't see a strong reason to shift parts of my code over to Hybrid ECS, though I'll continue to use Jobs when possible to seek better performance.

    Thanks again, and anyone can feel free to correct me if I've gotten the wrong idea about Hybrid ECS.
     
  10. Lurking-Ninja

    Lurking-Ninja

    Joined:
    Jan 20, 2015
    Posts:
    9,900
    The idea behind the hybrid tool set is to force yourself to write your application on a way that easily can be converted to ECS when it will be finished. If you have just started your project or you don't expect to finish it until late next year it's a good idea to make it hybrid and when Unity publish their systems one by one, you can drop the corresponding hybrid tool and change it easily to pure ECS.
    It's more work to adapt a full blown OOD-d application to DOD than to plan it beforehand and use some helper tools (hybrid monobehaviour tools) to run it on ECS.

    You may not see immediate performance gains (or just some marginal), but you will when Unity finishes its work on various parts of the Unity universe.
    BTW, also you can write your own stuff if you're adventurous. :)
     
    dgoyette likes this.
  11. dgoyette

    dgoyette

    Joined:
    Jul 1, 2016
    Posts:
    4,113
    That's an interesting point. I was looking at Hybrid ECS as a way to achieve performance gains in a mostly MonoBehaviour-based project, but that's an interesting way to look at it; as a way to make it easier to go full ECS in the future.