Burst Bench
A Burst-compiled job: console burst.bench times it against the same loop in plain C# and says whether Burst ran it.
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>