Pull to refresh

Сравнение производительности JSON-сериализаторов для .NET

Reading time 5 min
Views 20K
Json в .NET может использоваться для разных целей. В моём случае это формирование ответа на Ajax-запрос в ASP.NET Mvc приложении. Конечно, конвертация ответа в JSON — не самое тонкое место, но мне стало интересно, как можно ускорить эту операцию. Настоящая статья не является детальным обзором существующих JSON-сериализаторов для .NET/Mono. Меня интересовало в первую очередь время, затрачиваемое на сериализацию относительно простых структур данных, и во вторую очередь маппинг. То есть, хочется чтобы сериализация легко и гибко программировалась и быстро работала.

В исследование попали следующие средства сериализации:
  1. Простая конкатенация строк
  2. JavaScriptSerializer (.NET Framework)
  3. DataContractJsonSerializer (.NET Framework 3.5)
  4. Newton Json.net (json.codeplex.com, james.newtonking.com/pages/json-net.aspx)
  5. JsonEx (code.google.com/p/jsonexserializer)
  6. Fluent Json (fluentjson.codeplex.com, code.google.com/p/fluent-json)


Для замера производительности проводилось два типа тестов:
Тест №1. 10 000 раз выполнялась инициализация сериализатора и сериализация одного и того же объекта.

Тест №2. 1 раз создавался и инициализировался сериализатор. 10 000 раз выполнялась сериализация. Тест был придуман с надеждой на то, что «умный» сериализатор, получив заранее информацию о типе объекта, будет выполнять преобразование быстрее. В некоторых случаях, предварительная инициализация невозможна, далее результаты таких тестов буду отмечать прочерком.

Среда тестирования:
Среди испытуемых есть два класса, которые входят в платформу, а это значит, что в их работе в Mono и .NET могут быть различия. В качестве основной и единственной ОС у меня Ubuntu, поэтому для тестирования .NET я воспользовался виртуальной машиной VirtualBox. А если быть точным:
  • Конфигурация «mono»: Intel Atom 330, 4Gb + Ubuntu 10.10, MonoDevelop 2.6, Mono 2.10.5
  • Конфигурация «.NET»: VirtualBox, 1.5GB, Windows XP, VS2010, .NET Framework 3.5

Конкатенация строк


Первым делом я приведу результаты тестирования сериализации путём простого ToString() и конкатенации. В «продакшн» такой способ может быть оправдан лишь в редких случаях. Я же его рассматриваю для того, чтобы использовать в качестве эталона. Сложно придумать способ получающий строку в формате JSON быстрее.
Результаты тестов ниже:
Тест №1 Тест №2
.NET 0,25 сек -
Mono 0,6 сек -

Проведение теста №2, по понятным причинам, невозможно.

JavaScriptSerializer


JavaScriptSerializer находится в пространстве имён System.Web.Script.Serialization платформ .NET/Mono. Пример кода:
var serializer = new JavaScriptSerializer();//Инициализация
string jsonResult = serializer.Serialize(Program.bookA);//Сериализация


Заявлена возможность переопределить способы сериализации для различных типов данных при помощи классов JavaScriptConverter, RegisterConverters. Это может пригодиться, например, для преобразования DateTime или enum-ов. Тесты проводились без использования этой возможности.

Результаты тестов:
Тест №1 Тест №2
.NET 4 сек 3,5 сек
Mono 5 сек 5 сек


DataContractJsonSerializer


Ещё один сериализатор доступный «из коробки» находится в пространстве имён System.Runtime.Serialization.Json. В использовании почти не отличается от предыдущего, за исключением того, что сериализация происходит в stream, а не в строку. Пример:
var serializer = new DataContractJsonSerializer(typeof(Book));
MemoryStream ms = new MemoryStream();
serializer.WriteObject(ms, Program.bookA);
string jsonResult = Encoding.Default.GetString(ms.GetBuffer());


Есть различия использования этого инструмента в .NET и Mono. В .NET необходимо размечать сериализуемые классы и свойства атрибутами [DataContract] и [DataMember] соответственно. В Mono эти атрибуты не обязательны.
Результаты тестов показывают, что разница не только в атрибутах:
Тест №1 Тест №2
.NET 1,5 сек 1,5 сек
Mono 34 сек 34 сек

Забегая вперёд, скажу 1.5 сек — это лучшее время среди всех испытуемых, 34 сек — худшее.

Fluent Json


Достоинство (а кому и недостаток) этого сериализатора в том, что он предоставляет гибкий способ маппинга свойств. Нет необходимости вводить атрибуты в слой бизнес-логики, что при работе со сторонними библиотеками проблематично. Не понадобятся промежуточные классы, чтобы преобразовать структуры данных слоя бизнес-логики в структуры данных клиент-серверного обмена. Ещё одной из заявленных «фишек», является возможность многопоточной обработки больших наборов данных, что даёт высокую производительность. Не мой случай.

Пример кода ниже показывает, как это работает:

var serializer = Json.EnсoderFor<Book>(config => config
                .MapType<Book>(map => map
                    .AllFields()
                    .Field<DateTime>(field => field.PubDate, pubDate => pubDate
                        .EncodeAs<string>(value => value.ToShortDateString()))
                    .Field<BookType>(field => field.Type, type => type
                        .EncodeAs<int>(value => (int)value)
                        .To("book_type")))
                .MapType<Author>(map => map.AllFields())
				.UseTidy(true) );
string jsonResult = serializer.Enсode(Program.bookA);


Если сериализуемый тип данных содержит свойства типа перечисления или DateTime, как в примере выше, то указывать преобразование для них обязательно. Самостоятельно Fluent Json не догадается, что с ними делать.

Результаты тестов:
Тест №1 Тест №2
.NET 52,5 сек 9 сек
Mono 34 сек 10 сек

JsonExSerializer


По заверениям разработчика, этот инструмент предназначен для точной сериализации/десериализации объектов, которую не дают другие библиотеки. Для меня же это не самое главное, тестировать буду на скорость. Пример кода:
var serializer = new Serializer(typeof(Book));
var memoryStream = new MemoryStream();
serializer.Serialize(Program.bookA,	memoryStream);
var jsonResult = Encoding.Default.GetString(memoryStream.GetBuffer());


Пикантной особенностью JsonEx, является то, что в результат он добавляет форматирование и комментарий вида:
/*
  Created by JsonExSerializer
  Assembly: JsonTestConsole, Version=1.0.4347.32516, Culture=neutral, PublicKeyToken=null
  Type: JsonTestConsole.Book
*/

Форматирование может быть полезно при сохранении, например, настроек в файл, который в дальнейшем может редактироваться в текстовом редакторе.

Результаты тестов:
Тест №1 Тест №2
.NET 32 сек 8 сек
Mono 34 сек 10 сек

Newton JSON.NET


В описании возможностей есть интригующая строчка «High performance, faster than .NET's built-in JSON serializers». Пример кода использующего Newton укладывается в одну строку:
string jsonResult = JsonConvert.SerializeObject(Program.bookA);


Какой либо предварительной инициализации не требует, при этом показывает очень хорошее время:
Тест №1 Тест №2
.NET 1.5 сек -
Mono 2 сек -


Заключение


Результаты тестирования отсортированные по возрастанию времени (в миллисекундах) для обоих конфигураций (столбец K показывает во сколько раз тест медленнее «конкатенации строк»):

Платформа Mono .Net Framework
Сериализатор Тест №1 Тест №2 K Тест №1 Тест №2 K
String concatination 600 - 1 250 - 1
Newton JSON.NET 2 000 - 3,4 1 500 - 6
JavaScriptSerializer 5 000 4 000 8,3/6,7 1700 1700 6,8
JsonExSerializer 34 000 10 000 56,7/16,7 4 000 3 500 16/14
Fluent Json 34 000 10 000 56,7/16,7 32 000 8 000 128/32
DataContractJsonSerializer 34 000 34 000 56,7/56,7 1 500 1 500 6

Из таблицы видно, что если есть планы гонять приложение под Mono или платформа не определена, то лучше обратиться к сторонней библиотеке Newton JSON.net — она показывает хорошие результаты как в Win+.NET так и Linux+Mono. Если целевая платформа .NET и обязательность атрибутов не смущает, достаточно встроенного DataContractJsonSerializer. Если оба варианта не устраивают, можно изобрести свою библиотеку — стремиться есть куда.

Код тестов здесь: code.google.com/p/research-net-json/source/browse/Main.cs. Сторонние библиотеки придётся выкачать самостоятельно, ещё раз ссылки:


UPD. Дополняю тестами ещё нескольких сериализаторов (спасибо atd,skyboy,Guderian и kekekeks):
Платформа Mono .Net Framework
Сериализатор Тест №1 Тест №2 K Тест №1 Тест №2 K
new StringBuilder(400) 550 - 0,9 270 - 1
ServiceStack 1 300 - 2,2 1 200 - 4,8
Fast Json 2 600 - 4,3 1 200 - 4,8
Jayrock 8 200 - 13,7 9 200 - 36,8

Как видно, появился новый лидер — ServiceStack.

P.S. Если Вы заметили ошибку, или Вам известны другие библиотеки, пишите, с радостью дополню статью.
Tags:
Hubs:
+43
Comments 26
Comments Comments 26

Articles