【问题标题】:Flatbuffer serialization performance is slow compared to protobuf与 protobuf 相比,Flatbuffer 序列化性能较慢
【发布时间】:2020-03-03 11:30:47
【问题描述】:

通过以下 IDL 文件,我的目的是测量 Flatbuffer 的序列化速度。我正在使用 golang 进行分析

namespace MyFlat;

struct Vertices {
    x : double;
    y  :double;

}
table Polygon  {

    polygons : [Vertices];
}

table Layer {

    polygons : [Polygon];
}
root_type Layer;

这是我写的计算代码

主包

import (
    "MyFlat"
    "fmt"
    "io/ioutil"
    "log"
    "strconv"
    "time"

    flatbuffers "github.com/google/flatbuffers/go"
)

func calculation(size int, vertices int) {
    b := flatbuffers.NewBuilder(0)
    var polyoffset []flatbuffers.UOffsetT

    rawSize := ((16 * vertices) * size) / 1024
    var vec1 flatbuffers.UOffsetT
    var StartedAtMarshal time.Time
    var EndedAtMarshal time.Time
    StartedAtMarshal = time.Now()
    for k := 0; k < size; k++ {

        MyFlat.PolygonStartPolygonsVector(b, vertices)

        for i := 0; i < vertices; i++ {
            MyFlat.CreateVertices(b, 2.0, 2.4)

        }

        vec1 = b.EndVector(vertices)
        MyFlat.PolygonStart(b)
        MyFlat.PolygonAddPolygons(b, vec1)
        polyoffset = append(polyoffset, MyFlat.PolygonEnd(b))

    }

    MyFlat.LayerStartPolygonsVector(b, size)
    for _, offset := range polyoffset {
        b.PrependUOffsetT(offset)
    }
    vec := b.EndVector(size)
    MyFlat.LayerStart(b)
    MyFlat.LayerAddPolygons(b, vec)
    finalOffset := MyFlat.LayerEnd(b)

    b.Finish(finalOffset)
    EndedAtMarshal = time.Now()

    SeElaprseTime := EndedAtMarshal.Sub(StartedAtMarshal).String()
    mybyte := b.FinishedBytes()
    file := "/tmp/myflat_" + strconv.Itoa(size) + ".txt"
    if err := ioutil.WriteFile(file, mybyte, 0644); err != nil {
        log.Fatalln("Failed to write address book:", err)
    }

    StartedAt := time.Now()

    layer := MyFlat.GetRootAsLayer(mybyte, 0)

    size = layer.PolygonsLength()
    obj := &MyFlat.Polygon{}
    layer.Polygons(obj, 1)

    for i := 0; i < obj.PolygonsLength(); i++ {
        objVertices := &MyFlat.Vertices{}
        obj.Polygons(objVertices, i)
        fmt.Println(objVertices.X(), objVertices.Y())
    }

    EndedAt := time.Now()
    DeElapseTime := EndedAt.Sub(StartedAt).String()
    fmt.Println(size, ",", vertices, ", ", SeElaprseTime, ",", DeElapseTime, ",", (len(mybyte) / 1024), ",", rawSize)
}

func main() {

    data := []int{500000, 1000000, 1500000, 3000000, 8000000}

    for _, size := range data {
        //calculation(size, 5)
        //calculation(size, 10)
        calculation(size, 20)

    }
}

问题是我发现与具有相似 idl 的 protobuff 相比,它的序列化速度相当慢。

对于 3M 多边形序列化,它需要将近 4.1167037 秒。在 protobuf 中占一半的位置。 flatbuf 的反序列化时间非常短(以微秒为单位)。在 protobuf 中它相当高。但是如果我同时添加两个 flatbuf,性能仍然会降低。

您是否看到任何优化的序列化方式。 Flatbuffer 有一个方法 createBinaryVector 用于字节向量,但没有直接的方法可以从现有的用户定义类型向量序列化多边形向量。

我也在添加 protobuf 代码

语法 = 'proto3';

package myproto; 
message Polygon {
                 repeated double v_x = 1 ;
                 repeated  double v_y = 2 ;
            }
message CADData {


       repeated Polygon polygon = 1;
        string layer_name = 2;
} 

使用 protobuf 编写代码

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "math/rand"
    "myproto"
    "strconv"
    "time"

    "github.com/golang/protobuf/proto"
)

func calculation(size int, vertices int) {
    var comp []*myproto.Polygon
    var vx []float64
    var vy []float64
    for i := 0; i < vertices; i++ {
        r := 0 + rand.Float64()*(10-0)
        vx = append(vx, r)
        vy = append(vy, r/2)

    }
    rawSize := ((16 * vertices) * size) / 1024
    StartedAtMarshal := time.Now()

    for i := 0; i < size; i++ {
        comp = append(comp, &myproto.Polygon{

            VX: vx,
            VY: vy,
        })
    }
    pfs := &myproto.CADData{
        LayerName: "Layer",
        Polygon:   comp,
    }
    data, err := proto.Marshal(pfs)
    if err != nil {
        log.Fatal("marshaling error: ", err)
    }
    EndedAtMarshal := time.Now()
    SeElaprseTime := EndedAtMarshal.Sub(StartedAtMarshal).String()
    file := "/tmp/myproto_" + strconv.Itoa(size) + ".txt"
    if err := ioutil.WriteFile(file, data, 0644); err != nil {
        log.Fatalln("Failed to write address book:", err)
    }

    StartedAt := time.Now()

    serialized := &myproto.CADData{}
    proto.Unmarshal(data, serialized)
    EndedAt := time.Now()
    DeElapseTime := EndedAt.Sub(StartedAt).String()
    fmt.Println(size, ",", vertices, ", ", SeElaprseTime, ",", DeElapseTime, ",", (len(data) / 1024), ",", rawSize)
}

func main() {
    data := []int{500000, 1000000, 1500000, 3000000, 8000000}
    for _, size := range data {
        //  calculation(size, 5)
        //calculation(size, 10)
        calculation(size, 20)

    }
}

【问题讨论】:

    标签: go protocol-buffers flatbuffers


    【解决方案1】:

    您给出的时间是用于序列化、反序列化还是两者兼而有之?

    您的反序列化代码可能完全由fmt.Println 主导。你为什么不做sum += objVertices.X() + objVertices.Y() 并在计时完成后打印sum 呢?你能把objVertices := &amp;MyFlat.Vertices{}拉到循环之外吗?

    您没有发布您的 protobuf 代码。您是否在计时中包括创建正在序列化的对象树的时间(在 Protobuf 中使用但在 FlatBuffers 中不需要)?同样,您是否进行了至少 1000 倍左右的定时(反)序列化,因此您可以在比较中包括 GC 成本(Protobuf 分配大量对象,FlatBuffers 分配很少/无)?

    如果执行上述操作后仍然较慢,请在 FlatBuffers github 问题上发帖,Go 端口的作者可能会提供进一步的帮助。确保发布两个系统的完整代码和完整的时间。

    一般注意:FlatBuffers 的设计使其与 C/C++ 中的 Protobuf 产生最大的性能差距。也就是说,它在 Go 中也应该快得多。然而,Go 有一些不幸的事情阻止它最大限度地发挥性能潜力。

    【讨论】:

    • 我已经通过添加 protobuf 代码更新了问题。对于您的回答,我提到的时间只是序列化时间。 De-Ser 相当快。
    【解决方案2】:
        b := flatbuffers.NewBuilder(0)
    

    我不确定 Go 中对于 flatbuffers 的“自动增长”行为是什么,但我很确定要求缓冲区自动增长不是 首选 模式。你能在用flatbuffers.NewBuilder(moreBytesThanTheMessageNeeds)初始化缓冲区后尝试做同样的时间比较吗?

    【讨论】:

      猜你喜欢
      • 2021-04-02
      • 1970-01-01
      • 2019-04-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-24
      • 1970-01-01
      相关资源
      最近更新 更多