MessagePack 是一種以 binary 為基礎的序列化格式,設計目標很單純
讓機器之間交換資料更快、更小。它不像 JSON 追求人類可讀性,而是用固定順序與型別直接寫入位元資料,省略字串解析與中間物件建立的成本
在資料量大、結構固定、需要頻繁序列化與反序列化的情境下,MessagePack 往往能明顯降低 CPU 與 GC 壓力,這也是我這次會實際測它效能的原因
1. 我測試環境是 .NET 10 ,一樣沒有內建,請先到 nuget 下面去下載 https://www.nuget.org/packages/messagepack
2. 資料模型,因為我要測試,所以我得先設計一個有點點複雜的模型
[MessagePackObject]
public sealed class User
{
[Key(0)] public int Id { get; set; }
[Key(1)] public string Name { get; set; } = "";
[Key(2)] public List Friends { get; set; } = new();
[Key(3)] public int Age { get; set; }
[Key(4)] public string Alias { get; set; } = "";
[Key(5)] public decimal Salary { get; set; }
[Key(6)] public DateTime Birth { get; set; }
}
3. 這時候就是來測試他跟我常用的 JSON.NET 拚速度了,這邊我直接給程式碼
var testData = new List();
for (var i = 1; i <= 10000; i++)
{
var main = new User
{
Id = i,
Name = $"當麻 {i}",
Age = 20 + (i % 30),
Alias = (i % 2 == 0) ? $"Alias_{i}" : null,
Friends = new List(),
Salary = i * i ,
Birth=new DateTime(1970,1,1).AddDays(i)
};
main.Friends.Add(new User
{
Id = i,
Name = $"Donma Friend {i}",
Age = 20 + (i % 30),
Alias = (i % 2 == 0) ? $"朋友暱稱_{i}" : null,
Friends = new List(),
Salary = i * 10,
Birth = new DateTime(1990, 1, 1).AddDays(i)
});
testData.Add(main);
}
Console.WriteLine("-- MessagePack 測試 --");
var sp = new Stopwatch();
sp.Start();
//序列化 to MessagePack
byte[] messagePackBytes = MessagePackSerializer.Serialize(testData, MessagePackSerializerOptions.Standard);
Console.WriteLine("MessagePack 序列化時間:" + sp.Elapsed);
File.WriteAllBytes(AppDomain.CurrentDomain.BaseDirectory + "data.messagepack", messagePackBytes);
string base64 = Convert.ToBase64String(messagePackBytes);
File.WriteAllText(AppDomain.CurrentDomain.BaseDirectory + "data.base64", base64);
sp.Restart();
// MessagePack 反序列化
var testDataDeserialize = MessagePackSerializer.Deserialize>(messagePackBytes, MessagePackSerializerOptions.Standard);
Console.WriteLine("MessagePack 反序列化時間:" + sp.Elapsed);
Console.WriteLine($"Data Count:" + testDataDeserialize.Count +
", 測試一筆資料:" + testDataDeserialize[999].Name+","+ testDataDeserialize[999].Salary+","+
testDataDeserialize[999].Birth.ToString("yyyy-MM-dd"+","+ testDataDeserialize[999].Friends.Count));
Console.WriteLine("-- JSON.NET 測試 --");
//JSON 序列化
sp.Restart();
var testJson = JsonConvert.SerializeObject(testData);
Console.WriteLine("JSON.NET 序列化時間:" + sp.Elapsed);
File.WriteAllText(AppDomain.CurrentDomain.BaseDirectory + "data.json", testJson);
//JSON 反序列化
sp.Restart();
var testDataDeserializeJSON = JsonConvert.DeserializeObject>(testJson);
Console.WriteLine("JSON.NET 反序列化時間:" + sp.Elapsed);
Console.WriteLine($"Data Count:" + testDataDeserializeJSON.Count+
", 測試一筆資料:" + testDataDeserializeJSON[999].Name + "," + testDataDeserializeJSON[999].Salary + "," +
testDataDeserializeJSON[999].Birth.ToString("yyyy-MM-dd" + "," + testDataDeserializeJSON[999].Friends.Count));
4.測試結果
-- MessagePack 測試 --
MessagePack 序列化時間:00:00:00.1428397
MessagePack 反序列化時間:00:00:00.0652231
Data Count:10000, 測試一筆資料:當麻 1000,1000000,1972-09-27,1
-- JSON.NET 測試 --
JSON.NET 序列化時間:00:00:00.2635574
JSON.NET 反序列化時間:00:00:00.2995923
Data Count:10000, 測試一筆資料:當麻 1000,1000000.0,1972-09-27,1
先說結論跟注意事項,顯而易見的確速度有非常"顯著" 的改變,但是在第二步的時候
Key(index) 這部分,這需要完全對應,如果你對應錯誤則就會返序列化失敗這點要非常注意
畢竟這世界就是有一好沒二好,你享受速度就是必須要有些妥協,而且變成二進位後可讀性也減少了,這邊,我多紀錄一下他們存檔後的檔案大小
讓體積跟大小具象化
data.base64 1,088 KB
data.json 2,426 KB
data.messagepack 816 KB
所以檔案大小有非常非常顯著的差異,即使變成 base64 也還是小了 50%
這次測試數據到這邊,畢竟有時候問 AI ,AI 給的數據都不一定正確,不如自己實測看看。
--
本文原文首發於個人部落格: 實際測了 MessagePack:10,000 筆資料下,比 JSON.NET 快三倍
--
---
The bug existed in all possible states.
Until I ran the code.