#System failed to completely rest at 0 ms

1 messages · Page 1 of 1 (latest)

shy dew
#

Currently at latest dots 1.0.0-pre.15 release, it still not possible to make System completely rest at 0 ms when there is query that is essential to performance critical mobile platform like Android. Maybe there's more blocking system from resting at 0 ms but from what I see it's caused by when u use SystemAPI.GetSingleton API u will add query into System that will add burden into System. So meaning that most of time Systen will use Entities built-in singleton like BeginSimulationEntityCommandBufferSystem.Singleton, EndSimulationEntityCommandBufferSystem.Singleton, PhysicsWorldSingleton, NetworkTime and etc that make System never able to get 0ms. I think need to make SystemAPI.GetSingleton API not add query into System to further improve System performance.

I think focus on improving ISystem performance instead of SystemBase performance near to 0 ms as much as possible when there's no query should be ok. You can easily see this when ur system is at PredicatedSimulationSystemGroup when using dots netcode.

outer galleon
#

The Cost of a System should be 0ms, unless you add [RequireMatchingQueriesForUpdate] is this the cost you're talking about? think

shy dew
outer galleon
#

In which case, it won't run anything in your System.ShouldRun - that should be 0ms, how does your OnCreate look? think

shy dew
#

I think if u just use Entities without dots netcode it's not easy to see this issue

#

When u use dots netcode and ur system at PredicatedSimulationSystemGroup then u can see this issue

outer galleon
#

Could you share an example of one such system? think

shy dew
#

@outer galleon Alright. Here it's. Hopefully u are able to repro it at ur side.

using Unity.Entities;
using Unity.Mathematics;
using Unity.NetCode;
using Unity.Transforms;
using Unity.Burst;

[UpdateInGroup(typeof(PredictedSimulationSystemGroup))]
[BurstCompile]
public partial struct MoveCubeSystem : ISystem
{
    [BurstCompile]
    public void OnCreate(ref SystemState state) { }

    [BurstCompile]
    public void OnDestroy(ref SystemState state) { }

    [BurstCompile]
    public void OnUpdate(ref SystemState state)
    {
        foreach (var (trans,playerInput)
                 in SystemAPI.Query<RefRW<Translation>, RefRO<CubeInput>>()
                     .WithAll<NetCubeSpawner>())
        {
            var predictingTick = SystemAPI.GetSingleton<NetworkTime>().ServerTick;
            var ecb = SystemAPI.GetSingleton<EndSimulationEntityCommandBufferSystem.Singleton>().CreateCommandBuffer(state.WorldUnmanaged);

            var moveInput = new float2(playerInput.ValueRO.Horizontal, playerInput.ValueRO.Vertical);
            moveInput = math.normalizesafe(moveInput) * SystemAPI.Time.DeltaTime * 4;
            trans.ValueRW.Value += new float3(moveInput.x, 0, moveInput.y);
        }

    }
}
outer galleon
#

Well, that makes sense, how would it idle? It obvoiusly has to run check the SystemAPI.Query<RefRW<Translation>, RefRO<CubeInput>>().WithAll<NetCubeSpawner>() query for any matches.. That is not a zero cost think

#

This is exactly the kind of cost you pay by splitting into more systems. You could help it by controlling the systems' OnUpdate yourself inside a SystemGroup

shy dew
outer galleon
#

But it doesn't mean that the query generated by your GetSingleton is the cost you're seeing. As that part is cached in OnCreate, so you only pay the cost once.

But okay, so PredictedSimulationSystemGroup is slow? What kind of system performance improvements are you then looking for, any calls in particular? think

shy dew
royal sequoia
#

The only way for a system to take 0 ms each frame is if it is disabled. If you have a System that is active, it has to execute some code and that will take a non-zero amount of time.

There are strategies you can take to minimize the overhead of updating systems, and to minimize the cost of running the system itself. These depend on the nature of the system, so you'll have to look at each one yourself and evaluate which are applicable.

#

If the system uses a query that will not be matched every frame, you can use RequireForUpdate.

For the above example, if a NetCubeSpawner only exists in the World for a limited number of frames, you can write:

[BurstCompile]
void OnCreate(ref SystemState state)
{
    var query = new EntityQueryBuilder(Allocator.Temp)
        .WithAll<CubeInput, NetCubeSpawner>()
        .WithAllRW<Translation>()
        .Build(ref state);
    state.RequireForUpdate(query);
}

With this, we check whether or not there are any entities at all that could satisfy this query (specifically using EntityQuery.IsEmptyIgnoreFilter) and if not, we don't call OnUpdate on the system at all. This can be faster than doing the full query in OnUpdate only for it to be empty.

It's even more important to do this if your system does work in OnUpdate in addition to the query. For example if you're doing an EntityQuery.CalculateEntityCount() and allocating a NativeArray based on that count prior to an Entities.ForEach or a SystemAPI.Query, you'd want to make sure you call RequireForUpdate with the query to avoid that work in the case the query will be empty.

https://docs.unity3d.com/Packages/com.unity.entities@1.0/api/Unity.Entities.ComponentSystemBase.RequireForUpdate.html

#

If the system queries components that do not change often, you can use a Change Filter on your query.

For the above example, if you have many entities that would match the query, but you don't expect CubeInput to change every frame, you can write:

foreach (var (trans, playerInput)
         in SystemAPI.Query<RefRW<Translation>, RefRO<CubeInput>>()
             .WithAll<NetCubeSpawner>()
             .WithChangeFilter<RefRO<CubeInput>>())
{
...
}

This query will only iterate over entities in a chunk where CubeInput has been written to since the last OnUpdate.

Change Filters are most effective when you have a lot of entities that would match the query, or a fairly costly operation for each of them, and you know that the inputs are not often changed each frame. Note that each individual entity returned by the query may not have had its component changed, but it will belong to a chunk in which at least one of the components has changed.

https://docs.unity3d.com/Packages/com.unity.entities@1.0/manual/systems-entityquery-filters.html#use-a-change-filter

#

If the system has to do work every frame and there's no way around it, then the only thing you can do is make sure that work you do in OnUpdate is as small and as fast as possible.

shy dew
royal sequoia
#

In general, I prefer RequireForUpdate because it is explicit. You know what queries or components you are requiring.

[RequireMatchingQueriesForUpdate] looks at all the queries used by the system, and will call OnUpdate if any query is non-empty, which includes any queries create from calls to {Get|Set|Has}Singleton.

In your MoveCubeSystem example, this will include the query source-generated for SystemAPI.GetSingleton<NetworkTime>(), and since NetworkTime always exists, your system will end up updating every frame, even if the SystemAPI.Query is empty. That's why I recommend being explicit with RequireForUpdate.

#

If you only have a single SystemAPI.Query or Entities.ForEach in your system, then [RequireMatchingQueriesForUpdate] can be convenient. But as soon as singletons are involved, you should use RequireForUpdate.

shy dew
shy dew
royal sequoia
#

Well, you have to access the singleton data somehow, and using a query ensures the same safety and automatic dependency tracking as any other ComponentData, so that you can be sure you're not writing to the singleton data at the same time you're reading from it.

There's a bit more discussion of how that works in another thread: #1050476144782233671 message

shy dew