2016-11-29 1 views
0

Я широко использую хронику в Scala и недавно решил экспериментировать с сериализацией Kryo. Я добавил пользовательские маршаллеры (код ниже), и хотя он уменьшил размер моих магазинов на 14G (примерно 62%), и все еще работает, скорость была неприемлемой.Сериализация Крио в хронике Карта - медленное чтение байта

Я создал образец USECASE и сделал несколько спусков на один и те же данные

[Using kryo] took 6883, and then 7187, 6954, 7225, 13051 
[Not using kryo] took 2326, and then 1352, 1493, 1500, 1187 

Так что это в несколько раз медленнее. Вот маршаллер для чтения:

class KryoMarshallerReader[T] extends BytesReader[T] { 
    val kryo = // Reference to KryoPool from Twitter's Chill library 

    override def read(in: Bytes[_], using: T): T = { 

    val bytes = benchmark("array allocation") { 
     new Array[Byte](in.readRemaining().toInt) 
    } 


    benchmark("reading bytes") { 
     in.read(bytes) 
    } 


    benchmark("deserialising") { 
     kryo.fromBytes(bytes).asInstanceOf[T] 
    } 
    } 

    override def readMarshallable(wire: WireIn): Unit = {} 

    override def writeMarshallable(wire: WireOut): Unit = {} 
} 

Я усреднены время выполнения (все в мс) по сравнению с теми три этапа и понял, что чтение байт является самым медленным:

   stage Average time (ms) 
       (fctr)    (dbl) 
1 [array allocation]   0.9432907 
2 [deserialising]   0.9944112 
3 [reading bytes]  13.2367265 

Теперь вопрос - что я делаю неправильно?

Я просмотрел интерфейс Bytes[_] и, похоже, он читает байты один за другим - есть ли способ использовать буфер или что-то магически способное к массовой загрузке?

Update: В конце концов я изменил распределение массива + чтение байт in.toByteArray но он по-прежнему медленно, потому что под капотом у него копии байтов по одному. Просто работает читает на карте показывает, что чтение байт узкого места:

benchmarks from JProfiler

ответ

0

in.readRemaining() из байт, переданных в BytesReader.read(), не ваш объект в последовательной форме, это более чем , Сериализованная форма вашего объекта должна начинаться с in.readPosition(), но обычно она заканчивается намного раньше in.readLimit() (как readRemaining() = readLimit() - readPosition()). В общем случае пара реализаций BytesReader/BytesWriter должна заботиться об определении конца самих байтов объекта (если это необходимо), e. г. увидеть реализацию CharSequenceArrayBytesMarshaller в BytesReader and BytesWriter section of the Chronicle Map tutorial:

public final class CharSequenceArrayBytesMarshaller 
    implements BytesWriter<CharSequence[]>, BytesReader<CharSequence[]> { 
    ... 

    @Override 
    public void write(Bytes out, @NotNull CharSequence[] toWrite) { 
     out.writeInt(toWrite.length); // care about writing the size ourselves! 
     ... 
    } 

    @NotNull 
    @Override 
    public CharSequence[] read(Bytes in, @Nullable CharSequence[] using) { 
     int len = in.readInt(); // care about reading the size ourselves! 
     ... 
    } 
} 

Но поскольку ваши осуществляют Kryo сериализации, который должен быть концептуально похож на Java стандартной сериализации, вероятно, вы должны взять на себя источник SerializableReader и SerializableDataAccess и изменить его, чтобы использовать Kryo вместо стандартная сериализация Java (но обратите внимание, что эти источники лицензированы LGPLv3). В частности, эти реализации используют Bytes.inputStream() и Bytes.outputStream(), чтобы свести стандартную сериализацию Java, которая не знает о байтах, но знает о InputStream/OutputStream, без необходимости копировать байты. Я уверен, что Kryo поддерживает InputStream/OutputStream.

Будьте очень осторожны с kryo как поле экземпляра любого интерфейса последовательного интерфейса (BytesReader в вашем случае) без реализации StatefulCopyable. Вы можете легко ввести узкие места параллелизма или ошибки параллелизма (расы данных). Проверьте Understanding StatefulCopyable section in the Chronicle Map tutorial и Chronicle Map custom serialization checklist.

Смежные вопросы