Skip to content

Instantly share code, notes, and snippets.

@stonstad
Last active March 17, 2026 15:09
Show Gist options
  • Select an option

  • Save stonstad/0a3da869099ef1eb22bb50268aaa1d9f to your computer and use it in GitHub Desktop.

Select an option

Save stonstad/0a3da869099ef1eb22bb50268aaa1d9f to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Text.Json;
using NUnit.Framework;
using Unity.PerformanceTesting;
using UnityEngine;
public class JsonSerializationPerformanceTests
{
private const int ItemCount = 1000;
private TestPayload _payload;
private string _unityJson;
private string _systemTextJson;
private JsonSerializerOptions _systemTextJsonOptions;
[Serializable]
public class TestPayload
{
public int id;
public string name;
public float score;
public bool isActive;
public long createdAtTicks;
public NestedData nested;
public List<ItemData> items;
}
[Serializable]
public class NestedData
{
public int level;
public string description;
public Vector3Data position;
}
[Serializable]
public class Vector3Data
{
public float x;
public float y;
public float z;
}
[Serializable]
public class ItemData
{
public int index;
public string label;
public float value;
public bool enabled;
}
[SetUp]
public void SetUp()
{
_payload = BuildPayload(ItemCount);
_systemTextJsonOptions = new JsonSerializerOptions
{
IncludeFields = true,
WriteIndented = false
};
// Warm-up / baseline cached data
_unityJson = JsonUtility.ToJson(_payload);
_systemTextJson = JsonSerializer.Serialize(_payload, _systemTextJsonOptions);
// Sanity checks so the benchmark doesn't run against broken setup
Assert.IsFalse(string.IsNullOrEmpty(_unityJson));
Assert.IsFalse(string.IsNullOrEmpty(_systemTextJson));
}
[Test, Performance]
public void Unity_JsonUtility_Serialize()
{
Measure.Method(() =>
{
var json = JsonUtility.ToJson(_payload);
GC.KeepAlive(json);
})
.WarmupCount(10)
.MeasurementCount(30)
.IterationsPerMeasurement(100)
.GC()
.Run();
}
[Test, Performance]
public void SystemTextJson_Serialize()
{
Measure.Method(() =>
{
var json = JsonSerializer.Serialize(_payload, _systemTextJsonOptions);
GC.KeepAlive(json);
})
.WarmupCount(10)
.MeasurementCount(30)
.IterationsPerMeasurement(100)
.GC()
.Run();
}
[Test, Performance]
public void Unity_JsonUtility_Deserialize()
{
Measure.Method(() =>
{
var obj = JsonUtility.FromJson<TestPayload>(_unityJson);
GC.KeepAlive(obj);
})
.WarmupCount(10)
.MeasurementCount(30)
.IterationsPerMeasurement(100)
.GC()
.Run();
}
[Test, Performance]
public void SystemTextJson_Deserialize()
{
Measure.Method(() =>
{
var obj = JsonSerializer.Deserialize<TestPayload>(_systemTextJson, _systemTextJsonOptions);
GC.KeepAlive(obj);
})
.WarmupCount(10)
.MeasurementCount(30)
.IterationsPerMeasurement(100)
.GC()
.Run();
}
[Test]
public void SanityCheck_BothSerializers_CreateUsableJson()
{
Debug.Log($"Unity JsonUtility length: {_unityJson.Length}");
Debug.Log($"System.Text.Json length: {_systemTextJson.Length}");
var unityObj = JsonUtility.FromJson<TestPayload>(_unityJson);
var systemObj = JsonSerializer.Deserialize<TestPayload>(_systemTextJson, _systemTextJsonOptions);
Assert.NotNull(unityObj);
Assert.NotNull(systemObj);
Assert.AreEqual(_payload.id, unityObj.id);
Assert.AreEqual(_payload.id, systemObj.id);
Assert.AreEqual(_payload.items.Count, unityObj.items.Count);
Assert.AreEqual(_payload.items.Count, systemObj.items.Count);
}
private static TestPayload BuildPayload(int itemCount)
{
var payload = new TestPayload
{
id = 12345,
name = "Benchmark Payload",
score = 98.76f,
isActive = true,
createdAtTicks = DateTime.UtcNow.Ticks,
nested = new NestedData
{
level = 7,
description = "Nested benchmark object",
position = new Vector3Data
{
x = 12.34f,
y = 56.78f,
z = 90.12f
}
},
items = new List<ItemData>(itemCount)
};
for (int i = 0; i < itemCount; i++)
{
payload.items.Add(new ItemData
{
index = i,
label = $"Item_{i}",
value = i * 1.2345f,
enabled = (i % 2) == 0
});
}
return payload;
}
}
{
"TestSuite": "Playmode",
"Date": 1773759692576,
"Player": {
"Development": true,
"ScreenWidth": 5120,
"ScreenHeight": 2160,
"ScreenRefreshRate": 60,
"Fullscreen": false,
"Vsync": 1,
"AntiAliasing": 2,
"Batchmode": false,
"RenderThreadingMode": "MultiThreaded",
"MtRendering": true,
"GraphicsJobs": false,
"GpuSkinning": false,
"Platform": "WindowsEditor",
"ColorSpace": "Linear",
"AnisotropicFiltering": "ForceEnable",
"BlendWeights": "Unlimited",
"GraphicsApi": "Direct3D12",
"ScriptingBackend": "IL2CPP",
"AndroidTargetSdkVersion": "AndroidApiLevelAuto",
"AndroidBuildSystem": "Gradle",
"BuildTarget": "StandaloneWindows64",
"StereoRenderingPath": "MultiPass"
},
"Hardware": {
"OperatingSystem": "Windows 11 (10.0.26200)",
"DeviceModel": "System Product Name (ASUS)",
"DeviceName": "SHAUN-PC",
"ProcessorType": "AMD Ryzen 9 7950X 16-Core Processor ",
"ProcessorCount": 32,
"GraphicsDeviceName": "NVIDIA GeForce RTX 4090",
"SystemMemorySizeMB": 64654
},
"Editor": {
"Version": "6000.3.10f1",
"Branch": "6000.3/staging",
"Changeset": "e35f0c77bd8e",
"Date": 1771318112
},
"Dependencies": [
"com.unity.collab-proxy@2.11.3",
"com.unity.feature.2d@2.0.2",
"com.unity.ide.rider@3.0.39",
"com.unity.ide.visualstudio@2.0.26",
"com.unity.inputsystem@1.18.0",
"com.unity.test-framework@1.6.0",
"com.unity.test-framework.performance@3.2.1",
"com.unity.timeline@1.8.10",
"com.unity.ugui@2.0.0",
"com.unity.ui.test-framework@6.3.0",
"com.unity.visualscripting@1.9.9",
"org.nuget.system.text.json@10.0.5",
"com.unity.modules.accessibility@1.0.0",
"com.unity.modules.adaptiveperformance@1.0.0",
"com.unity.modules.ai@1.0.0",
"com.unity.modules.androidjni@1.0.0",
"com.unity.modules.animation@1.0.0",
"com.unity.modules.assetbundle@1.0.0",
"com.unity.modules.audio@1.0.0",
"com.unity.modules.cloth@1.0.0",
"com.unity.modules.director@1.0.0",
"com.unity.modules.imageconversion@1.0.0",
"com.unity.modules.imgui@1.0.0",
"com.unity.modules.jsonserialize@1.0.0",
"com.unity.modules.particlesystem@1.0.0",
"com.unity.modules.physics@1.0.0",
"com.unity.modules.physics2d@1.0.0",
"com.unity.modules.screencapture@1.0.0",
"com.unity.modules.terrain@1.0.0",
"com.unity.modules.terrainphysics@1.0.0",
"com.unity.modules.tilemap@1.0.0",
"com.unity.modules.ui@1.0.0",
"com.unity.modules.uielements@1.0.0",
"com.unity.modules.umbra@1.0.0",
"com.unity.modules.unityanalytics@1.0.0",
"com.unity.modules.unitywebrequest@1.0.0",
"com.unity.modules.unitywebrequestassetbundle@1.0.0",
"com.unity.modules.unitywebrequestaudio@1.0.0",
"com.unity.modules.unitywebrequesttexture@1.0.0",
"com.unity.modules.unitywebrequestwww@1.0.0",
"com.unity.modules.vectorgraphics@1.0.0",
"com.unity.modules.vehicles@1.0.0",
"com.unity.modules.video@1.0.0",
"com.unity.modules.vr@1.0.0",
"com.unity.modules.wind@1.0.0",
"com.unity.modules.xr@1.0.0",
"com.unity.modules.subsystems@1.0.0",
"com.unity.modules.hierarchycore@1.0.0",
"org.nuget.microsoft.bcl.asyncinterfaces@10.0.5",
"org.nuget.system.io.pipelines@10.0.5",
"org.nuget.system.text.encodings.web@10.0.5",
"org.nuget.system.buffers@4.6.1",
"org.nuget.system.memory@4.6.3",
"org.nuget.system.runtime.compilerservices.unsafe@6.1.2",
"org.nuget.system.threading.tasks.extensions@4.6.3",
"com.unity.ext.nunit@2.0.5",
"com.unity.2d.animation@13.0.4",
"com.unity.2d.pixel-perfect@5.1.1",
"com.unity.2d.psdimporter@12.0.1",
"com.unity.2d.sprite@1.0.0",
"com.unity.2d.spriteshape@13.0.0",
"com.unity.2d.tilemap@1.0.0",
"com.unity.2d.tilemap.extras@6.0.1",
"com.unity.2d.aseprite@3.0.1",
"com.unity.2d.tooling@1.0.2",
"org.nuget.system.numerics.vectors@4.6.1",
"com.unity.2d.common@12.0.2",
"com.unity.mathematics@1.3.3",
"com.unity.collections@2.6.2",
"com.unity.burst@1.8.28",
"com.unity.nuget.mono-cecil@1.11.6"
],
"Results": [
{
"Name": "JsonSerializationPerformanceTests.SystemTextJson_Deserialize",
"ClassName": "JsonSerializationPerformanceTests",
"MethodName": "SystemTextJson_Deserialize",
"Version": "1",
"Categories": [
"Performance"
],
"SampleGroups": [
{
"Name": "Time.GC()",
"Unit": 8,
"IncreaseIsBetter": false,
"Samples": [
6029.0,
6029.0,
6029.0,
6029.0,
6029.0,
6029.0,
6029.0,
6029.0,
6029.0,
6029.0,
6029.0,
6029.0,
6029.0,
6029.0,
6029.0,
6029.0,
6029.0,
6029.0,
6029.0,
6029.0,
6029.0,
6029.0,
6029.0,
6029.0,
6029.0,
6029.0,
6029.0,
6029.0,
6029.0,
6029.0
],
"Min": 6029.0,
"Max": 6029.0,
"Median": 6029.0,
"Average": 6029.0,
"StandardDeviation": 0.0,
"Sum": 180870.0
},
{
"Name": "Time",
"Unit": 2,
"IncreaseIsBetter": false,
"Samples": [
219.69190000000004,
219.6502999999998,
221.1895999999997,
226.79500000000008,
257.2282999999998,
222.4369999999999,
223.0891999999999,
222.4702000000002,
221.9940999999999,
262.0772999999999,
222.58780000000025,
222.70150000000013,
221.9795999999997,
221.5095999999994,
264.5603000000001,
220.4915000000001,
220.4309999999996,
221.09439999999996,
221.44920000000006,
257.0628999999999,
374.58440000000067,
254.76310000000013,
219.9979000000003,
219.84159999999978,
219.47419999999969,
219.39170000000017,
261.3117999999995,
220.52080000000024,
223.61709999999949,
223.89670000000116
],
"Min": 219.39170000000017,
"Max": 374.58440000000067,
"Median": 222.2155499999999,
"Average": 234.263,
"StandardDeviation": 30.194203124849513,
"Sum": 7027.889999999999
}
]
},
{
"Name": "JsonSerializationPerformanceTests.SystemTextJson_Serialize",
"ClassName": "JsonSerializationPerformanceTests",
"MethodName": "SystemTextJson_Serialize",
"Version": "1",
"Categories": [
"Performance"
],
"SampleGroups": [
{
"Name": "Time.GC()",
"Unit": 8,
"IncreaseIsBetter": false,
"Samples": [
5018.0,
5018.0,
5018.0,
5018.0,
5018.0,
5018.0,
5018.0,
5018.0,
5018.0,
5018.0,
5018.0,
5018.0,
5018.0,
5018.0,
5018.0,
5018.0,
5018.0,
5018.0,
5018.0,
5018.0,
5018.0,
5018.0,
5018.0,
5018.0,
5018.0,
5018.0,
5018.0,
5018.0,
5018.0,
5018.0
],
"Min": 5018.0,
"Max": 5018.0,
"Median": 5018.0,
"Average": 5018.0,
"StandardDeviation": 0.0,
"Sum": 150540.0
},
{
"Name": "Time",
"Unit": 2,
"IncreaseIsBetter": false,
"Samples": [
192.9550999999999,
177.51659999999993,
176.6638999999998,
177.0019000000002,
195.8095000000003,
178.6963000000001,
178.6501000000003,
178.6278000000002,
194.0826000000002,
178.87760000000029,
179.05510000000005,
193.26009999999997,
179.4454999999998,
178.48759999999994,
178.58819999999924,
192.01800000000004,
177.29970000000049,
178.8801000000003,
178.94360000000052,
195.4291999999996,
178.7588999999998,
178.4949999999999,
193.9444999999996,
176.52939999999945,
175.77700000000005,
176.09209999999985,
190.91429999999949,
176.14129999999933,
176.03039999999965,
175.8089
],
"Min": 175.77700000000005,
"Max": 195.8095000000003,
"Median": 178.6732000000002,
"Average": 181.9593433333333,
"StandardDeviation": 7.113551924586527,
"Sum": 5458.780299999998
}
]
},
{
"Name": "JsonSerializationPerformanceTests.Unity_JsonUtility_Deserialize",
"ClassName": "JsonSerializationPerformanceTests",
"MethodName": "Unity_JsonUtility_Deserialize",
"Version": "1",
"Categories": [
"Performance"
],
"SampleGroups": [
{
"Name": "Time.GC()",
"Unit": 8,
"IncreaseIsBetter": false,
"Samples": [
2007.0,
2007.0,
2007.0,
2007.0,
2007.0,
2007.0,
2007.0,
2007.0,
2007.0,
2007.0,
2007.0,
2007.0,
2007.0,
2007.0,
2007.0,
2007.0,
2007.0,
2007.0,
2007.0,
2007.0,
2007.0,
2007.0,
2007.0,
2007.0,
2007.0,
2007.0,
2007.0,
2007.0,
2007.0,
2007.0
],
"Min": 2007.0,
"Max": 2007.0,
"Median": 2007.0,
"Average": 2007.0,
"StandardDeviation": 0.0,
"Sum": 60210.0
},
{
"Name": "Time",
"Unit": 2,
"IncreaseIsBetter": false,
"Samples": [
79.64330000000007,
86.45499999999993,
113.8247,
80.13830000000007,
80.26660000000016,
80.05079999999998,
79.96709999999985,
80.05830000000015,
79.61950000000002,
79.15109999999982,
79.06040000000007,
79.1241,
79.19600000000014,
79.1051,
88.5603000000001,
114.34370000000013,
80.02590000000009,
80.16570000000002,
80.66829999999982,
80.4014000000002,
80.55099999999993,
79.89870000000019,
79.70389999999998,
79.49720000000025,
79.6251000000002,
79.529,
82.68589999999995,
121.93769999999997,
80.5881000000004,
80.90249999999969
],
"Min": 79.06040000000007,
"Max": 121.93769999999997,
"Median": 80.05455000000007,
"Average": 84.1581566666667,
"StandardDeviation": 11.094777683687737,
"Sum": 2524.744700000001
}
]
},
{
"Name": "JsonSerializationPerformanceTests.Unity_JsonUtility_Serialize",
"ClassName": "JsonSerializationPerformanceTests",
"MethodName": "Unity_JsonUtility_Serialize",
"Version": "1",
"Categories": [
"Performance"
],
"SampleGroups": [
{
"Name": "Time.GC()",
"Unit": 8,
"IncreaseIsBetter": false,
"Samples": [
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0,
1.0
],
"Min": 1.0,
"Max": 1.0,
"Median": 1.0,
"Average": 1.0,
"StandardDeviation": 0.0,
"Sum": 30.0
},
{
"Name": "Time",
"Unit": 2,
"IncreaseIsBetter": false,
"Samples": [
48.370000000000008,
48.6653,
48.36110000000008,
48.46260000000007,
48.21680000000004,
48.289499999999978,
62.58420000000001,
48.301000000000048,
48.33269999999993,
48.20500000000004,
48.48040000000003,
48.98990000000004,
49.06560000000013,
49.05319999999983,
62.92329999999993,
49.144399999999908,
49.24420000000009,
48.98220000000015,
49.078899999999979,
48.95399999999995,
48.91819999999984,
49.149400000000017,
63.25890000000004,
49.18909999999983,
49.07410000000005,
48.90020000000004,
49.049800000000008,
49.0744000000002,
49.09940000000006,
48.99289999999996
],
"Min": 48.20500000000004,
"Max": 63.25890000000004,
"Median": 48.9914,
"Average": 50.21369000000001,
"StandardDeviation": 4.249973620886756,
"Sum": 1506.4107
}
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment