Tutorial:Optimizing a data pack

This tutorial page is a work in progress.
 
Please help expand and improve it. The talk page may contain suggestions.
This tutorial is exclusive to Java Edition.
 

This tutorial shows how to make commands or a data pack performant, including best practices and methods to profile a data pack.

Best practices

Minecraft is a complex game. There are no rules that always apply and running commands can behave unexpectedly. When you are not sure which optimization to make, try both implementations and § Profiling.

Minimize commands running

This seems like an obvious rule, but it is one of the most effective ways to optimize commands.

One way of achieving this is by not running all commands every tick. Sometimes you can get away with running non-critical commands every N ticks.

Minimize NBT operations

Accessing or modifying NBT, especially on players is really expensive. This is because the game is essentially saving the entity to the format on disk, doing operations on that data structure, and then (in the case of modifying NBT) reading it back in to create an entirely new entity.

- execute as @a[nbt={SelectedItem:{id:"minecraft:apple"}}] run ...
+ execute if items entity @s weapon.mainhand apple run ...

When modifying items, use item modifiers instead of editing the NBT.

- data modify entity @s Item.count set value 10
+ item modify entity @s contents {function:"set_count",count:10}

Use predicates when you can.

- execute as @a[nbt={RootVehicle:{id:"minecraft:pig"}}] run ...
+ execute as @a[predicate=example:riding_pig] run ...

example:riding_pig predicate:

+ {
+   "condition": "minecraft:entity_properties",
+   "entity": "this",
+   "predicate": {
+     "vehicle": {
+       "type": "minecraft:pig"
+     }
+   }
+ }

Minimize execute subcommands

- execute as @a[tag=hider] run effect give @s glowing
+ effect give @a[tag=hider] glowing
- execute as @a[tag=hider] if score @s timer matches 0.. run ...
+ execute as @a[tag=hider,scores={timer=0..}] run ...
- execute run say hi
+ say hi

Include type checks in selectors

Always include a type= argument in selectors unless you intend to select all entity types. This allows the game to very cheaply filter out the entities that you don't want.

- execute as @e[tag=special_altar] run ...
+ execute as @e[type=marker,tag=special_altar] run ...

Minimize @e selectors

Finding all matching entities in the world is expensive. If you have many repeated or similar selectors, consider combining them into a one and branching to a separate function where you will be able to use @s to target the entity cheaply.

Perferred § Minimize execute subcommands and § Include type checks in selectors rather than putting all conditions inside the @e selector, as this helps reduce the number of commands executed.

- execute as @e[type=item] if items entity @s contents apple run ...
- execute as @e[type=item] if items entity @s contents cobblestone run ...

+ execute as @e[type=item] run function example:process_item

example:process_item function:

+ execute if items entity @s contents apple run ...
+ execute if items entity @s contents cobblestone run ...

Minimize unnecessary macros

Calling a macro function has an overhead. Consider using them only when strictly necessary.

- $scoreboard players set @s example $(Age)
+ execute store result score @s example run data get entity @s Age

Used the cache for macros

Minecraft stores the last 8 parsed functions containing macro function.

So, if your macros have only 16 possible inputs, consider creating 2 functions with 8 possible inputs each to significantly improve performance.

Alternative practices

The following practices carry a risk of misuse or offer limited optimization benefits.

Check player distance

If a performance-heavy feature is only effective near players, consider adding a distance= parameter.

- execute as @e[type=item] run ...
+ execute as @e[type=item] if entity @p[distance=..24] run ...

If there are many entities (or if entities are selected multiple times), matching distance can be costly. In this case, you can first use the /tag command to tag entities around players, and then select them using the tag= for execution. You only need to tag once and can use it in multiple places.

+ execute at @a run function optimize:tagging_active_entity
- execute as @e run ...
- execute as @e[type=item,tag=custom_item] run ...
+ execute as @e[tag=active.64] run ...
+ execute as @e[type=item,tag=active.64,tag=custom_item] run ...

The optimize:tagging_active_entity function:

tag @e[tag=!active.64,distance=..64] add active.64
tag @e[tag=active.64,distance=64..] remove active.64

Use periodic_tick entity predicate

If the effect of commands running in a cycle is not ideal (e.g., playing sounds simultaneously on entities around a player, causing sound overlap), you can try using periodic_tick from the Predicates § Entity predicate format.

This performs worse than /schedule because the target selector still runs, but it checks if the entity's appearance time is a multiple of periodic_tick.

Use return run to end functions

If after matching one condition, subsequent conditions cannot occur, you can use /return run to end the current function. If there is no logical sequence, place the more likely conditions first.

Note:

  • This method should be used in a separate function that handles various branch conditions.
  • If there are multiple command branches (e.g., selecting multiple entities with /execute as @e), /return run will only execute the first branch.
- execute if predicate some:1 run function some:1
- execute if predicate some:2 at @s run function some:2
- execute if predicate some:3 in minecraft:overworld run function some:3
+ execute if predicate some:1 run return run function some:1
+ execute if predicate some:2 run return run execute at @s run function some:2
+ execute if predicate some:3 run return run execute in minecraft:overworld run function some:3

Use advancements on players

If you are matching and running commands on players, using advancements can avoid the performance cost of using the @a target selector.

Set the trigger conditions in the advancement and add the commands to run at the end of the function.

example:player/score advancement:

{
  "criteria": {
    "example": {
      "trigger": "minecraft:tick",
      "conditions": {
        "player": [
          {
            "condition": "minecraft:entity_scores",
            "entity": "this",
            "scores": {
              "example.score": {
                "min": 1
              }
            }
          }
        ]
      }
    }
  },
  "rewards": {
    "function": "example:triggered"
  }
}

example:triggered function:

advancement revoke @s only example:player/score

Profiling

Use /perf on a dedicated server, or use F3 + L on a locally hosted world cauld starts profiling and get server performance data.

After profiling stopped, open <server directory root>/debug/profiling/yyyy-MM-dd_HH.mm.ss-<world name>-<game version>.zip (can also access from chat), unzip server/profiling.txt.

Inside of it, the commandFunctionsunder tick and scheduledFunctions under tick/levels/ServerLevel[<world name>] <dimension ID>/tick are the function performance data.

External links

Navigation