Эффективная двоичная сериализация для Clojure / Java - PullRequest
5 голосов
/ 09 октября 2011

Я ищу способ эффективной сериализации объектов Clojure в двоичный формат, т. Е. Не просто для классической сериализации печати и чтения текста.

т.е. я хочу что-то сделатьнапример:

(def orig-data {:name "Data Object" 
                :data (get-big-java-array) 
                :other (get-clojure-data-stuff)})

(def binary (serialize orig-data))

;; here "binary" is a raw binary form, e.g. a Java byte array
;; so it can be persisted in key/value store or sent over network etc.

;; now check it works!

(def new-data (deserialize binary))

(= new-data orig-data)
=> true

Мотивация состоит в том, что у меня есть несколько больших структур данных, которые содержат значительное количество двоичных данных (в массивах Java), и я хочу избежать накладных расходов на их преобразование в текст и обратноснова.Кроме того, я стараюсь сохранить формат компактным, чтобы минимизировать использование пропускной способности сети.

Особые функции, которые я хотел бы иметь:

  • Легкая, чисто Java-реализация
  • Поддержка всех стандартных структур данных Clojure, а также всех примитивов Java, массивов и т. Д.
  • Нет необходимости в дополнительных шагах сборки / файлах конфигурации - я бы предпочел, чтобы это просто работало "изbox "
  • Хорошая производительность с точки зрения требуемого времени обработки
  • Компактность с точки зрения двоичного кодированного представления

Какой лучший / стандартный подход для этого в Clojure

Ответы [ 4 ]

8 голосов
/ 10 октября 2011

Я могу что-то здесь упустить, но что не так со стандартной сериализацией Java? Слишком медленный, слишком большой, что-то еще?

Оболочка Clojure для простой сериализации Java может выглядеть примерно так:

(defn serializable? [v]
  (instance? java.io.Serializable v))

(defn serialize 
  "Serializes value, returns a byte array"
  [v]
  (let [buff (java.io.ByteArrayOutputStream. 1024)]
    (with-open [dos (java.io.ObjectOutputStream. buff)]
      (.writeObject dos v))
    (.toByteArray buff)))

(defn deserialize 
  "Accepts a byte array, returns deserialized value"
  [bytes]
  (with-open [dis (java.io.ObjectInputStream.
                   (java.io.ByteArrayInputStream. bytes))]
    (.readObject dis)))

 user> (= (range 10) (deserialize (serialize (range 10))))
 true

Есть значения, которые нельзя сериализовать, например, Потоки Java и атом Clojure / агент / будущее, но он должен работать для большинства простых значений, включая примитивы и массивы Java, а также функции, коллекции и записи Clojure.

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

Но для особого случая, когда большая часть данных представляет собой массивы примитивов Java, сериализация Java может быть на несколько порядков быстрее и сэкономить значительную часть пространства. (Быстрый тест на ноутбуке, 100 Кб случайных байтов: сериализация 0,9 мс, 100 КБ; текст 490 мс, 700 КБ.)

Обратите внимание, что тест (= new-data orig-data) не работает для массивов (он делегирует Java 1015 *, который для массивов просто проверяет, является ли это один и тот же объект), поэтому вам может потребоваться / нужно написать собственную функцию равенства для проверить сериализацию.

user> (def a (range 10))
user> (= a (range 10))
true
user> (= (into-array a) (into-array a))
false
user> (.equals (into-array a) (into-array a))
false
user> (java.util.Arrays/equals (into-array a) (into-array a))
true
5 голосов
/ 16 июня 2013

Nippy - один из лучших вариантов imho: https://github.com/ptaoussanis/nippy

4 голосов
/ 09 октября 2011

Рассматривали ли вы Google protobuf ?Возможно, вы захотите проверить GitHub репозиторий с интерфейсом для Clojure.

3 голосов
/ 09 октября 2011

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

И наоборот, если вы действительно хотите оптимизированный формат проводов, вам нужно определить схему.Я использовал thrift из java и protobuf из clojure: и это не доставляет большого удовольствия, но если вы планируете заранее, это не будет ужасно обременительным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...