Как стать автором
Обновить

Комментарии 15

Можно вместо putFields просто писать данные в поток.


private fun writeObject(s : ObjectOutputStream) {
    s.putObject(strField as Any?)
    s.putInt(intFieldChanged)
  }

В этом случае имена полей не сохраняются, сохранённый класс будет занимать меньше места, а сериализация будет работать в несколько раз быстрее.
Конечно, я не призываю всё время делать так, но это очень удобная возможность: можно использовать сначала стандартную сериализацию, а потом при необходимости "ускорять" только медленные классы, не трогая всё остальное.

Методы putFields \ readFields используются немного для другого.
Они предназначены для загрузки полей, сохраненных ранее с другими именами или типами и, соответственно, для сохранения текущих полей в формате, который отличается от существующего в объекте.
Добиться такого поведения простым сохранением объекта невозможно. Вернее, чтобы это обеспечить придется все сохранение\восстановление реализовывать самостоятельно.


Вообще, после тестирования я лично от стандартной сериализации решил отказаться совсем.
Чтобы она работала быстро ее приходится реализовывать очень многословно и решать много проблем и, при этом, из-за особенностей формата, сохраненный результат оказывается сильно зависим от того формата объекта, который использовался при сохранении.
В результате, получаем либо медленно но универсально, либо быстро но кропотливо, т.е. штатная сериализация имеет неоспоримое преимущество в довольно узком диапазоне задач.

Большинство библиотек позиционируют себя как "на порядок быстрее чем ХХХ".
К примеру библиотека "minimal-json", использованная в тесте, попала в него именно благодаря само-позиционированию, как одна из самых скоростных. К сожалению, мои измерения это не подтвердили.


Библиотека "RuedigerMoeller/fast-serialization", конечно, в 10 раз быстрее физически быть не может, но, возможно она действительно быстрая.
Можно ее добавить в тест, если это кому-нибудь интересно.

Насколько можно доверять вашим замерам? Полноценный бенчмаркинг дело нелегкое, люди обычно пишут используют фреймворки, чтобы замеры были как можно более чистыми. По типу https://github.com/dotnet/BenchmarkDotNet для .NET

Абсолютно нельзя доверять.
Ровно как и любому другому бенчмарку в любой области, буть то Antutu для телефонов или мой тест для библиотеки сериализации.


Любой тест — это замер очень конкретных кода и данных. Единственное что можно сделать при тестировании универсальным — это нивелировать влияние платформы на которой запускается тест на его результаты. Я постарался это сделать. В остальном же, результаты будут очень сильно зависеть от той модели данных, которой оперирует тест.


Возьмем, к примеру, библиотеку "minimal-json". Я не утверждаю что тесты, опубликованные на ее странице являются полным враньем только потому, что на моих данных она демонстрирует в разы худшие результаты. Различные данные накладывают свой отпечаток на результаты.
Если посмотреть тесты, которые используются для этой библиотеки, то видно, что они основаны на очень маленьких наборах данных (примеры для тестов по 20-50Кб), тогда как в моем тесте за один раз обрабатывались объемы данных на два порядка больше.
Во-первых, мне лично неинтересны результаты тестирования для маленьких наборов данных и, во-вторых, я считаю что работа с большими объемами демонстрирует преимущества и недостатки библиотек гораздо более ярко. Поэтому я считаю что мой тест для этой конкретной библиотеки демонстрирует более адекватные результаты чем те, которые приведены на ее сайте.


Итого, возвращаясь к вопросу о доверии.


  1. Если данные, которыми Вы планируете оперировать более похожи на мои по объему и\или типу, то доверять соотношению производительности сравниваемых средств вполне можно. Если бы я так не считал, то не опубликовал бы статью вообще :)


  2. В любом случае, практически в любом тесте можно доверять соотношения. Если какой-то код в более чем одном тесте оказывается медленнее другого, то можно сделать вполне адекватный вывод о том, что он будет всегда работать медленнее. Другое дело, что на разных данных величина различия может быть разной, но само отношение все равно сохранится.


  3. Простейшая библиотека для парсинга xml (!), которую я использую повседневно, работает на порядок (!!) быстрее лидеров этого теста. Правда реализована она на С++.
    Я специально убил довольно много времени на утилиту тестирования и на эту статью для того, чтобы не только поделиться своими результатами, но и обеспечить возможность всем провести собственные тесты именно для JVM.
    Если возникают сомнения в моих результатах, то можно вынуть исходники утилиты тестирования, собрать ее, погонять в разных условиях и сделать собственные выводы.
    Переделать модель используемых данных в ней будет довольно сложно, но поиграть с объемами можно легко.
Вы тут про Serializable, вот я и написал про FST.
FST, в первую очередь, интересна как JDK Serialization compatible drop-in replacement. И она действительно неплоха в этом качестве.

Ткни плз пальцем в конкретный пример ее использования.
Я почитал документацию, но мне показалось ее использование излишне усложненным, а простые примеры не папались.

НЛО прилетело и опубликовало эту надпись здесь
Что это значит? В общем случае List и Map не Serializable, не знаю что имеется ввиду под Tree.

Это значит, что поля с данными такого типа Serializable сохранит и восстановит автоматически.
Иллюстрация:


package jm.test.ktest

import java.io.File
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import java.io.Serializable

class AA(ar : List<String>) : Serializable {
  @JvmField var list = ar

  override fun toString() : String =
    "AA( ${list.joinToString(",")} )"
}

fun main(args : Array<String>) {

  val a = AA(listOf("One", "Two", "Three"))
  println("Orig: $a")
  ObjectOutputStream(File("out.bin").outputStream()).use {
    it.writeObject(a)
  }

  ObjectInputStream(File("out.bin").inputStream()).use {
    println("Read: " + (it.readObject() as AA) )
  }
}

Вывод:


Orig: AA( One,Two,Three )
Read: AA( One,Two,Three )

Наверное я криво это описал в тексте?
Стоит как-то исправить или примера выше будет достаточно?


На счет аннотаций Kotlin спасибо, почерпну.
Вообще в Kotlin крайне много недокументированного, что часто раздражает.
Например пришлось из его исходников вырезать списки ошибок и предупреждений, чтобы знать какой текст писать в @Suppress("?")

НЛО прилетело и опубликовало эту надпись здесь

Согласен, можно реализовать интерфейс без маркера Serializable.


Переформулирую так:


Поддерживается сохранение и восстановление любых объектов, которые имеет интерфейс-маркер Serializable. В частности, будут автоматически сохраняться все стандартные JDK коллекции, основанные на List, Set и Map т.к. все их реализации этот маркер имеют.


подходит?

НЛО прилетело и опубликовало эту надпись здесь

Дык это и правильно — описание и должно соответствовать документации, иначе это дезинформация :)
Правда эта документация…
В документации Java есть многое, но проблема в том, что мест где все это сведено воедино с примерами применения и ссылками на аналоги, как правило нет и приходится рыть кучи альтернативных источников.
Из-за этого, собственно, и свел все в один текст.


ПС: Текст статьи поправил, спасибо за уточнение.
Отдельное спасибо за наводку о возможности реализовывать интерфейсы делегатами. Не знал об этом :)

К сожалению, это плохое решение на практике, так как часто в качестве типа чего угодно (полей, параметров, возвращаемых значений) используется просто List, и по нему статически неясно, сериализуемый он или нет. Если объявить класс Foo с такой сигнатурой, он будет несовместим с большинством существующего кода. Даже какие-нибудь Collections.unmodifiableList возвращает просто List, который сериализуем, если поданный на вход список сериализуем, и нет в противном случае. Здесь в системе типов джавы определённый провал.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории