.NET Core 3 yang baru saja dirilis membawa sejumlah inovasi. Selain C # 8 dan dukungan untuk WinForms dan WPF, rilis terbaru telah menambahkan serializer JSON (de) baru - System.Text.Json , dan seperti namanya, semua kelasnya ada di namespace ini.
Ini adalah inovasi besar. Serialisasi JSON merupakan faktor penting dalam aplikasi web.Sebagian besar REST API saat ini bergantung padanya. Saat klien javascript Anda mengirim JSON dalam isi permintaan POST, server menggunakan deserialisasi JSON untuk mengubahnya menjadi objek C #. Dan ketika server mengembalikan sebuah objek sebagai tanggapan, itu membuat serial objek ini ke JSON sehingga klien javascript Anda dapat memahaminya. Ini adalah operasi besar yang dilakukan pada setiap permintaan dengan objek. Performa mereka dapat mempengaruhi performa aplikasi secara signifikan, yang akan saya tunjukkan sekarang.
Jika Anda memiliki pengalaman dengan .NET, maka Anda pasti pernah mendengar tentang serializer Json.NET yang sangat baik , juga dikenal sebagai Newtonsoft.Json . Jadi mengapa kita membutuhkan serializer baru ketika kita sudah memiliki Newtonsoft.Json ? Meskipun Newtonsoft.Json tidak diragukan lagi hebat, ada beberapa alasan bagus untuk menggantinya:
- Microsoft sangat ingin menggunakan tipe baru, seperti
, untuk meningkatkan kinerja. Memodifikasi pustaka besar seperti Newtonsoft tanpa merusak fungsionalitas sangat sulit.Span<T> - , HTTP, UTF-8.
String.NET — UTF-16. Newtonsoft UTF-8 UTF-16, . UTF-8. - Newtonsoft , .NET Framework ( BCL FCL), . ASP.NET Core Newtonsoft, .
Pada artikel ini, kami akan menjalankan beberapa tolok ukur untuk melihat seberapa baik serializer baru dalam hal kinerja. Selain itu, kami juga akan membandingkan Newtonsoft.Json dan System.Text.Json dengan serializers terkenal lainnya dan melihat bagaimana mereka menangani hubungannya satu sama lain.
Pertempuran serializers
Inilah penggaris kami:
- Newtonsoft.Json (juga dikenal sebagai Json.NET ) adalah serializer yang saat ini menjadi standar industri. Itu diintegrasikan ke dalam ASP.NET, meskipun itu pihak ketiga. Paket NuGet # 1 sepanjang masa. Perpustakaan pemenang banyak penghargaan (saya mungkin tidak tahu pasti).
- System.Text.Json — Microsoft. Newtonsoft.Json. ASP.NET Core 3. .NET, NuGet ( ).
- DataContractJsonSerializer — , Microsoft, ASP.NET , Newtonsoft.Json.
- Jil — JSON Sigil

- ServiceStack — .NET JSON, JSV CSV. .NET ( ).

- Utf8Jso n adalah serializer C # ke JSON tercepat yang memproklamirkan diri sendiri. Bekerja dengan alokasi memori nol dan membaca / menulis langsung ke biner UTF8 untuk kinerja yang lebih baik.

Perhatikan bahwa ada serializers non-JSON di luar sana yang lebih cepat. Secara khusus, protobuf-net adalah serializer biner yang seharusnya lebih cepat daripada serializer yang dibandingkan dalam artikel ini (yang belum diuji oleh benchmark).
Struktur benchmark
Serializers tidak mudah untuk dibandingkan. Kita perlu membandingkan serialisasi dan deserialisasi. Kita perlu membandingkan berbagai jenis kelas (kecil dan besar), daftar, dan kamus. Dan kita perlu membandingkan target serialisasi yang berbeda: string, aliran, dan array karakter (array UTF-8). Ini adalah matriks pengujian yang cukup besar, tetapi saya akan mencoba untuk membuatnya tetap teratur dan sesingkat mungkin.
Kami akan menguji 4 fungsi berbeda:
- Serialisasi ke string
- Serialisasi ke aliran
- Deserialisasi dari string
- Permintaan per detik di aplikasi ASP.NET Core 3
Untuk masing-masing, kami akan menguji berbagai jenis objek (yang dapat Anda lihat di GitHub ):
- Kelas kecil dengan hanya 3 properti tipe primitif.
- Kelas besar dengan sekitar 25 properti, DateTime dan beberapa enum
- Daftar 1000 elemen (kelas kecil)
- Kamus 1000 elemen (kelas kecil)
Ini tidak semua tolok ukur yang diperlukan, tetapi, menurut saya, itu cukup untuk mendapatkan gambaran umum.
Untuk semua benchmark saya menggunakan BenchmarkDotNet pada sistem berikut: BenchmarkDotNet=v0.11.5, OS=Windows 10.0.17134.1069 (1803/April2018Update/Redstone4) Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores. .NET Core SDK=3.0.100. Host : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64bit RyuJIT. Anda dapat menemukan proyek benchmark itu sendiri di GitHub .
Semua pengujian hanya akan berjalan di proyek .NET Core 3.
Tolok Ukur 1: Serialisasi ke String
Hal pertama yang akan kita periksa adalah membuat serial sampel objek kita menjadi string.
Kode benchmark itu sendiri cukup sederhana (lihat di GitHub ):
public class SerializeToString<T> where T : new()
{
private T _instance;
private DataContractJsonSerializer _dataContractJsonSerializer;
[GlobalSetup]
public void Setup()
{
_instance = new T();
_dataContractJsonSerializer = new DataContractJsonSerializer(typeof(T));
}
[Benchmark]
public string RunSystemTextJson()
{
return JsonSerializer.Serialize(_instance);
}
[Benchmark]
public string RunNewtonsoft()
{
return JsonConvert.SerializeObject(_instance);
}
[Benchmark]
public string RunDataContractJsonSerializer()
{
using (MemoryStream stream1 = new MemoryStream())
{
_dataContractJsonSerializer.WriteObject(stream1, _instance);
stream1.Position = 0;
using var sr = new StreamReader(stream1);
return sr.ReadToEnd();
}
}
[Benchmark]
public string RunJil()
{
return Jil.JSON.Serialize(_instance);
}
[Benchmark]
public string RunUtf8Json()
{
return Utf8Json.JsonSerializer.ToJsonString(_instance);
}
[Benchmark]
public string RunServiceStack()
{
return SST.JsonSerializer.SerializeToString(_instance);
}
}
Kelas pengujian di atas bersifat generik, jadi kita bisa menguji semua objek kita dengan kode yang sama, misalnya:
BenchmarkRunner.Run<SerializeToString<Models.BigClass>>();
Setelah menjalankan semua kelas pengujian dengan semua serializer, kami mendapatkan hasil sebagai berikut:
Indikator yang lebih akurat dapat ditemukan di sini
- Utf8Json adalah yang tercepat hingga saat ini, lebih dari 4x lebih cepat dari Newtonsoft.Json dan System.Text.Json . Ini adalah perbedaan yang mencolok.
- Jil juga sangat cepat, sekitar 2.5x lebih cepat dari Newtonsoft.Json dan System.Text.Json.
- Dalam kebanyakan kasus, serializer System.Text.Json baru berkinerja lebih baik daripada Newtonsoft.Json sekitar 10%, kecuali untuk Dictionary, yang akhirnya 10% lebih lambat.
- DataContractJsonSerializer yang lebih lama jauh lebih buruk daripada yang lain.
- ServiceStack berada tepat di tengah, menunjukkan bahwa ini bukan lagi penyambung teks tercepat. Setidaknya untuk JSON.
Tolok Ukur 2: Serialisasi ke Streaming
Rangkaian pengujian kedua hampir sama, kecuali kami melakukan serialisasi ke aliran. Kode patokan ada di sini . Hasil:
Angka yang lebih akurat dapat ditemukan di sini . Terima kasih kepada Adam Sitnik dan Ahson Khan karena telah membantu saya membuat System.Text.Json berfungsi.
Hasilnya sangat mirip dengan tes sebelumnya. Utf8Json dan Jil 4 kali lebih cepat dari yang lain. Jil sangat cepat, kedua setelah Utf8Json . DataContractJsonSerializer masih yang paling lambat dalam banyak kasus. Newtonsoft bekerja seperti System.Text.Json dalam banyak kasus , kecuali untuk kamus di mana Newtonsoft memiliki keuntungan yang nyata .
Tolok Ukur 3: Deserialisasi Dari String
Rangkaian pengujian berikutnya berkaitan dengan deserialisasi dari string. Kode tes dapat ditemukan di sini .
Angka yang lebih akurat dapat ditemukan di sini .
Saya mengalami kesulitan menjalankan DataContractJsonSerializer untuk benchmark ini, jadi tidak disertakan dalam hasil. Jika tidak, kami melihat bahwa Jil adalah yang tercepat dalam deserialisasi , Utf8Json di tempat kedua. Mereka 2-3 kali lebih cepat dari System.Text.Json . Dan System.Text.Json sekitar 30% lebih cepat dari Json.NET .
Sejauh ini, ternyata Newtonsoft.Json yang populer dan System.Text.Json yang baru memiliki kinerja yang jauh lebih buruk daripada pesaing mereka. Ini adalah hasil yang agak tidak terduga bagi saya karena popularitas Newtonsoft.Jsondan semua hype seputar Microsoft System.Text.Json berkinerja terbaik baru . Mari kita lihat di aplikasi ASP.NET.
Tolok ukur 4: Jumlah permintaan per detik di server .NET
Seperti yang disebutkan sebelumnya, serialisasi JSON sangat penting karena selalu ada di REST API. Permintaan HTTP ke server yang menggunakan tipe konten
application/jsonperlu membuat serial atau deserialisasi objek JSON. Ketika server menerima payload dalam permintaan POST, server melakukan deserialisasi dari JSON. Ketika server mengembalikan sebuah objek dalam tanggapannya, itu membuat serial JSON. Komunikasi klien-server modern sangat bergantung pada serialisasi JSON. Oleh karena itu, untuk menguji skenario "nyata", masuk akal untuk membuat server pengujian dan mengukur kinerjanya.
Saya terinspirasi oleh tes kinerja Microsoftdi mana mereka membuat aplikasi server MVC dan memeriksa permintaan per detik. Benchmark Microsoft menguji System.Text.Json dan Newtonsoft.Json . Pada artikel ini kita akan melakukan hal yang sama, kecuali kita akan membandingkannya dengan Utf8Json , yang telah terbukti menjadi salah satu serializer tercepat dalam pengujian sebelumnya.
Sayangnya saya tidak dapat mengintegrasikan ASP.NET Core 3 dengan Jil, jadi benchmark tidak menyertakannya. Saya cukup yakin itu bisa dilakukan dengan lebih banyak usaha, tapi sayangnya.
Pembuatan tes ini terbukti lebih sulit dari sebelumnya. Saya pertama kali membuat aplikasi ASP.NET Core 3.0 MVC, seperti di benchmark Microsoft. Saya menambahkan pengontrol untuk tes kinerja, mirip dengan yang ada di tes Microsoft :
[Route("mvc")]
public class JsonSerializeController : Controller
{
private static Benchmarks.Serializers.Models.ThousandSmallClassList _thousandSmallClassList
= new Benchmarks.Serializers.Models.ThousandSmallClassList();
[HttpPost("DeserializeThousandSmallClassList")]
[Consumes("application/json")]
public ActionResult DeserializeThousandSmallClassList([FromBody]Benchmarks.Serializers.Models.ThousandSmallClassList obj) => Ok();
[HttpGet("SerializeThousandSmallClassList")]
[Produces("application/json")]
public object SerializeThousandSmallClassList() => _thousandSmallClassList;
}
Ketika klien memanggil titik akhir
DeserializeThousandSmallClassList, server akan menerima teks JSON dan menghapus kontennya. Beginilah cara kami menguji deserialisasi. Ketika klien memanggil SerializeThousandSmallClassList, server akan mengembalikan daftar 1000 SmallClassitem dan dengan demikian membuat serial konten ke JSON.
Kemudian kita perlu membatalkan logging untuk setiap permintaan agar tidak mempengaruhi hasil:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging(logging =>
{
logging.ClearProviders();
//logging.AddConsole();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
Kami sekarang membutuhkan cara untuk beralih antara System.Text.Json , Newtonsoft dan Utf8Json . Sangat mudah dengan dua yang pertama. Untuk System.Text.Json Anda tidak perlu melakukan apa pun. Untuk beralih ke Newtonsoft.Json cukup tambahkan satu baris ke
ConfigureServices :
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews()
// Newtonsoft. - System.Text.Json
.AddNewtonsoftJson()
;
Untuk Utf8Json, kita perlu menambahkan format media kustom
InputFormatterdan OutputFormatter. Tidak semudah itu, tetapi saya akhirnya menemukan solusi yang baik di internet , dan setelah mencari-cari di pengaturan itu berhasil. Ada juga paket NuGet dengan pemformat, tetapi tidak berfungsi dengan ASP.NET Core 3.
internal sealed class Utf8JsonInputFormatter : IInputFormatter
{
private readonly IJsonFormatterResolver _resolver;
public Utf8JsonInputFormatter1() : this(null) { }
public Utf8JsonInputFormatter1(IJsonFormatterResolver resolver)
{
_resolver = resolver ?? JsonSerializer.DefaultResolver;
}
public bool CanRead(InputFormatterContext context) => context.HttpContext.Request.ContentType.StartsWith("application/json");
public async Task<InputFormatterResult> ReadAsync(InputFormatterContext context)
{
var request = context.HttpContext.Request;
if (request.Body.CanSeek && request.Body.Length == 0)
return await InputFormatterResult.NoValueAsync();
var result = await JsonSerializer.NonGeneric.DeserializeAsync(context.ModelType, request.Body, _resolver);
return await InputFormatterResult.SuccessAsync(result);
}
}
internal sealed class Utf8JsonOutputFormatter : IOutputFormatter
{
private readonly IJsonFormatterResolver _resolver;
public Utf8JsonOutputFormatter1() : this(null) { }
public Utf8JsonOutputFormatter1(IJsonFormatterResolver resolver)
{
_resolver = resolver ?? JsonSerializer.DefaultResolver;
}
public bool CanWriteResult(OutputFormatterCanWriteContext context) => true;
public async Task WriteAsync(OutputFormatterWriteContext context)
{
if (!context.ContentTypeIsServerDefined)
context.HttpContext.Response.ContentType = "application/json";
if (context.ObjectType == typeof(object))
{
await JsonSerializer.NonGeneric.SerializeAsync(context.HttpContext.Response.Body, context.Object, _resolver);
}
else
{
await JsonSerializer.NonGeneric.SerializeAsync(context.ObjectType, context.HttpContext.Response.Body, context.Object, _resolver);
}
}
}
Sekarang untuk ASP.NET menggunakan pemformat ini:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews()
// Newtonsoft
//.AddNewtonsoftJson()
// Utf8Json
.AddMvcOptions(option =>
{
option.OutputFormatters.Clear();
option.OutputFormatters.Add(new Utf8JsonOutputFormatter1(StandardResolver.Default));
option.InputFormatters.Clear();
option.InputFormatters.Add(new Utf8JsonInputFormatter1());
});
}
Jadi ini servernya. Sekarang tentang klien.
C # klien untuk mengukur permintaan per detik
Saya juga telah membuat aplikasi klien C #, meskipun klien JavaScript akan menang di sebagian besar skenario dunia nyata. Tidak masalah untuk tujuan kita. Ini kodenya:
public class RequestPerSecondClient
{
private const string HttpsLocalhost = "https://localhost:5001/";
public async Task Run(bool serialize, bool isUtf8Json)
{
await Task.Delay(TimeSpan.FromSeconds(5));
var client = new HttpClient();
var json = JsonConvert.SerializeObject(new Models.ThousandSmallClassList());
// ,
for (int i = 0; i < 100; i++)
{
await DoRequest(json, client, serialize);
}
int count = 0;
Stopwatch sw = new Stopwatch();
sw.Start();
while (sw.Elapsed < TimeSpan.FromSeconds(1))
{
count++;
await DoRequest(json, client, serialize);
}
Console.WriteLine("Requests in one second: " + count);
}
private async Task DoRequest(string json, HttpClient client, bool serialize)
{
if (serialize)
await DoSerializeRequest(client);
else
await DoDeserializeRequest(json, client);
}
private async Task DoDeserializeRequest(string json, HttpClient client)
{
var uri = new Uri(HttpsLocalhost + "mvc/DeserializeThousandSmallClassList");
var content = new StringContent(json, Encoding.UTF8, "application/json");
var result = await client.PostAsync(uri, content);
result.Dispose();
}
private async Task DoSerializeRequest(HttpClient client)
{
var uri = HttpsLocalhost + "mvc/SerializeThousandSmallClassList";
var result = await client.GetAsync(uri);
result.Dispose();
}
}
Klien ini akan terus mengirim permintaan selama 1 detik, menghitungnya.
hasil
Jadi, tanpa basa-basi lagi, berikut hasilnya:
Indikator yang lebih akurat dapat ditemukan di sini
Utf8Json mengungguli serializer lain dengan selisih yang sangat besar. Ini bukanlah kejutan besar setelah tes sebelumnya.
Dalam hal serialisasi, Utf8Json 2x lebih cepat dari System.Text.Json dan 4x lebih cepat dari Newtonsoft . Untuk deserialisasi, Utf8Json 3,5 kali lebih cepat dari System.Text.Json dan 6 kali lebih cepat dari Newtonsoft .
Satu-satunya kejutan bagi saya di sini adalah betapa buruknya Newtonsoft.Json bekerja... Ini mungkin karena masalah UTF-16 dan UTF-8. Protokol HTTP bekerja dengan teks UTF-8. Newtonsoft mengubah teks ini menjadi jenis string .NET, yaitu UTF-16. Overhead ini tidak ada di Utf8Json atau System.Text.Json , yang bekerja langsung dengan UTF-8.
Penting untuk dicatat bahwa tolok ukur ini tidak boleh 100% dipercaya karena mungkin tidak sepenuhnya mencerminkan skenario sebenarnya. Dan itulah kenapa:
- Saya menjalankan semuanya di mesin lokal saya - klien dan server. Dalam skenario dunia nyata, server dan klien berada di mesin yang berbeda.
- . , . . - . , , . , , GC. Utf8Json, .
- Microsoft ( 100 000). , , , , .
- . , - - .
Semua hal dipertimbangkan, hasil ini sangat luar biasa. Tampaknya waktu respons dapat ditingkatkan secara signifikan dengan memilih penyambung JSON yang tepat. Beralih dari Newtonsoft ke System.Text.Json akan meningkatkan jumlah permintaan sebanyak 2-7 kali, dan beralih dari Newtonsoft ke Utf8Json akan meningkat 6-14 kali. Ini tidak sepenuhnya adil, karena server nyata akan melakukan lebih dari sekadar menerima argumen dan mengembalikan objek. Ini mungkin akan melakukan hal-hal lain juga, seperti bekerja dengan database dan karena itu menjalankan beberapa logika bisnis, jadi waktu serialisasi mungkin kurang penting. Namun, angka-angka ini luar biasa.
kesimpulan
Mari kita rangkum:
- System.Text.Json , Newtonsoft.Json ( ). Microsoft .
- , Newtonsoft.Json System.Text.Json. , Utf8Json Jil 2-4 , System.Text.Json.
- , Utf8Json ASP.NET . , , , ASP.NET.
Apakah ini berarti kita semua harus beralih ke Utf8Json atau Jil? Jawabannya adalah ... mungkin. Ingat, Newtonsoft.Json telah bertahan dalam ujian waktu dan menjadi serializer paling populer karena suatu alasan. Ini mendukung banyak fitur, telah diuji dengan semua jenis kasus tepi, dan memiliki banyak solusi dan penyelesaian yang terdokumentasi. Baik System.Text.Json dan Newtonsoft.Json didukung dengan sangat baik. Microsoft akan terus menginvestasikan sumber daya dan upaya di System.Text.Json sehingga Anda dapat mengandalkan dukungan yang hebat. Sedangkan Jil dan Utf8Jsonmenerima sangat sedikit komitmen pada tahun lalu. Nyatanya, sepertinya mereka tidak banyak melakukan perawatan dalam 6 bulan terakhir.
Salah satu opsinya adalah menggabungkan beberapa serializers dalam aplikasi Anda. Tingkatkan ke serializers yang lebih cepat untuk berintegrasi dengan ASP.NET untuk kinerja yang unggul, tetapi tetap gunakan Newtonsoft.Json dalam logika bisnis Anda untuk mendapatkan hasil maksimal dari rangkaian fiturnya.
Saya harap Anda menikmati artikel ini. Semoga berhasil)
Tolok ukur lainnya
Beberapa benchmark lain yang membandingkan berbagai serializers
Ketika Microsoft mengumumkan System.Text.Json, mereka menunjukkan benchmark mereka sendiri yang membandingkan System.Text.Json dan Newtonsoft.Json . Selain serialisasi dan deserialisasi, tolok ukur ini menguji kelas Dokumen untuk akses acak, Pembaca dan Penulis. Mereka juga mendemonstrasikan pengujian Query Per Second , yang menginspirasi saya untuk membuat pengujian sendiri.
Repositori .NET Core GitHub menyertakan serangkaian tolok ukur yang serupa dengan yang dijelaskan di artikel ini. Saya mengamati tes mereka dengan saksama untuk memastikan saya tidak membuat kesalahan sendiri. Anda dapat menemukan mereka diSolusi tolok ukur mikro .
Jil memiliki benchmark sendiri yang membandingkan Jil , Newtonsoft , Protobuf , dan ServiceStack .
Utf8Json telah memposting serangkaian benchmark yang tersedia di GitHub . Mereka juga menguji serializers biner.
Alois Kraus telah melakukan pengujian mendalam yang sangat baik dari serializers .NET paling populer, termasuk serializers JSON, serializers biner, dan serializers XML. Tolok ukurnya mencakup tolok ukur untuk .NET Core 3 dan .NET Framework 4.8.
Pelajari lebih lanjut tentang kursus tersebut.