I’m currently learning DOTS and doing some experiments to get a feeling for the performance behavior with different numbers of entities and different layouts for components. I’m using Unity 2019.4.5, Entities 0.11.1.
In my current experiment, I create 1.000.000 entities and have a system that checks whether each entity (or the one component it has) is currently active by checking Time.ElapsedTime against a time interval stored in the component.
In the EntityDebugger and Systems pane, with 1 million entities, that system takes about 0.02ms. So, to put some “load” into system, I wrapped the actual code into a loop. Doing it 100 times changed almost nothing (still about 0.02ms). At 1000, it suddenly went up to 20ms. 500 had 10ms. So far, so good, even if going from 100 to 500 seems odd (0.02ms to 10ms) but probably something “breaks” there between 100 and 500, and then it’s linear from 500 to 1000. My guess would be that this is loop vectorization being possible at 100 but no more at 500 (that’s apparently not it, though).
Then, I realized I had a bug and replaced an if-statement with a bool assignment. Time went back down to 0.02ms (still with the 500 loop, and even when I increased that loop to 1000 iterations).
So I thought “hm, okay, branching in burst-compiled code is very bad for performance”. Except when I added the if-statement back in, time stayed at 0.02ms. Removed the bool assignment (but kept the if-statement), and BOOM, back to 20ms.
That’s factor 1000 worse performance by removing a simple assignment.
Turns out when I include gem.SpawnOrDestroyThisFrame = gem.IsActive != nowActive;
I get only one “loop not vectorized” warning in the Burst Inspector, on the line of the for-statement. When I remove it, I get two of these messages, both on the line of the if-statement. Changing the number of iterations (100, 500, 1000) doesn’t make a difference in that regard.
Now, I could probably spend the next two weeks trying to figure out all the possibilities but … is there some documentation that explains what kind of statements / coding constructs have which performance impact? I did find Burst User Guide but from reading that, I don’t understand why adding this line improves performance / changes vectorization like it does. I even tried assigning gem.IsActive != nowActive
to a temporary bool that I then use in the if-statement but that doesn’t seem to change anything.
Here’s the code of the system:
protected override void OnUpdate() {
double time = Time.ElapsedTime;
Entities
.ForEach((ref GameplayEventMovement gem) => {
for (int i = 0; i < 1000; i++) {
gem.CurrentTime = time;
bool nowActive = gem.SpawnTime < time && time < gem.DestroyTime;
// *removing* this => performance 1000 times *worse*
gem.SpawnOrDestroyThisFrame = gem.IsActive != nowActive;
// keeping or removing this makes no difference, if above statement is present
if (gem.IsActive != nowActive) {
gem.SpawnOrDestroyThisFrame = true;
}
gem.IsActive = nowActive;
}
})
.WithName("CheckGameplayEventActivityJob")
.ScheduleParallel();
}