JSON Libraries Comparison in Unity 5.5
Unity 5.5 has been out for about a month now and it’s time to update the benchmarks for JSON libraries. Which is fastest now? Which creates the least garbage? Read on to find out!
Last time I ran these benchmarks it was clear that Unity 5.4 was the fastest JSON library available. LitJSON and Json.NET were more or less tied for second place and FullSerializer was by far the slowest. I also tested how much garbage they created and found that, once again, Unity 5.4 created the least. Here too LitJSON and Json.NET tied for second place and FullSerializer brought up the rear.
Today’s test uses the latest Unity 5.5.0f3 release instead of 5.4.0f3 and also uses the latest source of FullSerializer, still at version 1.1.0. LitJSON and Json.NET remain unchanged at versions 0.9.0 and 9.0.1 respectively.
All the JSON libraries are put to the same test, which is a combination of the benchmarks and garbage creation tests from the previous two articles. If the profiler is running, the garbage tests run. Otherwise the benchmarks run.
using System; using System.Collections.Generic; using System.Text; using UnityEngine; using UnityEngine.Profiling; using LitJson; using Newtonsoft.Json; using FullSerializer; [Serializable] public class SaveGame { public string Name; public int HighScore; public List<InventoryItem> Inventory; } [Serializable] public class InventoryItem { public int Id; public int Quantity; } class TestScript : MonoBehaviour { string report = ""; void Start() { var saveGame = new SaveGame { Name = "Speed Run", HighScore = 10000, Inventory = new List<InventoryItem> { new InventoryItem { Id = 100, Quantity = 5 }, new InventoryItem { Id = 200, Quantity = 20 } } }; // Warm up reflection JsonUtility.ToJson(saveGame); JsonMapper.ToJson(saveGame); JsonConvert.SerializeObject(saveGame); var fsSerializer = new fsSerializer(); fsData fsData; fsSerializer.TrySerialize(saveGame, out fsData); fsJsonPrinter.CompressedJson(fsData); if (Profiler.enabled) { var tempSaveGame = new SaveGame(); TestGarbage(saveGame, tempSaveGame, fsSerializer); } else { var stopwatch = new System.Diagnostics.Stopwatch(); const int reps = 10000; string unityJson = null; string litJsonJson = null; string jsonDotNetJson = null; string fullSerializerJson = null; SaveGame unitySaveGame = null; SaveGame litJsonSaveGame = null; SaveGame jsonDotNetSaveGame = null; SaveGame fullSerializerSaveGame = null; stopwatch.Start(); for (var i = 0; i < reps; ++i) { unityJson = JsonUtility.ToJson(saveGame); } var unitySerializeTime = stopwatch.ElapsedMilliseconds; stopwatch.Reset(); stopwatch.Start(); for (var i = 0; i < reps; ++i) { litJsonJson = JsonMapper.ToJson(saveGame); } var litJsonSerializeTime = stopwatch.ElapsedMilliseconds; stopwatch.Reset(); stopwatch.Start(); for (var i = 0; i < reps; ++i) { jsonDotNetJson = JsonConvert.SerializeObject(saveGame); } var jsonDotNetSerializeTime = stopwatch.ElapsedMilliseconds; stopwatch.Reset(); stopwatch.Start(); for (var i = 0; i < reps; ++i) { fsSerializer.TrySerialize(saveGame, out fsData); fullSerializerJson = fsJsonPrinter.CompressedJson(fsData); } var fullSerializerSerializeTime = stopwatch.ElapsedMilliseconds; stopwatch.Reset(); stopwatch.Start(); for (var i = 0; i < reps; ++i) { unitySaveGame = JsonUtility.FromJson<SaveGame>(unityJson); } var unityDeserializeTime = stopwatch.ElapsedMilliseconds; stopwatch.Reset(); stopwatch.Start(); for (var i = 0; i < reps; ++i) { litJsonSaveGame = JsonMapper.ToObject<SaveGame>(litJsonJson); } var litJsonDeserializeTime = stopwatch.ElapsedMilliseconds; stopwatch.Reset(); stopwatch.Start(); for (var i = 0; i < reps; ++i) { jsonDotNetSaveGame = JsonConvert.DeserializeObject<SaveGame>(jsonDotNetJson); } var jsonDotNetDeserializeTime = stopwatch.ElapsedMilliseconds; stopwatch.Reset(); stopwatch.Start(); for (var i = 0; i < reps; ++i) { fsData = fsJsonParser.Parse(fullSerializerJson); fsSerializer.TryDeserialize<SaveGame>(fsData, ref fullSerializerSaveGame); } var fullSerializerDeserializeTime = stopwatch.ElapsedMilliseconds; report += "Unity: " + unityJson + "\n"; report += "LitJSON: " + litJsonJson + "\n"; report += "Json.NET: " + jsonDotNetJson + "\n"; report += "FullSerializer: " + fullSerializerJson + "\n"; PrintSaveGame("Unity", unitySaveGame); PrintSaveGame("LitJSON", litJsonSaveGame); PrintSaveGame("Json.NET", jsonDotNetSaveGame); PrintSaveGame("FullSerializer", fullSerializerSaveGame); var unitySize = Encoding.UTF8.GetBytes(unityJson).Length; var litJsonSize = Encoding.UTF8.GetBytes(litJsonJson).Length; var jsonDotNetSize = Encoding.UTF8.GetBytes(jsonDotNetJson).Length; var fullSerializerSize = Encoding.UTF8.GetBytes(fullSerializerJson).Length; report += string.Format( "Library,Size,SerializeTime,Deserialize Time\n" + "Unity,{0},{1},{2}\n" + "LitJSON,{3},{4},{5}\n" + "Json.NET,{6},{7},{8}\n" + "FullSerializer,{9},{10},{11}\n", unitySize, unitySerializeTime, unityDeserializeTime, litJsonSize, litJsonSerializeTime, litJsonDeserializeTime, jsonDotNetSize, jsonDotNetSerializeTime, jsonDotNetDeserializeTime, fullSerializerSize, fullSerializerSerializeTime, fullSerializerDeserializeTime ); } } void PrintSaveGame(string title, SaveGame saveGame) { var builder = new StringBuilder(title); builder.Append(":\n\tName="); builder.Append(saveGame.Name); builder.Append('\n'); builder.Append("\tHighScore="); builder.Append(saveGame.HighScore); builder.Append("\n"); builder.Append("\tInventory=\n"); foreach (var item in saveGame.Inventory) { builder.Append("\t\t"); builder.Append("Id="); builder.Append(item.Id); builder.Append(", Quantity="); builder.Append(item.Quantity); builder.Append('\n'); } report += builder.ToString() + "\n"; } void OnGUI() { GUI.TextArea(new Rect(0, 0, Screen.width, Screen.height), report); } void TestGarbage(SaveGame saveGame, SaveGame tempSaveGame, fsSerializer fsSerializer) { var json = TestUnitySerialize(saveGame); TestUnityDeserialize(json); json = TestLitJsonSerialize(saveGame); TestLitJsonDeserialize(json); json = TestJsonDotNetSerialize(saveGame); TestJsonDotNetDeserialize(json); json = TestFullSerializerSerialize(saveGame, fsSerializer); TestFullSerializerDeserialize(json, fsSerializer, tempSaveGame); } string TestUnitySerialize(SaveGame saveGame) { return JsonUtility.ToJson(saveGame); } SaveGame TestUnityDeserialize(string json) { return JsonUtility.FromJson<SaveGame>(json); } string TestLitJsonSerialize(SaveGame saveGame) { return JsonMapper.ToJson(saveGame); } SaveGame TestLitJsonDeserialize(string json) { return JsonMapper.ToObject<SaveGame>(json); } string TestJsonDotNetSerialize(SaveGame saveGame) { return JsonConvert.SerializeObject(saveGame); } SaveGame TestJsonDotNetDeserialize(string json) { return JsonConvert.DeserializeObject<SaveGame>(json); } string TestFullSerializerSerialize(SaveGame saveGame, fsSerializer serializer) { fsData fsData; serializer.TrySerialize(saveGame, out fsData); return fsJsonPrinter.CompressedJson(fsData); } SaveGame TestFullSerializerDeserialize(string json, fsSerializer serializer, SaveGame saveGame) { var fsData = fsJsonParser.Parse(json); serializer.TryDeserialize<SaveGame>(fsData, ref saveGame); return saveGame; } }
If you want to try out the test yourself, simply paste the above code into a TestScript.cs
file in your Unity project’s Assets
directory and attach it to the main camera game object in a new, empty project. Then build in non-development mode for 64-bit processors and run it windowed at 640×480 with fastest graphics. I ran it that way on this machine:
- 2.3 Ghz Intel Core i7-3615QM
- Mac OS X 10.12.1
- Unity 5.5.0f3, Mac OS X Standalone, x86_64, non-development
- 640×480, Fastest, Windowed
And here are the results I got:
Library | Size | SerializeTime | Deserialize Time |
---|---|---|---|
Unity | 101 | 57 | 74 |
LitJSON | 101 | 67 | 225 |
Json.NET | 101 | 89 | 137 |
FullSerializer | 101 | 299 | 797 |
Little has changed since the last releases of Unity and FullSerializer. Unity seems a bit quicker, but it’s possibly just noise in the testing. The performance order of the libraries hasn’t changed and that’s the most important part.
I’ll omit the detailed output size data since it’s the same as before: all of the libraries output the same, minimal JSON.
Now let’s look at garbage creation. Unity only shows garbage in KB with one decimal place of precision, so some of these numbers are approximated by converting to bytes.
Unity, LitJSON, and Json.NET are all exactly the same as before. FullSerializer does better at serialization now as it uses only 4.2 KB instead of the previous 4.4 KB. Unfortunately, it now does worse at deserialization by using 36.7 KB instead of 34 KB. Regardless, it still creates way more garbage than any of the other libraries.
So it turns out that not much has changed with JSON libraries since Unity 5.4 debuted. Their speed, output size, and garbage creation are all very much the same as before. Unity’s JsonUtility
is still fastest and creates the least garbage with LitJSON and Json.NET coming in second place and FullSerializer being far behind those. If you can live with the restrictions imposed by JsonUtility
(e.g. no support for Dictionary
), then it’s probably the highest performance option of all the publicly-available libraries.
Which JSON library do you use? Why? Let me know in the comments!
#1 by VVEthan on January 9th, 2017 ·
It’s great to see updated versions of this article, even if not much has changed. (There are lots of old articles on the web about Unity-related things and its hard to know if they are outdated or not…) Thanks for keeping up the great blog!
#2 by Yohan Hadjedj on February 10th, 2017 ·
Hey!
Thanks a lot for everything you share :)
Quick question, what about using a “special” build of JSON.Net for Unity? https://www.assetstore.unity3d.com/en/#!/content/11347
It might have the exact same performance, but it has a BSON format that can serialize json to binary (so it’s faster and smaller). But maybe it’s killing the comparison as this is binary and not json readable by human :)
#3 by jackson on February 10th, 2017 ·
That’s a great idea and you’ll see BSON soon in an upcoming article!
#4 by friuns on December 29th, 2017 ·
JSON.Net is very bad, its only popular because microsoft using it by default in asp and other frameworks
#5 by friuns on December 29th, 2017 ·
Fastest is utf8json its even beats protobuf! but i use fastjson
#6 by jackson on December 29th, 2017 ·
Thanks for the tips. I’ll be sure to include utf8json and fastjson in a future article updating these benchmarks.