JSON Performance Benchmarks
Unity 5.3 came out this week and introduced a new, built-in JSON serializer library. Today’s article compares its performance against some popular third-party JSON serializer libraries to see if Unity’s version is any faster. Read on for the results!
Today’s contenders are:
- Unity3D 5.3.0f4
- LitJSON 0.9.0
- Json.NET 7.0.1
They will all be made to serialize this simple “save game” structure:
[Serializable] public class SaveGame { public string Name; public int HighScore; public List<InventoryItem> Inventory; } [Serializable] public class InventoryItem { public int Id; public int Quantity; }
The test will judge them on three grounds:
- JSON size
- Serialization time
- Deserialization time
There is also the “ease of use” factor. It’s hard to compare, but you can see that they all offer one-liners for both serialization and deserialization. I’ll call this a tie:
using UnityEngine; var unityJson = JsonUtility.ToJson(saveGame); var unitySaveGame = JsonUtility.FromJson<SaveGame>(unityJson); using LitJson; var litJsonJson = JsonMapper.ToJson(saveGame); var litJsonSaveGame = JsonMapper.ToObject<SaveGame>(litJsonJson); using Newtonsoft.Json; var jsonDotNetJson = JsonConvert.SerializeObject(saveGame); var jsonDotNetSaveGame = JsonConvert.DeserializeObject<SaveGame>(jsonDotNetJson);
Now for the size and performance test script:
using System; using System.Collections.Generic; using System.Text; using UnityEngine; using LitJson; using Newtonsoft.Json; [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 stopwatch = new System.Diagnostics.Stopwatch(); const int reps = 10000; string unityJson = null; string litJsonJson = null; string jsonDotNetJson = null; SaveGame unitySaveGame = null; SaveGame litJsonSaveGame = null; SaveGame jsonDotNetSaveGame = 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) { 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; report += "Unity: " + unityJson + "\n"; report += "LitJSON: " + litJsonJson + "\n"; report += "Json.NET: " + jsonDotNetJson + "\n"; PrintSaveGame("Unity", unitySaveGame); PrintSaveGame("LitJSON", litJsonSaveGame); PrintSaveGame("Json.NET", jsonDotNetSaveGame); var unitySize = Encoding.UTF8.GetBytes(unityJson).Length; var litJsonSize = Encoding.UTF8.GetBytes(litJsonJson).Length; var jsonDotNetSize = Encoding.UTF8.GetBytes(jsonDotNetJson).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", unitySize, unitySerializeTime, unityDeserializeTime, litJsonSize, litJsonSerializeTime, litJsonDeserializeTime, jsonDotNetSize, jsonDotNetSerializeTime, jsonDotNetDeserializeTime ); } 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); } }
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.11.2
- Unity 5.3.0f4, 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 | 68 | 78 |
LitJSON | 101 | 71 | 253 |
Json.NET | 101 | 94 | 151 |
All three JSON serializers produced the exact same, minimal JSON:
Unity: {"Name":"Speed Run","HighScore":10000,"Inventory":[{"Id":100,"Quantity":5},{"Id":200,"Quantity":20}]} LitJSON: {"Name":"Speed Run","HighScore":10000,"Inventory":[{"Id":100,"Quantity":5},{"Id":200,"Quantity":20}]} Json.NET: {"Name":"Speed Run","HighScore":10000,"Inventory":[{"Id":100,"Quantity":5},{"Id":200,"Quantity":20}]}
They all deserialized to the exact same SaveGame
instance:
Unity: Name=Speed Run HighScore=10000 Inventory= Id=100, Quantity=5 Id=200, Quantity=20 LitJSON: Name=Speed Run HighScore=10000 Inventory= Id=100, Quantity=5 Id=200, Quantity=20 Json.NET: Name=Speed Run HighScore=10000 Inventory= Id=100, Quantity=5 Id=200, Quantity=20
But when it comes to serialization and deserialization performance, the differences started to show. Unity’s new JsonUtility
serialized and deserialized the fastest. It’s serialization time was only slightly faster than second-place LitJSON, but about a third faster than Json.NET.
When it comes to deserialization, Unity’s JsonUtility
really outshines the competition. It completed the task in about half the time that it took Json.NET and about a third the time as LitJSON. While LitJSON is faster than Json.NET at serialization, Json.NET is faster than LitJSON at deserialization. Meanwhile, Unity is faster than both of them!
Simply put, Unity’s new built-in JSON library is blazingly fast. If you want a quick way to boost your app’s handling of JSON, upgrade to Unity 5.3 and make the switch to JsonUtility
!
What do you use for JSON? Any plans to switch when you upgrade to 5.3? Let me know in the comments!
#1 by Yohan Hadjedj on December 14th, 2015 ·
Hey!
Nice article! Super fast, considering 5.3 just got out :p
Maybe you should re-do the test with a bigger and more complex JSON structure? Maybe it will change the results?
#2 by jackson on December 14th, 2015 ·
Great idea! I did a quick test by changing the
Name
field to be 100,000 asterisks (*) and thereps
to 10. In the same environment as the article, I got these results:Unity ended up slower than Json.NET here, but not by much. LitJSON was a ways behind both of them.
I’ll have to do some further testing to see how they each perform with more complex documents than just making a string field really big.
#3 by Yohan Hadjedj on December 15th, 2015 ·
Nice! Thanks for sharing this ;)
#4 by raven on April 29th, 2016 ·
Wow ,I thought Unity’s JSON has more overhead…Can you also include benchmark from other popular JSON parsers named SimpleJson and Full Serializer?
http://wiki.unity3d.com/index.php/SimpleJSON
https://github.com/jacobdufault/fullserializer
#5 by jackson on April 30th, 2016 ·
That’s a good idea, especially since SimpleJSON seems popular. I’ll probably do a follow-up article for 5.4 since Unity’s JSON may have improved then, too.
#6 by zephyr3 on July 19th, 2016 ·
The unity JsonUtility is native so it’s super fast and low allocations but only supports the simple types (no Dictionaries). I think Json.NET performs well on the large field data because it processes the json using forward only streams.
#7 by Ä°lkin on September 19th, 2016 ·
Very informative article, thank you!
Do you have any idea or data about how much garbage each library produces? I would prefer a library which is slower but generates less garbage.
#8 by jackson on September 19th, 2016 ·
That’s not something I tested, but it sounds like an excellent idea for another followup article!