Ни одна из перечисленных библиотек не выполняет преобразований вида: Object -> JSON -> byte[].
Либо Object -> JSON, либо Object -> byte[]. В последнем случае как раз и наблюдается более высокая скорость, чем у Java Standard.
В моем понимании эти понятия связаны с выводом в поток и восстановлением объекта из потока.
У сериализации другое определение, если посмотреть даже в Wikipedia:
In computer science, in the context of data storage, serialization (or serialisation) is the process of translating data structures or object state into a format that can be stored (for example, in a file or memory buffer) or transmitted (for example, across a network connection link) and reconstructed later (possibly in a different computer environment).
Для Java, фактически, сериализация — это только преобразование Object-а в byte[]|String|..., которые можно передать по сети/сохранить в БД и т.д., но эти действия не относятся к самой сериализации.
Останемся при своих мнениях, не вижу смысла их друг другу навязывать.
Нет, не путаю. Речь здесь идёт именно про микросекунды (тысячные доли миллисекунды). Это результат усреднения сотен тысяч повторений сериализации/десериализации, о чём я написал выше в комментарии.
Конечно, измерять с такой точностью системные часы компьютеров не позволяют.
Сериализованы ли объекты передаваемые через сеть?
Передачи через сеть в наших измерениях нет, только сериализация/десериализация.
Рекомендую ознакомиться с Java Microbenchmark Harness (JMH).
«JMH-бенчмарк без деталей» я привёл в одном из комментариев выше. В ближайшее время добавлю этот кусок Java-кода в тело самой публикации.
Во-первых, коллега, я не «умудрился намерить», а выполнил качественные измерения с использованием JMH — специального софта для измерения производительности JVM. Опубликованные результаты измерений многократно повторялись на нашем софте и железе.
Во-вторых, если вы взглянете на первые графики из раздела «Гонки», то увидите, что для Java Standard цикл сериализации/десериализации данных размером порядка 1 КБ (примерно ваш размер) у нас занял 0,007 + 0,021 = 0,028 мс. У вас же получилось 4 мс за 2 цикла сериализации/десериализации + сетевые задержки. Это, без учёта сети, в 2000/28=~71 раз медленнее нашего результата. И где здесь «плачевный» результат?..
Кстати, исходя из того, что сама фаза измерения в нашем случае длилась 5 сек (я это указывал в начале «Гонок»), то для получения значения 0,007 мс по сериализации у нас ушло 714 285 повторений с усреднением результата. Для десериализации (0,021 мс) было использовано 238 095 повторений. Эти цифры кратно больше ваших 71 000 запросов, что говорит о том, что точность опубликованных измерений выше, чем в ваших «измерителях».
Java Standart — делает подобное, на мой взгляд, с наименьшими преобразованиями данных по сравнению со всем остальным перечисленным вами.
JBoss Serialization исключили из участников на основании вот этого теста: eishay/jvm-serializers
Там сериализация JBoss сильно уступает Jackson Smile-у, Kryo и FST, которые попали в наше исследование.
У One Nio те же плюсы, за одним непринципиальным исключением:
There is limited support for readObject/writeObject. These methods will be called, but they should not work with the stream directly. The only stream methods they may call are defaultReadObject and defaultWriteObject. Other calls will result in exception.
Externalizable is completely supported.
Да, мы смотрели в эту сторону, но предложенный способ использования аннотации @Version не подходит для нашей задачи, когда классы сериализуемых объектов готовятся потребителями нашего сервиса, а для самого сервиса это «чёрный ящик».
Кусок javadoc-а для аннотации @Version из FST
/**
* support for adding fields without breaking compatibility to old streams.
* For each release of your app increment the version value. No Version annotation means version=0.
* Note that each added field needs to be annotated.
*
* e.g.
*
* class MyClass implements Serializable {
*
* // fields on initial release 1.0
* int x;
* String y;
*
* // fields added with release 1.5
* @Version(1) String added;
* @Version(1) String alsoAdded;
*
* // fields added with release 2.0
* @Version(2) String addedv2;
* @Version(2) String alsoAddedv2;
*
* }
Как видим, нашим потребителям пришлось бы «заморочиться» со своими классами, сохраняемыми в сессию. А ведь из-за цепочек зависимостей объектов потребитель может даже не осознавать, что объекты какого-то из классов он сохраняет в сессию…
Таким образом, по умолчанию («не задавая лишних вопросов») у FST нет обратной совместимости со старыми сериализуемыми классами.
Если честно, то мне сложно представить case-ы, когда реально, а не надуманно, было бы необходимо поддержать смену типа у поля при развитии класса. Если меняется тип поля, то, вероятнее всего, это уже другое поле.
Почему-то если имя не совпадает, то поле останется null, а если тип не совпадает, то это критическая ошибка.
Потому что имя — это всего лишь строка, а тип определяет структуру данных. Смена типа — гораздо более серьёзное изменение, нежели смена имени поля.
Потюнить Jackson, конечно, интересно, и я постараюсь это сделать, как будет время. Спасибо за наводки.
Однако добавлять геттеры и сеттеры в сериализуемые объекты — это не то, что нам нужно: объекты готовят потребители нашего сервиса, а мы должны быстро сериализовывать всё подряд.
Как я написал во введении, нам нужна библиотека, «не задающая лишних вопросов».
Такое поддерживает только One Nio (for persist) и только в оригинальном виде с использованием sun.reflect.MagicAccessorImpl. Об этом написано в сноске #4 под таблицей раздела «Гибкость».
И да, у других библиотек не удалось найти даже custom-изируемых средств достижения данного свойства.
Нет, это будет работать при достаточно обширном наборе различий в классах источника и получателя. В этом и состоит гибкость.
Например, у отправителя появились новые поля в классе — более старый получатель просто их проигнорирует. У получателя появились новые поля — при получении данных от более старого отправителя новые поля останутся null-ами, либо значениями по умолчанию. Данных примеров с отличиями в классах отправителя и получателя масса.
Обратите внимание на столбик One Nio (for persist) в таблице раздела «Гибкость». Почти по каждому критерию-сценарию там зачтённый бал.
Описание @Benchmark-ов и сериализуемых объектов привёл выше.
Используемые для сериализации данные, боюсь, что показать не могу, т.к. это реальные данные из логов системы.
При увеличении размера сериализуемых объектов до 1 МБ кривые на графиках скорости предсказуемо расходятся практически по прямым линиям. Победители и аутсайдеры видны уже, начиная с 10 КБ.
В целом, сериализация Java объектов может быть применима для большого круга задач, была бы фантазия. Но конкретный profit нужно мерить.
Как уже писал, в следующих публикациях я постараюсь выполнить сравнительный анализ на разных JVM. Более широкий круг сериализаторов?.. Возможно, если обнаружатся достаточно быстрые аналоги.
Описание @Benchmark-ов и сериализуемых объектов привёл выше. БОльшая часть «участников соревнования» используют бинарный формат, а не JSON.
Динамический обмен схемами в One Nio мы видели и, честно говоря, впечатлились. Я попытался отразить суть этой процедуры в разделе «Гибкость», когда говорил про одновременное использование достоинств One Nio (скорость) и One Nio (for persist) (гибкость). Из коробки RpcClient/RpcServer «as is» нам не подошли из-за нашей специфики, поэтому мы делаем обмен схемами немного по-своему.
На счёт генерируемых заглушек при десериализации классов, отсутствующих в classpath. Действительно, эта любопытная механика заслуживает галочки в таблице раздела «Гибкость» — скорректирую. Спасибо за наводку, изначально я не разглядел всю прелесть этой фичи.
Ни одна из перечисленных библиотек не выполняет преобразований вида: Object -> JSON -> byte[].
Либо Object -> JSON, либо Object -> byte[]. В последнем случае как раз и наблюдается более высокая скорость, чем у Java Standard.
Для Java, фактически, сериализация — это только преобразование
Object-а вbyte[]|String|..., которые можно передать по сети/сохранить в БД и т.д., но эти действия не относятся к самой сериализации.Останемся при своих мнениях, не вижу смысла их друг другу навязывать.
Конечно, измерять с такой точностью системные часы компьютеров не позволяют.
Передачи через сеть в наших измерениях нет, только сериализация/десериализация.
Рекомендую ознакомиться с Java Microbenchmark Harness (JMH).
«JMH-бенчмарк без деталей» я привёл в одном из комментариев выше. В ближайшее время добавлю этот кусок Java-кода в тело самой публикации.
Во-вторых, если вы взглянете на первые графики из раздела «Гонки», то увидите, что для Java Standard цикл сериализации/десериализации данных размером порядка 1 КБ (примерно ваш размер) у нас занял 0,007 + 0,021 = 0,028 мс. У вас же получилось 4 мс за 2 цикла сериализации/десериализации + сетевые задержки. Это, без учёта сети, в 2000/28=~71 раз медленнее нашего результата. И где здесь «плачевный» результат?..
Кстати, исходя из того, что сама фаза измерения в нашем случае длилась 5 сек (я это указывал в начале «Гонок»), то для получения значения 0,007 мс по сериализации у нас ушло 714 285 повторений с усреднением результата. Для десериализации (0,021 мс) было использовано 238 095 повторений. Эти цифры кратно больше ваших 71 000 запросов, что говорит о том, что точность опубликованных измерений выше, чем в ваших «измерителях».
Субъективное суждение, это лично ваше мнение.
Там сериализация JBoss сильно уступает Jackson Smile-у, Kryo и FST, которые попали в наше исследование.
@Versionне подходит для нашей задачи, когда классы сериализуемых объектов готовятся потребителями нашего сервиса, а для самого сервиса это «чёрный ящик».Как видим, нашим потребителям пришлось бы «заморочиться» со своими классами, сохраняемыми в сессию. А ведь из-за цепочек зависимостей объектов потребитель может даже не осознавать, что объекты какого-то из классов он сохраняет в сессию…
Таким образом, по умолчанию («не задавая лишних вопросов») у FST нет обратной совместимости со старыми сериализуемыми классами.
Потому что имя — это всего лишь строка, а тип определяет структуру данных. Смена типа — гораздо более серьёзное изменение, нежели смена имени поля.
Однако добавлять геттеры и сеттеры в сериализуемые объекты — это не то, что нам нужно: объекты готовят потребители нашего сервиса, а мы должны быстро сериализовывать всё подряд.
Как я написал во введении, нам нужна библиотека, «не задающая лишних вопросов».
Пока могу показать лишь «скелет» без деталей реализации:
За абстракцицей
Serializerскрыты все 14 исследуемых реализаций Java-сериализаторов.sun.reflect.MagicAccessorImpl. Об этом написано в сноске #4 под таблицей раздела «Гибкость».И да, у других библиотек не удалось найти даже custom-изируемых средств достижения данного свойства.
Думаю, может получиться. Подумаю, как лучше сделать, и обязательно учту это пожелание.
У нас всё по-честному :)
Например, у отправителя появились новые поля в классе — более старый получатель просто их проигнорирует. У получателя появились новые поля — при получении данных от более старого отправителя новые поля останутся
null-ами, либо значениями по умолчанию. Данных примеров с отличиями в классах отправителя и получателя масса.Обратите внимание на столбик One Nio (for persist) в таблице раздела «Гибкость». Почти по каждому критерию-сценарию там зачтённый бал.
@Benchmark-ов и сериализуемых объектов привёл выше.Используемые для сериализации данные, боюсь, что показать не могу, т.к. это реальные данные из логов системы.
При увеличении размера сериализуемых объектов до 1 МБ кривые на графиках скорости предсказуемо расходятся практически по прямым линиям. Победители и аутсайдеры видны уже, начиная с 10 КБ.
В целом, сериализация Java объектов может быть применима для большого круга задач, была бы фантазия. Но конкретный profit нужно мерить.
Описание
@Benchmark-ов и сериализуемых объектов привёл выше. БОльшая часть «участников соревнования» используют бинарный формат, а не JSON.Структура сериализуемых/десериализуемых данных при «Гонках» и «Взвешивании» была примерно следующая:
String,Long,Map,byte[]Микробенчмарки тривиальные:
@Setup-методе готовим сериализатор, объект для сериализации иbyte[]с результатами сериализации (для benchmark-а десериализации)@Benchmark-ах просто выполняем serialize/deserialzeНа счёт генерируемых заглушек при десериализации классов, отсутствующих в classpath. Действительно, эта любопытная механика заслуживает галочки в таблице раздела «Гибкость» — скорректирую. Спасибо за наводку, изначально я не разглядел всю прелесть этой фичи.