Modding

Burst Bench

A Burst-compiled job: console burst.bench times it against the same loop in plain C# and says whether Burst ran it.

Updated 26 Oct 2026

A Burst-compiled job: console burst.bench times it against the same loop in plain C# and says whether Burst ran it.

Mod id example.burstbench, side client.

A code mod: build it with dotnet build (see Making mods) and put the output folder in the game's Mods folder.

mod.json

{
    "id": "example.burstbench",
    "name": "Burst Bench",
    "version": "1.0",
    "author": "Seeds & Circuits",
    "side": "client",
    "description": "A Burst-compiled job: console burst.bench times it against the same loop in plain C# and says whether Burst ran it."
}

BurstBench.cs

using System.Diagnostics;
using SeedsAndCircuits.DebugConsole;
using SeedsAndCircuits.Mods;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;

namespace BurstBench
{
    /// <summary>
    /// A [BurstCompile] job in a mod. The Mod Kit (and build.ps1) also compile it to native code
    /// (Burst/lib_burst_mod.dll), which the game loads before OnLoad, so it runs as fast as the game's own jobs.
    /// Console: burst.bench times it against the same loop in plain C#.
    /// </summary>
    public sealed class BurstBench : Mod
    {
        const int Count = 1 << 20;

        public override void OnLoad() => Log(Bench());

        [ConsoleCommandAttribute("burst.bench", "Times a Burst job against the same loop in plain C#.", Local = true)]
        static string Bench()
        {
            var values = new NativeArray<float>(Count, Allocator.TempJob);
            var flag = new NativeArray<int>(1, Allocator.TempJob);
            try
            {
                var job = new NoiseJob { Values = values, IsBurst = flag };
                job.Schedule().Complete(); // first run: warm up (and, without a native library, Burst would compile here)
                var watch = Stopwatch.StartNew();
                job.Schedule().Complete();
                double burstMs = watch.Elapsed.TotalMilliseconds;
                bool burst = flag[0] == 1;

                // The same work as plain C# (what a job does without Burst).
                watch.Restart();
                job.Execute();
                double managedMs = watch.Elapsed.TotalMilliseconds;
                return $"Job: {burstMs:0.00} ms ({(burst ? "Burst" : "not Burst")}), plain C#: {managedMs:0.00} ms, " +
                       $"{managedMs / System.Math.Max(burstMs, 0.001):0.0}x. Checksum {values[Count / 2]:0.000}";
            }
            finally
            {
                values.Dispose();
                flag.Dispose();
            }
        }

        [BurstCompile(FloatMode = FloatMode.Fast)]
        struct NoiseJob : IJob
        {
            public NativeArray<float> Values;
            public NativeArray<int> IsBurst;

            public void Execute()
            {
                int burst = 1;
                NotBurst(ref burst);
                IsBurst[0] = burst;
                for (int i = 0; i < Values.Length; i++)
                {
                    float2 p = new float2(i % 1024, i / 1024) * 0.05f;
                    float v = 0f, amp = 0.5f;
                    for (int octave = 0; octave < 4; octave++)
                    {
                        v += noise.snoise(p) * amp;
                        p *= 2f;
                        amp *= 0.5f;
                    }
                    Values[i] = v;
                }
            }

            // Burst leaves [BurstDiscard] methods out: this only runs when the job runs as plain C#.
            [BurstDiscard]
            static void NotBurst(ref int burst) => burst = 0;
        }
    }
}

BurstBench.csproj

<Project Sdk="Microsoft.NET.Sdk">
  <!-- Build settings and game references: ../Mod.props. Output: bin/Burst Bench/ (+ Burst/lib_burst_mod.dll) -->
  <PropertyGroup>
    <AssemblyName>BurstBench</AssemblyName>
    <ModFolder>Burst Bench</ModFolder>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
    <!-- Also compile the [BurstCompile] jobs to native code (Mod.props: needs Unity installed). -->
    <ModBurst>true</ModBurst>
  </PropertyGroup>
  <Import Project="../Mod.props" />
</Project>