使用ScalaPB解析ByteString时如何避免数组复制?
解析com.google.protobuf.ByteString的最优实践
为什么Foo.parseFrom(bs.toByteArray())更快
你观察到的JMH结果符合protobuf的内部实现逻辑:多数情况下ByteString是ByteArrayByteString的实例(直接包装了一个字节数组),此时调用toByteArray()会直接返回内部数组的引用,完全没有数组复制开销;而bs.newInput()会创建CodedInputStream包装,引入了流读取的间接层和额外的方法调用开销,反而拖慢了速度。
是否存在更优方法
在绝大多数场景下,Foo.parseFrom(bs.toByteArray())已经是最优选择,原因如下:
- protobuf对
ByteString.toByteArray()做了针对性优化:当内部存储是原生数组时直接返回引用,无复制;仅当ByteString是基于其他来源(如分段数组、只读包装)时才会触发复制,但这种场景占比极低。 - 尝试绕过
toByteArray()的操作(比如直接强转ByteArrayByteString获取内部数组)存在风险:ByteArrayByteString是protobuf的包私有类,直接强转会导致编译或运行时的访问权限问题,且protobuf版本迭代可能会修改内部实现,破坏代码兼容性。
如果追求极致性能且能确保ByteString的内部类型,可以考虑直接使用CodedInputStream的原生实例,但实际收益微乎其微,写法如下:
CodedInputStream input = CodedInputStream.newInstance(bs); Foo foo = Foo.parseFrom(input); input.checkLastTagWas(0); // 确保完整解析
但这种方式的性能表现不会超过toByteArray()版本,因为本质还是基于流的读取逻辑。
总结:你的JMH测试结果已经给出了最优解——优先使用Foo.parseFrom(bs.toByteArray())。
内容的提问来源于stack exchange,提问作者Yann Moisan
相关产品推荐
相关产品推荐

