zt3ff3n 发表于 2023-2-16 13:37

go语言序列化json/gob/msgp/protobuf性能对比

基础知识

json和gob是go语言自带的序列化方式,都在encoding包下面。
messagepack是一种十分高效的编码方式,在文件头加入“//go:generate msgp”,使用go generate xx.go命令生成文件。
protobuf有多个实现版本,官方版本使用了反射性能相对较差,对CPU和内存要求非常高的情况下可以使用FlatBuffers,一般推荐使用gogo-protobuf就足够用了。
要使用msgp(全称message pack)和protobuf需要先安装:
go get http://github.com/tinylib/msgp
go get http://github.com/gogo/protobuf/protoc-gen-gogofaster
安装后在$GOPATH/bin下生成protoc-gen-gogofaster和msgp两个可执行文件。
使用msgp需要先写一个go文件,定义好要序列化的结构体。
person.go
//go:generate msgp

package serialize

type Person struct {
        DocId       uint32
        Position    string
        Company   string
        City      string
        SchoolLevel int32
        Vip         bool
        Chat      bool
        Active      int32
        WorkAge   int32
}执行命令 go generate ./serialize/person.go 会生成person_gen.go和person_gen_test.go。Person结构体的序列化和反序列化函数就在person_gen.go文件里。
要使用protobuf需要先编写.proto文件,为保证对比的公平性,我们定义一个Doc,它跟Person的字段完全相同。
doc.proto
syntax = "proto3";
package serialize;

messageDoc {
    uint32 doc_id = 1;
    string position = 2;
    string company = 3;
    string city = 4;
    int32 school_level = 5;
    bool vip = 6;
    bool chat = 7;
    int32 active = 8;
    int32 work_age=9;
}执行命令 protoc -I=. doc.proto --gogofaster_out=. 会生成doc.pb.go,Doc的序列化和反序列化函数就在这个文件里。
单元测试

package serialize

import (
        "bytes"
        "encoding/gob"
        "encoding/json"
        "fmt"
        "testing"

        "github.com/gogo/protobuf/proto"
        "github.com/tinylib/msgp/msgp"
)

var doc = Doc{DocId: 123, Position: "搜索工程师", Company: "百度", City: "北京", SchoolLevel: 2, Vip: false, Chat: true, Active: 1, WorkAge: 3}
var person = Person{DocId: 123, Position: "搜索工程师", Company: "百度", City: "北京", SchoolLevel: 2, Vip: false, Chat: true, Active: 1, WorkAge: 3}

func TestJson(t *testing.T) {
        bs, _ := json.Marshal(doc)
        fmt.Printf("json encode byte length %d\n", len(bs))
        var inst Doc
        _ = json.Unmarshal(bs, &inst)
        fmt.Printf("json decode position %s\n", inst.Position)
}

func TestGob(t *testing.T) {
        var buffer bytes.Buffer
        encoder := gob.NewEncoder(&buffer)
        _ = encoder.Encode(doc)
        fmt.Printf("gob encode byte length %d\n", len(buffer.Bytes()))
        var inst Doc
        decoder := gob.NewDecoder(&buffer)
        _ = decoder.Decode(&inst)
        fmt.Printf("gob decode position %s\n", inst.Position)
}

func TestGogoProtobuf(t *testing.T) {
        bs, _ := proto.Marshal(&doc)
        fmt.Printf("pb encode byte length %d\n", len(bs))
        var inst Doc
        _ = proto.Unmarshal(bs, &inst)
        fmt.Printf("pb decode position %s\n", inst.Position)
}

func TestMsgp(t *testing.T) {
        var buf bytes.Buffer
        _ = msgp.Encode(&buf, &person)
        fmt.Printf("msgp encode byte length %d\n", len(buf.Bytes()))
        var inst Person
        _ = msgp.Decode(&buf, &inst)
        fmt.Printf("msgp decode position %s\n", inst.Position)
}基准测试

func BenchmarkJsonEncode(b *testing.B) {
        for i := 0; i < b.N; i++ {
                json.Marshal(doc)
        }
}

func BenchmarkJsonDecode(b *testing.B) {
        bs, _ := json.Marshal(doc)
        var inst Doc
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
                json.Unmarshal(bs, &inst)
        }
}

func BenchmarkGobEncode(b *testing.B) {
        for i := 0; i < b.N; i++ {
                var buffer bytes.Buffer
                encoder := gob.NewEncoder(&buffer)
                encoder.Encode(doc)
        }
}

func BenchmarkGobDecode(b *testing.B) {
        var buffer bytes.Buffer
        encoder := gob.NewEncoder(&buffer)
        encoder.Encode(doc)
        var inst Doc
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
                buffer.Reset()
                decoder := gob.NewDecoder(&buffer)
                decoder.Decode(&inst)
        }
}

func BenchmarkPbEncode(b *testing.B) {
        for i := 0; i < b.N; i++ {
                proto.Marshal(&doc)
        }
}

func BenchmarkPbDecode(b *testing.B) {
        bs, _ := proto.Marshal(&doc)
        var inst Doc
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
                proto.Unmarshal(bs, &inst)
        }
}

func BenchmarkMsgpEncode(b *testing.B) {
        for i := 0; i < b.N; i++ {
                var buf bytes.Buffer
                msgp.Encode(&buf, &person)
        }
}

func BenchmarkMsgpDecode(b *testing.B) {
        var buf bytes.Buffer
        msgp.Encode(&buf, &person)
        var inst Person
        b.ResetTimer()
        for i := 0; i < b.N; i++ {
                buf.Reset()
                msgp.Decode(&buf, &inst)
        }
}在跑基础测试时我们通过-benchmem 把内存的使用情况也输出。
速度 ns/op内存开销 B/op序列化json818224gob56601808gogo-protobuf11248msgp354160反序列化json3401240gob322288gogo-protobuf14632msgp16432结论:无论在速度还是内存方面,gogo-protobuf都全面领先,msgpack性能也不错。json和gob很差劲。

RhinoFreak 发表于 2023-2-16 13:44

protobuf比json性能强这么多吗。
页: [1]
查看完整版本: go语言序列化json/gob/msgp/protobuf性能对比