Unsplash

One chunk can hold multiple commands. The most efficient (performance wise) is to allocate large enough chunk that it does not expand when you continue to add commands before it can playback. You will save this memory expanding cost.

On your ECB you can set MinimumChunkSize which is 4 * 1024 bytes by default (preview 21).

Commands like create, destroy, and instantiate the size is fixed. But variable sized command is the command with payload like Add or Set, in that case the command has to hold what you want to set from threads back to main thread.

Let’s take a look at size of each commands

EntityArchetype : should be 1 pointer = 4

BasicCommand : 4 * 3 = 12

Entity : 4 + 4 = 8 since it contains index and version

Then we have

  • Create commands : 12 + 4 + 4 = 20
    This command needs nothing!
  • Entity commands : 12 + 8 + 4 = 24
    This is such as instantiate and destroy, it needs an entity to do work.
  • Commands with type : 24 + 4 + 4 = 32
    For Remove component command you save the payload, so that’s just 32.
  • Commands with payload : 32 + ???
    Add or set, you have to pay for the size of your struct anyways. You cannot do “empty add” (preview 21)
  • Buffer commands : 24 + 4 + 4 +(4 * 3) for buffer header = 44
  • Shared commands : 24 + 4 + 4 + 4 + 4 = 40

So you can think just this : will it go over 4096 before it can playback? If it is, increase minimum chunk size.

You can also decrease minimum chunk size for memory optimization, but I think such a small memory is not worth it. You may add more commands in the future and forgot to increase back the chunk size and that will cost performance. (Also if it is too small to fit even 1 command, it will allocate a chunk equal to that command’s size in order to not hard crash.)

Here are some rough numbers

Just create or destroy : about 170 commands.

Add/set commands with payload : Typical IComponentData may sized average around 5 integers? In that case you got about 80 commands.

So you can think like : “This job must create around 500 entities so the size should be around 4096 * 3”

Or just calculate it based on your job’s need :

4096 / (32 * sizeof(YourThing))

Or cache the calculated chunk size with simple method like :

private const int noPayloadSize = 24;
private const int withPayloadSize = 32;
private const int multipleOf = 1024;

public static int CalculateMinimumChunkSize(int createInstantiateDestroy, int remove, params (int sizeOf, int count)[] addSet)
{
    var exactSize =
    (createInstantiateDestroy * noPayloadSize) +
    (remove * withPayloadSize) +
    addSet.Sum(x => (withPayloadSize + x.sizeOf) * x.count);

    return (int)(math.ceil(exactSize / (float)multipleOf) * multipleOf);
}

Currently you must stare at your code to estimate command count at barriers. I hope Entity Debugger could display some useful information at the barrier in the future.

ps. For SharedComponentData addition with ECB it seems to not care about your payload size since it box that object and just make it a pointer and a hash.