0

我在 Scala 中广泛使用 Chronicle Map,最近决定尝试 Kryo 序列化。我添加了自定义编组器(下面的代码),虽然它将我的商店大小减少了 14G(大约 62%),并且一切仍然正常,但速度令人无法接受。

我创建了一个示例用例并对相同的数据进行了几次运行

[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[_]它看起来像是一个一个地读取字节 - 有没有办法使用缓冲区或能够神奇地批量加载的东西?

更新:最终我将数组分配+读取字节更改为in.toByteArray但它仍然很慢,因为它在引擎盖下一个接一个地复制字节。仅在地图上运行读取表明字节读取是瓶颈:

JProfiler 的基准测试

4

1 回答 1

0

in.readRemaining()传递给 BytesReader.read() 的字节不是序列化形式的对象,它不仅如此。您的对象的序列化形式保证从 开始in.readPosition(),但它通常比in.readLimit()(as readRemaining()= readLimit()- readPosition()) 早得多。通常 BytesReader/BytesWriter 对的实现应该关心确定对象字节本身的结束(如果需要),例如参见 Chronicle Map 教程的CharSequenceArrayBytesMarshallerinBytesReaderBytesWriter部分的实现:

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 标准序列化,您可能应该获取SerializableReaderSerializableDataAccess修改它以使用 Kryo 而不是标准 Java 序列化(但请注意这些源是 LGPLv3 许可的)。特别是那些实现使用Bytes.inputStream()Bytes.outputStream()桥接标准 Java 序列化,它不知道字节,但知道InputStream/ OutputStream,而无需不必要地复制字节。我很确定 Kryo 也支持InputStream/ OutputStream

在不kryo实现. 您可能很容易引入并发瓶颈或并发错误(数据竞争)。检查Chronicle Map 教程Chronicle Map 自定义序列化清单中的理解部分。BytesReaderStatefulCopyableStatefulCopyable

于 2016-12-01T07:33:31.950 回答