Fastpb
A faster Protobuf serializer & deserializer.
Attention
- only proto3 is supported now.
- known-types(any, api, duration...) is not supported now.
Install
go install github.com/cloudwego/fastpb/[email protected]
Usage
Refer to examples, use in two steps:
- use fastpb to generate code. (refer here)
- use fastpb API to marshal/unmarshal. (refer here)
Step 1: Generate Code
Using the command line tool to generate code:
protoc --go_out=. \
--fastpb_out=. \
${your_idl}.proto
or
protoc --go_opt=paths=source_relative --go_out=. \
--fastpb_opt=paths=source_relative --fastpb_out=. \
${your_idl}.proto
Step 2: Codec Message
Encoding and Decoding must use fastpb's API, shown as demo:
package examples
import (
"github.com/cloudwego/fastpb"
)
// Marshal .
func Marshal(msg fastpb.Writer) []byte {
// TODO: buffer can be reused.
buf := make([]byte, msg.Size())
msg.FastWrite(buf)
return buf
}
// Unmarshal .
func Unmarshal(buf []byte, msg fastpb.Reader) error {
_, err := fastpb.ReadMessage(buf, int8(fastpb.SkipTypeCheck), msg)
return err
}
Performance
goos: linux
goarch: amd64
pkg: github.com/cloudwego/fastpb/benchmark
cpu: Intel(R) Xeon(R) Gold 5118 CPU @ 2.30GHz
Benchmarks have compared golang/protobuf (referred to as _golang
) and fastpb here.
Marshal
Benchmark_marshal_number_golang-48 | 375.2 ns/op | ~ | 96 B/op | ~ | 1 allocs/op |
Benchmark_marshal_number_fastpb-48 | 145.7 ns/op | -61.17% | 0 B/op | -100.00% | 0 allocs/op |
Benchmark_marshal_string_golang-48 | 1010 ns/op | ~ | 2304 B/op | ~ | 1 allocs/op |
Benchmark_marshal_string_fastpb-48 | 58.57 ns/op | -94.20% | 0 B/op | -100.00% | 0 allocs/op |
Benchmark_marshal_list_golang-48 | 8788 ns/op | ~ | 18432 B/op | ~ | 1 allocs/op |
Benchmark_marshal_list_fastpb-48 | 3430 ns/op | -60.97% | 0 B/op | -100.00% | 0 allocs/op |
Benchmark_marshal_map_golang-48 | 43497 ns/op | ~ | 21680 B/op | ~ | 393 allocs/op |
Benchmark_marshal_map_fastpb-48 | 5951 ns/op | -86.32% | 0 B/op | -100.00% | 0 allocs/op |
Unmarshal
Benchmark_unmarshal_number_golang-48 | 497.1 ns/op | ~ | 144 B/op | 1 allocs/op |
Benchmark_unmarshal_number_fastpb-48 | 431.6 ns/op | -13.18% | 144 B/op | 1 allocs/op |
Benchmark_unmarshal_string_golang-48 | 939.7 ns/op | ~ | 2128 B/op | 3 allocs/op |
Benchmark_unmarshal_string_fastpb-48 | 668.4 ns/op | -28.87% | 2128 B/op | 3 allocs/op |
Benchmark_unmarshal_list_golang-48 | 12527 ns/op | ~ | 20296 B/op | 99 allocs/op |
Benchmark_unmarshal_list_fastpb-48 | 12593 ns/op | +0.53% | 20296 B/op | 99 allocs/op |
Benchmark_unmarshal_map_golang-48 | 49868 ns/op | ~ | 24226 B/op | 426 allocs/op |
Benchmark_unmarshal_map_fastpb-48 | 21213 ns/op | -57.46% | 21467 B/op | 61 allocs/op |
当idl 有子目录时
Describe the bug
user.proto
和pet.proto
子文件夹中(如idl/user/user.proto
,idl/pet/pet.proto
)时出现引用,如pet/pet.proto
中引用了user/user.proto
, 生成的user.pb.go
的 中的protoreflect.FileDescriptor
变量名会是File_user_user_proto
,而pet.fast.pb.go
中最后一行生成的却是var _ = user.File_user_proto
,从而导致报错不可用fix: add import guard
What type of PR is this?
fix
What this PR does / why we need it (en: English/zh: Chinese):
en: add import guard to prevent import not being used zh: 增加 import guard 来防止 import no used 错误
Which issue(s) this PR fixes:
none
fix: ReadFieldError not used if struct has no fields
What type of PR is this?
fix
What this PR does / why we need it (en: English/zh: Chinese):
en: fix: ReadFieldError not used if struct has no fields zh: 修复定义空 struct 会导致生成多余的 ReadFieldError 的问题
Which issue(s) this PR fixes:
none
fix: do not generate code if idl contains proto2
What type of PR is this?
fix
What this PR does / why we need it (en: English/zh: Chinese):
en: fix: do not generate code if idl contains proto2 zh: 如果 idl 包含 proto2,则不生成代码。
Which issue(s) this PR fixes:
none
chore: add license header to files
What type of PR is this?
chore
What this PR does / why we need it (en: English/zh: Chinese):
en: add license header to files zh: 给文件添加许可证头
Which issue(s) this PR fixes:
none
custom go_package name, error
Describe the bug
When the go_package name is customized, the generated
pb.fast.go
is wrongSteps to reproduce the behavior: custom go_package:
although the official documentation does not recommend this, but it is legal.
fix: custom golang's pkg name
What type of PR is this?
fix: #14 When the file path does differ from the
go_package
name, the generatedpb.fast.go
is not available, although the official documentation does not recommend this, but it is legal. ps:What this PR does / why we need it (en: English/zh: Chinese):
en: Fix the generated file is not available when the package name and path of custom golang are different zh: 修复自定义golang的包名与路径不同时,生成的文件不可用
导入 google protobuf 报错
Describe the bug Cannot use '&v' (type *wrappers.StringValue) as the type ReaderType does not implement 'Reader' as some methods are missing:FastRead(buf []byte, _type int8, number int32) (n int, err error)
Unresolved reference 'File_common_realm_proto'
To Reproduce 执行了下面命令 kitex -module "gitlab.mvalley.com/fgq/kitex_demo" -service getinfo ./proto/info/kd.proto
Screenshots
本地文件 proto/common_realm/common.proto
本地文件 proto_vendor/google/protobuf/wrappers.proto

fastpb version: Code generated by FastPB v0.0.1.
Benchmarks
It's good to see this project is open source right now. Recently, I did some simple benchmarks for comparing
Marshal
/Unmarshal
in different implementations and I also added fastpb. See https://github.com/rleungx/benchmarks/tree/main/protobuf. Please let me know if I was using the right way to do these benchmarks.