由已分配的 ByteString 支持的高效 okio 源?

Posted

技术标签:

【中文标题】由已分配的 ByteString 支持的高效 okio 源?【英文标题】:Efficient okio Source backed by an already allocated ByteString? 【发布时间】:2019-06-29 03:43:31 【问题描述】:

使用 OkHttp Websocket 时,listener 使用 ByteString 向应用程序提供二进制有效负载。 我想将这些字节输入到一些采用 okio.Source(在本例中为 GzipSource)的代码中,但我找不到任何有效地执行此操作的好方法。

我目前的解决方案是这样的:

        @Override
        public void onMessage(WebSocket webSocket, ByteString bytes) 
            Buffer gzipBuffer = new Buffer();
            gzipBuffer.write(bytes);

            GzipSource gzipSource = new GzipSource(gzipBuffer);
            ....
        

这个Buffer.write 的缺点是它会产生额外的字节副本(在 Buffer 的情况下,是分段的,即使池化也是额外的开销)。在这个 Websocket 案例中,一个字节数组只是为 ByteString 本身分配的(当从 WebSocketReader impl 移交时)。

我的问题是:是否有任何其他首选方式可以通过Source 读取ByteString? 由于ByteString 应该是不可变的,而Source 只会保存一些读取位置信息,我认为它应该是完全可行的(但不是来自外部代码,因为我无法访问byte[])..所以感觉我在这里错过了一个明显的解决方案.. :)

感谢任何提示或指点!

【问题讨论】:

【参考方案1】:

您编写它的方式几乎是最佳的。

Okio 已针对在转换层之间移动数据进行了优化:压缩、成帧、线程等。虽然在层之间移动数据非常快(无副本),但最初将数据导入系统是有前期成本的。这通常是您无论如何都需要做的 I/O:加载文件或发送数据包。但在这种情况下,您必须复制才能将字节放入第一个缓冲区。这感觉效率低下,但好处是你的下一个 Source.read() 调用会很快,所以整体效果仍然很好。

【讨论】:

感谢您的回复。您正在写“接近最佳”,还有改进的余地吗?进一步挖掘,我看到 GzipSource 依赖于具有内部缓冲区段的源(实际上是 BufferedSource)。我认为这就是需要这种前期负载的原因,因为它无法使用该实现在单个长 byte[] 上工作?因此,要支持这样的来源,它需要被重写,然后当然不能与其他来源兼容。 是的,就是这样。单个byte[] 不起作用。

以上是关于由已分配的 ByteString 支持的高效 okio 源?的主要内容,如果未能解决你的问题,请参考以下文章

Haskell语言学习笔记(49)ByteString Text

PubSub 模拟器 - ( 支持 Proto Buffer 发布/接收消息)

Haskell 中的内存高效字符串

如何将 Source[ByteString, Any] 转换为 InputStream

由已打开的文件读取数据---read

将 Data.ByteString.Lazy 转换为 CStringLen 的最有效方法