Best way to get rid of GetComponent() inside a job?

I’m curious if any DOTS experts have any thoughts on this.

I have a simple job that has to do several GetComponents() on a few different entity’s inside a job Which I’m pretty sure is not optimal. And it likely would not be good if I want it to run on roughly 100,000 entity’s max(just as a rough estimate). I can see a few different ways of doing it.

  1. I can prepackage the data beforehand into a nativearrays in another job just before.

  2. I could format the original data in a better way. The below is how its currently formatted. Hence why I have to use GetComponent().

public struct StatsonObject : IComponentData
{
    public Entity HPEnt;
    public Entity SPEnt;
    public Entity ATKEnt;
    public Entity DEFEnt;
    public Entity INTEnt;
    public Entity RESEnt;
    public Entity HITEnt;
    public Entity SPDEnt;
   

}

I’m tempted to change the datha around another way instead of having the above componentdata on the main enity. I would have the separate entiity’s refer back to the main entity with a uniqueID. Like.

public struct HPSData : Icompontentdata
{
        public int HPVal;
        public int  MainEntUniqueID

}
  1. I could maybe not use a job at all perhaps, maybe it would be fine just as is.

Or maybe there is another way I’m not thinking of currently. I’m curious what anyone’s opinion is on the matter.

This probably won’t help you. Randomly accessing an array isn’t much cheaper than CDFE.

Don’t know about the ID thing, but accessing data as a many-to-one instead of a one-to-many increases the chance of your lookups being in cache, especially if the data is small.

Aren’t you already using a job? Why do you think it would be faster to move away from that?

You are not considering the logical transform requirements on your data, nor do you have any profiling information. These are both essential before optimizing your data structures.

2 Likes

Can you expand on that?

Thanks for reply. Yeah, I guess the only way to find out is to test it. It seems the best approach is to figure out the system first before the data because I always end up changing the data several times anyway.

Getcomponent is definitely bad though.

It’s on par with hash lookups. It’s just not as fast as the fastest possible solution when you use linear access. But that’s absolute best case and not everything, slightly slower is “definitely bad”.

3 Likes

Sure.

First, if I were to ask you which is better: an array or a binary tree, and gave you no further context, what would you answer?

And second, there’s a big difference between asking:
“I was told my code is probably slow because it uses GetComponent a lot. How do I make it faster?”
and asking:
“This code takes 5 milliseconds to execute. And that seems too long. I suspect the GetComponent calls are the problem. How do I make it faster?”

That depends on what you are doing, which it sounds like you yourself don’t even know yet (much less us).

2 Likes

GetComponent in Entities.ForEach is completely different than GetComponent in MonoBehaviour world (context matters).

GetComponent as in Entities.ForEach gets replaced with GetComponentFromEntity (CDFE for scheduled jobs, EntityManager.GetComponent with .Run).

CDFE is pretty much a lookup access with a bit of caveats (use readonly access, and you’re blazing fast, less job stalls). Entities already act as "int id"s. Difference is how much memory is spent when those id’s are stored inside components.

But from my perspective, CDFE usually tend to be faster than manually creating lookups inside of systems.

GetComponent in MonoBehaviour is a native call to the lookup. They have different performance speed implications. TryGetComponent for MonoBehaviours are preferable due to no GC in editor.
Note that both are quite fa.

Speed wise if both worlds are compared, from fastest to slowest:

CDFE > TryGetComponent (due to editor no GC) > GetComponent > GetComponentInChildren > GetComponentInParent > FindObjectWithTag > FindObjectOfType

TL;DR:
If solution requires random access, you’d have to have random access somewhere eventually.
So if you don’t want to have random access - move data around. Store required data locally.
Or better yet… profile first to figure out whether its even worth doing.

I’d say - profile it first. If its not going to be a problem - don’t make it one.