Goおよびプロトコルバッファー、アクセラレーション

記事「 Go and Protocol Buffers」の少しの練習の続き(または、慣れていない人のためのクイックスタート) 。 Goの特定の形式のエンコード/デコードプロセスは、リフレクションと密接に関連しています。 そして、読者の皆さん、ご存知のように、反省は長い時間です。 この記事に存在する戦闘方法について。 私は、洗練された人々がその中に新しいものを見つける可能性は低いと思います。




実際、言及された記事はgithub.com/golang/protobuf/{proto,protoc-gen-go}パッケージについて話していました。 それらの何が問題になっていますか? つまり、その反射が使用されます。 特定の構造セットで機能するプロジェクトがあるとします。 そして、これらの構造はプロトコルバッファに常にエンコードされ、その逆も同様です。 常に異なる、予測不可能なタイプであれば、問題はありません。 ただし、セットが事前にわかっている場合は、リフレクションを使用する必要はありません。 ご存知のように、コーディングを担当するインターフェースを使用するのが慣例です。 これはencoding/json例です:
 type Marshaler interface { MarshalJSON() ([]byte, error) } type Unmarshaler interface { UnmarshalJSON([]byte) error } 

参照: マーシャラーアン マーシャラー
エンコーダーがこれらのインターフェイスの1つを具体化する型に遭遇した場合、この場合、すべての作業はメソッドに依存します。
簡単なJSONの例
 type X struct { Name string, Value int, } func (x *X) MarshalJSON() ([]byte, error) { return []byte(fmt.Sprintf(`{"name": %q, "value": %d}`, x.Name, x.Value)) } 

常に(Un)Marshalerはとてもバラ色ではありません。 たとえば、 yaml( Eng。について読むべきことがあります一般的にはこのトピックについてです。


キー



いつものように、解決策は簡単です。 別のパッケージを使用:
 go get github.com/gogo/protobuf/{proto,protoc-gen-gogo,gogoproto,protoc-gen-gofast} 

これらのパッケージは、単に利便性と速度を追加します。
パッケージについて(リンク):

ご覧のとおり、1.10x以上の加速。 拡張機能のセットを単純に使用することができます-加速なし。 単にスピードアップする機会があります。 私はこのコマンドで解決しました:
 protoc \ --proto_path=$GOPATH/src:$GOPATH/src/github.com/gogo/protobuf/protobuf:. \ --gogofast_out=. *.proto 
拡張機能(ある場合)とアクセラレーションの両方を取得します。
拡張機能を使用するように呼び出すのではなく、参照用です。
 syntax="proto3"; package some; //protoc \ // --proto_path=$GOPATH/src:$GOPATH/src/github.com/gogo/protobuf/protobuf:. \ // --gogofast_out=. *.proto import "github.com/gogo/protobuf/gogoproto/gogo.proto"; //  ,   Equal,   option (gogoproto.equal_all) = true; option (gogoproto.goproto_stringer_all) = false; // Stringer   (    ) option (gogoproto.stringer_all) = true; //   -    option (gogoproto.populate_all) = true; //    option (gogoproto.testgen_all) = true; //   option (gogoproto.benchgen_all) = true; //  option (gogoproto.marshaler_all) = true; //   option (gogoproto.sizer_all) = true; //  option (gogoproto.unmarshaler_all) = true; // enums,   -    option (gogoproto.goproto_enum_prefix_all) = false; enum Bool { Yes = 0; No = 1; DontCare = 2; } message Some { option (gogoproto.goproto_unrecognized ) = false; option (gogoproto.goproto_getters) = false; Bool Waht = 1; int64 Count = 2; bytes Hash = 3; } 

成功します
 /*        (Size, String  ..)       */ type Bool int32 const ( Yes Bool = 0 No Bool = 1 DontCare Bool = 2 ) // ... type Some struct { Waht Bool `protobuf:"varint,1,opt,name=Waht,proto3,enum=some.Bool" json:"Waht,omitempty"` Count int64 `protobuf:"varint,2,opt,name=Count,proto3" json:"Count,omitempty"` Hash []byte `protobuf:"bytes,3,opt,name=Hash,proto3" json:"Hash,omitempty"` } //   proto.Message (github.com/golang/protobuf/proto) func (m *Some) Reset() { *m = Some{} } func (*Some) ProtoMessage() {} //   func (m *Some) Marshal() (data []byte, err error) { // ... } //   func (m *Some) Unmarshal(data []byte) error { // ... } 


ご覧のとおり、一部の拡張機能にはベータ版のステータスがあり、これはproto3に関する注意事項です。 heしないでください。 このパッケージは、多くの人によって正常に使用されています(ホームページを参照)。 それでも、これはテストの作成を免除するものではありません。 拡張機能やものに興味がない場合は、プロジェクトのREADMEに記載されているように、このコマンドで十分です。
 protoc --gofast_out=. myproto.proto 


ストーンズ



軟膏で飛ぶ

以前のスポイラーを調べなかった場合、その断片の1つを強調したいと思います。
 func (m *Some) Reset() { *m = Some{} } //   

実際のところ、 gogo使用すると「高速」構造を生成できます。 「古い」 github.com/golang/protobuf/protoそれらを使用することもできます。 この場合、 MarshalおよびUnmarshal方法が使用されます-問題はありません。 しかし、構造の同じインスタンスを何度も使用するとどうなりますか。 構造が大きい場合(いいえ、巨大な場合)、プールを使用して "ワークアウトされた"構造を保存し、それらを元に戻して再利用しても大丈夫です。

github.com/golang/protobuf/protoアプローチ。 参照先
 func Unmarshal(buf []byte, pb Message) error { pb.Reset() //    return UnmarshalMerge(buf, pb) } 

Reset呼び出します。 したがって、 *m = Some{} -古い構造は破棄され、新しい構造が作成されます。 この構造は小さい-気にしない-しかし、大きなハッシュが使用される場合に備えて、 Hash []byteHash []byte割り当てられたメモリ )を保存したいと思います。

github.com/gogo/protobuf/protoアプローチはコピーgithub.com/gogo/protobuf/protoペーストに似ています。 一見ではありません。

じゃあ Unmarshalメソッドを直接使用するか、 Unmarshalを試すことができますMyResetメソッドを追加し、スライスの長さをカットします-容量を残します。 いや! 生成されたUnmarshal行は次のUnmarshalです。
 m.Hash = append([]byte{}, data[iNdEx:postIndex]...) 

新しいスライスが作成されます-古いスライスがGCファイアボックスに飛び込みます。 実際、小さな構造物(構造物のフィールド-そしてすべて一緒に )がある場合、最も簡単な方法は入浴しないことです。 大規模な場合-回避策を探します(生成されたコードを書き換えてください)。 現在の実装では、プールを使用しても意味がありません。

ボーナス



ライブラリはストリーミングに便利です。 io.Writerからの読み取り、 io.Writerでのメッセージの書き込み-このような自転車はすでに存在します。

jsonについての会話が来てから: github.com/pquerna/ffjson jsonについても同様です。 単なるジェネレーターではなく、 json + Go用のスイス製ナイフです。

私が速度とプールについて話し始めたので: github.com/valyala/fasthttpnet/http 「クイック」置換。 メモリの再利用による加速。 追加機能も同じです。

Source: https://habr.com/ru/post/J276905/


All Articles