清晰胜过聪明: 改进 flatbuffers-go

栏目: IT技术 · 发布时间: 4年前

内容简介:使用 flatbuffers 已经有相当长的一段时间了.在几个商用项目中, flatbuffers 也因快速的反序列化而带来性能上的不少提升.flatbuffers 尤其适合传输小块数据, 一次序列化, 多个地方进行反序列化.

使用 flatbuffers 已经有相当长的一段时间了.

在几个商用项目中, flatbuffers 也因快速的反序列化而带来性能上的不少提升.

flatbuffers 尤其适合传输小块数据, 一次序列化, 多个地方进行反序列化.

go 的 flatbuffers 有一些小遗憾:

  1. go flatbuffers 功能支持, 滞后于 c++ 版, Go 代码库也很久没有更新了. 相比 c ++ , go 版本缺少一些功能. 如 vector of unions , 在 unions 中包含 struct / strings . ( 注: go 版本的 flatbuffers 在 unions 中只能包含 table )
  2. 缺少 verifier 验证器 ( 这是我需要的)
  3. go flatbuffers 序列化的速度, 慢于 gogo protobuf. flatbuffers 序列化消耗的时间, 大约是 gogo protobuf 的两倍.
  4. go flatbuffers 不支持 go module. 尤其是自动生成的 go 代码存在相互引用时的 import 并不友好.
  5. go flatbuffers 的序列化代码不太优雅, 不太符合 go 的习惯风格

在这样情况下, 我起了改进 go flatbuffers 的念头.

flatbuffers 的编译器, 是 c++ 写的. 我已经很多年没有用过 c++ 开发了. 对我来说, 这可能是一次有趣的探险历程.

1. 我对 go flatbuffers 的折腾

刚开始, 我写一个 flatbuffers verifier , 本地验证通过后, 我向 google flatbuffers 发了一个 PR. 结果被建议我重读一下 flatbuffers 的设计规范文档. 嗯哼, 这就开始有趣了.

在接下来的两周左右, 我边读 flatbuffers 的关键规范文档 ( 见附录参考列表) , 边写了一个全新的序列化生成器 ( flatbuffers builder ) .

我拆分了flatbuffers 的 memory block , 采用 goroutine 并发处理各个独立的 memory block 转化为二进制序列数据, 最后进行合并/排序/优化. 当这个手写序列化器看起来可以工作时, 我发现, 需要把这些手写代码嵌入 flatbuffers 编译器中, 支持自动代码生成, 我遇到了一个小难题. 我几乎忘记如何写 C++ 了.

为此, 我重读了 Effective C++ 这样的几本册子, 随书写几行代码跑跑. 一周之后, 重新熟悉 C++ , 意外收获是对 go 的内存管理有了进一步的认识.

如何让 go flatbuffers 序列化更快, 我还在尝试中.

而熟悉了 C++ 后, 我先让 go flatbuffers API 变得清晰简单, 易用一些.

2. 移植 C++ 有用功能, 支持 vector of unions.

union 是 flatbuffers 中很有趣也很有用的一个功能, 当然, struct 也很有用. go flatbuffers 中, union 只支持 table , 并且不支持 union array ( 被称为 vector of unions ) , 先加上这个

IDL

union Character {
  MuLan: Attacker,  // table, 相当于 protobuf 中的 message
  Rapunzel,         // struct , 与 c++ 的 struct 相当
  Belle: BookReader,
  BookFan: BookReader,
  Other: string,   // string 
  Unused: string
}

table Movie {
  main_character: Character;      // 单一 union 字段
  characters: [Character];            // vector of unions 
}
复制代码

3. 支持 go module via Attribute ( 在 IDL 定义中 ).

每一个 fbs IDL 定义文件都支持各自的 module , 格式像这样: "go_module:github.com/tsingson/flatbuffers-sample/go-example/";

weapons.fbs

namespace weapons;

attribute "go_module:github.com/tsingson/flatbuffers-sample/samplesNew/";

table Gun {
  damage:short;
  bool:bool;
  name:string;
  names:[string];
}
复制代码

monster.fbs

include "../weapons.fbs";

namespace Mygame.Example;

attribute "go_module:github.com/tsingson/flatbuffers-sample/go-example/";

enum Color:byte { Red = 0, Green, Blue = 2 }
union Equipment {   MuLan: Weapon, Weapon, Gun:weapons.Gun, SpaceShip,   Other: string } // Optionally add more tables.

......
复制代码

生成的 go 代码

package Example

import (
	"strconv"
	flatbuffers "github.com/google/flatbuffers/go"

	weapons "github.com/tsingson/flatbuffers-sample/samplesNew/weapons"    /// 嗯哼!  
)

type Equipment byte

..........

复制代码

4. 增加一些清晰易用的 API /生成代码.

weaponsOffset := flatbuffers.UOffsetT(0)
	if t.Weapons != nil {
		weaponsLength := len(t.Weapons)
		weaponsOffsets := make([]flatbuffers.UOffsetT, weaponsLength)
		for j := weaponsLength - 1; j >= 0; j-- {
			weaponsOffsets[j] = t.Weapons[j].Pack(builder)
		}
		MonsterStartWeaponsVector(builder, weaponsLength)            //////// start
		for j := weaponsLength - 1; j >= 0; j-- {
			builder.PrependUOffsetT(weaponsOffsets[j])
		}
		weaponsOffset = MonsterEndWeaponsVector(builder, weaponsLength)   /////// end 
	}
复制代码

shortcut for []strings vector

// native object 

	Names []string


// builder

namesOffset := builder.StringsVector( t.Names...)


复制代码

getter for vector of unions

func (rcv *Movie) Characters(j int, obj *flatbuffers.Table) bool {
	o := flatbuffers.UOffsetT(rcv._tab.Offset(10))
	if o != 0 {
		a := rcv._tab.Vector(o)
		obj.Pos = a + flatbuffers.UOffsetT(j*4)
		obj.Bytes = rcv._tab.Bytes
		return true
	}
	return false
}

复制代码

so get struct or table

// GetStructVectorAsBookReader shortcut to access struct in vector of unions
func GetStructVectorAsBookReader(table *flatbuffers.Table) *BookReader {
	n := flatbuffers.GetUOffsetT(table.Bytes[table.Pos:])
	x := &BookReader{}
	x.Init(table.Bytes, n+ table.Pos)
	return x
}

// GetStructAsBookReader shortcut to access struct in single union field
func GetStructAsBookReader(table *flatbuffers.Table) *BookReader {
	x := &BookReader{}
	x.Init(table.Bytes, table.Pos)
	return x
}

复制代码

for object-api , comments in generated code to make it clear

// UnPack use for single union field
 func (rcv Character) UnPack(table flatbuffers.Table) *CharacterT {
	switch rcv {
	case CharacterMuLan:
		x := GetTableAsAttacker(&table)
		return &CharacterT{ Type: CharacterMuLan, Value: x.UnPack() }
 .............

// UnPackVector use for vector of unions 
func (rcv Character) UnPackVector(table flatbuffers.Table) *CharacterT {
	switch rcv {
	case CharacterMuLan:
		x := GetTableVectorAsAttacker(&table)
		return &CharacterT{ Type: CharacterMuLan, Value: x.UnPack() }
	case CharacterRapunzel:
.........
复制代码

或许, 稍后更多, 让 Go flatbuffers ...... 更好用.

5. happy hacking....... 折腾继续中

本文持续有更新...........

.

.

本文首发于 GolangChina , 在此 gocn.vip/topics/1022…

祝安康愉快!

_

_

关于我

网名 tsingson (三明智)

原 ustarcom IPTV/OTT 事业部播控产品线技术架构湿/解决方案工程湿角色(8年), 自由职业者,

喜欢音乐(口琴,是第三/四/五届广东国际口琴嘉年华的主策划人之一), 摄影与越野,

喜欢 golang 语言 (商用项目中主要用 postgres + golang )

tsingson ( 三明智 ) 于深圳南山. 小罗号口琴音乐中心 2020/04/09

清晰胜过聪明: 改进 flatbuffers-go

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

区块链与人工智能:数字经济新时代

区块链与人工智能:数字经济新时代

高航、俞学劢、王毛路 / 电子工业出版社 / 2018-7-23 / 80

《区块链与人工智能》是畅销书《区块链与新经济:数字货币2.0时代》全新修订升级版。本书是市场上为数不多的系统阐述区块链、人工智能技术与产业的入门级系统教程。从比特币到各类数字货币(代币),从基础原理到应用探讨,全景式呈现区块链与人工智能的发展脉络,既有历史的厚重感也有科技的未来感。本书的另一个亮点是系统整理了区块链创业地图,是一本关于区块链创业、应用、媒体的学习指南,以太坊创始人Vitalik专门......一起来看看 《区块链与人工智能:数字经济新时代》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具