百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

Protobuf 不建议在 Deno 中使用 Protobuf

liebian365 2025-02-26 12:41 1 浏览 0 评论

Ryan Dahl 最初选择了 golang,后来又将 golang 从 deno 中彻底删除。前几天 Protobuf 的作者 Kenton Varda(kentonv) 开了一个 issue:Protobuf seems like a lot of overhead for this use case? #269,在文中 kentonv 指出:

I was surprised by the choice of Protobuf for intra-process communications within Deno. Protobuf’s backwards compatibility guarantees and compact wire representation offer no benefit here, while the serialize/parse round-trip on every I/O seems like it would be pretty expensive.

大概意思是:kentonv 对于 Deno 选择 Protobuf 感到很吃惊,因为 Protobuf 的兼容性优势并不是 Deno 需要的,相反,Protobuf 的序列化和反序列化非常消耗 I/O 性能。

1. Cap’n Proto 性能

kentonv 离开 Google 之后开发了 Cap’n Proto。

Cap’n Proto 相比 Protobuf 到底有多快呢?10 倍?100 倍?1000倍?官网给出了一张对比图:

Cap’n Proto 的编码解码速度是 Protobuf 的 8倍。

其实这张图是个标题党,图中对比了两者的编码解码,但是在 Cap’n Proto 中,是根本不需要编码和解码(encoding/decoding)的。Cap’n Proto 编码后的数据格式直接存放在内存,数据结构跟在内存里面的布局保持一致,所以可以直接将编码好的结构根据字节存放到硬盘,或者通过网络传输。

这是不是意味这 Cap’n Proto 编码是特定于平台的?

不!Cap’n Proto 采用的按字节编码方案是独立于任何平台的,但在如今主流的通用 CPU 上面会有更好的性能。数据的组织类似于编译器对 struct 的组织形式:固定宽度,固定偏移,以及内存对齐,对于可变的数组元素使用指针,而指针也是使用的偏移存放而不是绝对地址。整数使用的是小端序,因为大多数现代 CPU 都是小端序的,甚至大端序的 CPU 通常有读取小端序数据的指令。

注:大端序(big-endian)和小端序(little-endian)统称为字节顺序。对于多字节数据,例如 32 位整数占据 4 字节,在不同的处理器中存放方式也不同,以内存中 0x0A0B0C0D 的存放方式为例:

在大端序中,如果数据以 8bit 为单位进行存储,则最高位字节 0x0A 存储在最低的内存地址处。

地址增长方向 →
0x0A, 0x0B, 0x0C, 0x0D

如果数据以 16bit 为单位进行存储,则最高的 16bit 单元 0x0A0B 存储在低位:

地址增长方向 →
0x0A0B, 0x0C0D

而小端序则与此相反。目前大多数主流 CPU 都是小端序的,这也是 Cap’n Proto 采用小端序的原因。

如果熟悉 C 或者 C++ 的结构体,可以看到 Cap’n Proto 的编码方式跟 struct 的内存结构很相似。即使在 V8 引擎内部,也是使用了类似的结构来进行属性的快速读取。相比使用 Hash Map 有很高的性能提升。

2. 序列化/反序列化

Protobuf 每次都会构建一个用于表示 message 的对象,然后将对象序列化为 ArrayBuffer,在消息的接收方需要从缓冲区读取 message,然后解析为一个对应的对象,在之后的编程中使用该对象。而在 Cap’n Proto 中消息的结构直接存放在 ArrayBuffer 上,当我们调用 message.setFoo(123) 时,实际上就类似于 uint32Array[offset] = 123,在消息的接收方,我们可以直接从缓冲区读取这条消息。

Protobuf 可以使用变宽的编码,这样对于某些场景可以有更小的编码长度。而 Cap’n Proto 为了性能考虑会把整数编码为固定宽度,额外的字节使用 0 进行填充(这种存储方式很类似于 memcached)。一个是以空间换时间,一个是以时间换空间。在通过网络发送消息时,我们希望消息体越小越好,但是如果在同一地址空间内通信时,则我们有无限带宽。Cap’n Proto 的文档中还指出,当带宽真的很重要时,无论您使用何种编码格式,都应该对消息体进行通用压缩,如 zlib 或 LZ4。

3. FlatBuffers

deno 的作者 ry 也在 issue 中参与了讨论,对大家的热情关注 ry 感到十分感动,然后。。。。然后创建了一个 flatbuffers 分支

FlatBuffers 同样是一个 created at Google 的库,具有更加完善的文档以及 Benchmarks。而 Cap’n Proto 除了那个“无限倍速”的不公平测试外,没有任何的基准测试数据。

而 kentonv 对基准测试的态度是:

关于基准测试 - 我花了很多时间对序列化系统进行基准测试,不幸的是,我的结论是基准测试结果几乎总是毫无意义。

一个真正有意义的基准测试,需要使用两种不同的序列化来编写两个版本的实际应用程序,并对它们进行比较…但这是几乎没有人做过的大量工作。

这确实是个大工程。相比而言,V8 和 Chrome 每次发布都会进行 Real-world JavaScript performance

4. 安全

在当前 deno 的 protobuf 使用上,每个消息都会创建一个副本。deno 使用 protobuf 只是为了在 V8 和其他特权代码之间通讯,即使真的明确需要一个消息副本,那么也可以直接使用 memcpy() 来达到更高的性能。

如果在同一个缓冲区(ArrayBuffer),当不同的线程同时操作时,则需要一个副本来防止 TOCTOU 漏洞,或者谨慎的处理 JavaScript 代码,但这是不可控的,因为你不能防止第三方模块也做相同的假设(如果第三方扩展也使用相同的通讯机制的化)。

TOCTOU 的全程是“time of check to time of use”,TOCTOU 是竞争条件缺陷的一种。在多线程、多核系统中,这个漏洞很普遍。当我们访问某个共享资源时,系统首先会检测当前用户或代码是否有权限,而检查(check)和使用(use)是分离的,而且不是原子的。当系统检查资源被授予用户权限后,攻击者可以临时阻塞调用户线程,然后在时间差内替换调资源,以达到越权访问的目的。

举个简单的例子:

if (hasPermission("file")) {
 // (1)
 buffer = open("file");
 // (2) dosth
 write("file", buffer);
 // (3)
}

而攻击者可以在 (1) 处构造如下代码:

// ...
// hasPermission 检查通过
symlink("/etc/passwd", "file");
// 文件打开之前
// ...

这样用户就越权拿到了 "/etc/passwd" 的控制权。

上面只是一个简单的例子,TOCTOU 有很多不同的形式。在类 Unix 系统上,/tmp 和 /var/tmp 目录经常会被错误地使用,从而导致竞争条件。

为了安全而暂时损失性能是一种不得已的妥协,之前 V8 也遇到过,对于逃逸分析的漏洞直接导致了安全问题,Chrome 团队不得不在下一个发行版中去除了逃逸分析。

5. 综上

Deno 就像一个出生不久的孩子,Ryan Dahl 也在不停的探索,难免会走一些弯路。而作为普通开发者的我们,可以关注 deno 的源码以及 github 上的 commit。#程序员#

对于一个非常成熟的项目,比如 Node.js,我们很难读懂他的全部源码,甚至我们都不知道从何读起。而 deno 则是一个机会,我们见证了 deno 的诞生,截至到写这篇文章,deno 一共才有 249 次 commit。#真相来了#

相关推荐

20 个 2020 年软件开发趋势预测

企业上云已成不可逆的趋势,全面云计算时代宣告来临,微服务已成软件架构主流,Kubernetes将会变得更酷,2020年还有哪些技术趋势值得观察?一起来看!1.基础设施:条条道路通云端对于云厂商来...

目录发布!安徽这些紧缺人才急需

《安徽省5G产业急需紧缺人才目录(2020-2025)》(以下简称目录)近日正式发布。本次调研调查了216家代表企业、6家头部企业,获取了426份有效问卷,分析安徽省5G产业紧缺人才需求现状,其中产品...

AI树莓派——构建树莓派大脑(NCNN环境搭建)

前言镜像已经做好了,传到百度网盘中了(请大家及时保存,不定期删除!)...

把远程进程通讯grpc引入到Spring boot maven项目中

1、参考链接:gRPC官网:https://grpc.io/HTTP2:https://http2.github.io/...

面向数据的架构

在软件架构中,有一种模式虽鲜为人知的,但值得引起更多的关注。面向数据的架构(Data-OrientedArchitecture)由RajiveJoshi在RTI的2007年白皮书中首次提出,...

Go语言11岁了,网友:他喵的,终于确定出「泛型」了

金磊发自凹非寺量子位报道|公众号QbitAI比Python更快,比Java更简洁,还有C++没有的GC...

深度剖析数据库国产化迁移之路

作者|吴夏,腾讯云TDSQL高级工程师责编|唐小引头图|CSDN下载自东方IC出品|CSDN(ID:CSDNnews)随着国家有关部门近年来陆续出台相关政策指导文件,推动探索安...

一文掌握物体检测库TensorFlow 2.x Object Detection安装

...

团队协作-代码格式化工具clang-format

环境:clang-format:10.0.0前言统一的代码规范对于整个团队来说十分重要,通过git/svn在提交前进行统一的ClangFormat格式化,可以有效避免由于人工操作带来的代码格式问题。...

嵌入式大杂烩周记 第 9 期:nanopb

大家好,我是杂烩君。...

开源鸿蒙 OpenHarmony 3.1 Beta 版本发布:系统基础能力增强

IT之家1月2日消息,OpenAtom社区已于12月31日发布了OpenHarmony-v3.1-Beta版本。版本概述当前版本在OpenHarmony3.0LTS的基础...

零基础物联网开发,踩坑无数,得到这份宝典 | 原力计划

作者|Haor.L责编|王晓曼出品|CSDN博客笔者最近参加了校内的一场物联网开发竞赛,从零开始,踩坑无数,感觉很多时候事情都不像预料的一样发展,离开了美好的IDE,太多事情要在板子上一步...

gRPC:Google开源的基于HTTP/2和ProtoBuf的通用RPC框架

gRPC是一个高性能、通用的开源RPC框架,其由Google主要面向移动应用开发并基于HTTP/2协议标准而设计,基于ProtoBuf(ProtocolBuffers)序列化协议开发,且支持众多开发...

搜狗开源srpc:自研高性能通用RPC框架

今年7月底,搜狗公司开源了内部的工业级C++服务器引擎Workflow,一路收获业内许多认可和关注。9月15日,作为Workflow最重要的生态项目——srpc,一个基于其打造的轻量级RPC框架,也在...

WebSocket与Protobuf在现代网络通信中的应用实践

在现代网络通信中,WebSocket和Protobuf已成为构建高效、跨平台通信系统的关键技术。本文将详细介绍如何使用这两种技术来实现一个稳定且高效的网络通信系统。...

取消回复欢迎 发表评论: