割り当てなしの型アサーション

みなさんこんにちは。 前の記事に加えて、 kirill_danshinとの興味深い対話がありました 。 最後に、私たちはそれをやった。 Meet- efaceconv 、go generateのツール。これを使用すると、インターフェイス{}から割り当てなしで型をキャストでき、 最大 4倍高速になります。



どのように作業するのですか?



簡単です:



  1. インストール:github.com/t0pep0/efaceconvを取得します
  2. go generate // go:efaceconvをソースに生成する呼び出しを追加します
  3. 変換が必要なタイプを説明します(以下を参照)
  4. 生成してお楽しみください(ZYをボーナスとして-生成されたコードを100%カバレッジでテスト)


型を記述する方法



繰り返しますが、すべてが簡単です。 タイプはコメントで説明されています。 記述形式は次のとおりです。



//ec: ( ):: 
      
      





例:



 //ec:net/http:http.ResponseWriter:ResWriter //ec::string:String
      
      





パッケージのディレクトリでgo generateを実行すると、2つの新しいファイルが表示されます。



efaceconv_generated.go-生成されたメソッド

efaceconv_generated_test.go-それらのテストとベンチマーク



demo.goの例:



 //go:generate efaceconv //ec::string:String //ec::[]uint64:SUint64 package demo
      
      





efaceconv_generated.go:



 //generated by efaceconv DO NOT EDIT! package demo import ( "github.com/t0pep0/efaceconv/ecutils" ) var ( _StringKind uintptr _SUint64Kind uintptr ) func init(){ var sString string _StringKind = ecutils.GetKind(sString) var sSUint64 []uint64 _SUint64Kind = ecutils.GetKind(sSUint64) } // Eface2String returns pointer to string and true if arg is a string // or nil and false otherwise func Eface2String(arg interface{}) (*string, bool) { if ecutils.GetKind(arg) == _StringKind { return (*string)(ecutils.GetDataPtr(arg)), true } return nil, false } // Eface2SUint64 returns pointer to []uint64 and true if arg is a string // or nil and false otherwise func Eface2SUint64(arg interface{}) (*[]uint64, bool) { if ecutils.GetKind(arg) == _SUint64Kind { return (*[]uint64)(ecutils.GetDataPtr(arg)), true } return nil, false }
      
      





efaceconv_generated_test.go:



 //generated by efaceconv DO NOT EDIT! package demo import ( "reflect" "testing" ) func TestEface2String(t *testing.T) { var String string res, ok := Eface2String(String) if !ok { t.Error("Wrong type!") } if !reflect.DeepEqual(*res, String) { t.Error("Not equal") } _, ok = Eface2String(ok) if ok { t.Error("Wrong type!") } } func benchmarkEface2String(b *testing.B) { var String string var v *string var ok bool for n := 0; n < bN; n++ { v, ok = Eface2String(String) } b.Log(v, ok) //For don't use compiler optimization } func _StringClassic(arg interface{}) (v string, ok bool) { v, ok = arg.(string) return v, ok } func benchmarkStringClassic(b *testing.B) { var String string var v string var ok bool for n := 0; n < bN; n++ { v, ok = _StringClassic(String) } b.Log(v, ok) //For don't use compiler optimization } func TestEface2SUint64(t *testing.T) { var SUint64 []uint64 res, ok := Eface2SUint64(SUint64) if !ok { t.Error("Wrong type!") } if !reflect.DeepEqual(*res, SUint64) { t.Error("Not equal") } _, ok = Eface2SUint64(ok) if ok { t.Error("Wrong type!") } } func benchmarkEface2SUint64(b *testing.B) { var SUint64 []uint64 var v *[]uint64 var ok bool for n := 0; n < bN; n++ { v, ok = Eface2SUint64(SUint64) } b.Log(v, ok) //For don't use compiler optimization } func _SUint64Classic(arg interface{}) (v []uint64, ok bool) { v, ok = arg.([]uint64) return v, ok } func benchmarkSUint64Classic(b *testing.B) { var SUint64 []uint64 var v []uint64 var ok bool for n := 0; n < bN; n++ { v, ok = _SUint64Classic(SUint64) } b.Log(v, ok) //For don't use compiler optimization }
      
      





ご覧のとおり、efaceconvは次の形式のメソッドを生成します



 Eface2<  >(arg interface{}) (*< >, bool)
      
      





それらのドキュメント、テスト、ベンチマークに加えて、パフォーマンスゲインを比較できるように、クラシックキャストタイプ(v、ok:= arg。(Type))のベンチマークも生成されます。



仕組み



以前の記事から)空のインターフェイスは、2つのフィールド(* TypeDescriptorとオブジェクトへのポインター)を持つ構造にすぎません。 TypeDescriptorは、1つの型からのすべての空のインターフェイスに対して、それぞれの型の単一のインスタンスで実行時に生成されます* TypeDescriptorは等しくなり、TypeDescriptor自体を解析する必要はありません。 ポインタの数値を単純に比較することができ、それらが一致する場合でも、必要な型があることを確認しながら、オブジェクトへのポインタを返すことができます。



なぜこれが標準的な方法より速いのですか?



TypeDescriptorsを比較した後の型キャストの標準的な方法は、値によってデータをコピーします。ソースオブジェクトへのポインタを与えるだけです



では、なぜGoの作者はこれをしなかったのでしょうか?



これは安全ではありません。 より正確にはそうではありませんが、不変のデータ型(文字列、スライス、配列)を使用している限り、安全です。 不正確なコード記述で不変ではないデータ型を使用する場合、副作用が発生する可能性があります。



すでにどこかで使用されていますか?



kirill_danshinは彼のプロダクションで最初のバージョンを導入しました。結果については信頼できませんが、 コミットによって判断すると、彼は満足しています。



数字はどこですか? パフォーマンスと割り当てについて



 BenchmarkEface2SByte-4 100000000 11.8 ns/op 0 B/op 0 allocs/op --- BENCH: BenchmarkEface2SByte-4 efaceconv_generated_test.go:33: &[] true efaceconv_generated_test.go:33: &[] true efaceconv_generated_test.go:33: &[] true efaceconv_generated_test.go:33: &[] true efaceconv_generated_test.go:33: &[] true BenchmarkSByteClassic-4 30000000 50.4 ns/op 32 B/op 1 allocs/op --- BENCH: BenchmarkSByteClassic-4 efaceconv_generated_test.go:48: [] true efaceconv_generated_test.go:48: [] true efaceconv_generated_test.go:48: [] true efaceconv_generated_test.go:48: [] true efaceconv_generated_test.go:48: [] true BenchmarkEface2String-4 100000000 11.1 ns/op 0 B/op 0 allocs/op --- BENCH: BenchmarkEface2String-4 efaceconv_generated_test.go:76: 0xc42003fee8 true efaceconv_generated_test.go:76: 0xc420043ea8 true efaceconv_generated_test.go:76: 0xc420043ea8 true efaceconv_generated_test.go:76: 0xc420043ea8 true efaceconv_generated_test.go:76: 0xc420043ea8 true BenchmarkStringClassic-4 30000000 45.3 ns/op 16 B/op 1 allocs/op --- BENCH: BenchmarkStringClassic-4 efaceconv_generated_test.go:91: true efaceconv_generated_test.go:91: true efaceconv_generated_test.go:91: true efaceconv_generated_test.go:91: true efaceconv_generated_test.go:91: true BenchmarkEface2SInt-4 100000000 11.6 ns/op 0 B/op 0 allocs/op --- BENCH: BenchmarkEface2SInt-4 efaceconv_generated_test.go:119: &[] true efaceconv_generated_test.go:119: &[] true efaceconv_generated_test.go:119: &[] true efaceconv_generated_test.go:119: &[] true efaceconv_generated_test.go:119: &[] true BenchmarkSIntClassic-4 30000000 50.5 ns/op 32 B/op 1 allocs/op --- BENCH: BenchmarkSIntClassic-4 efaceconv_generated_test.go:134: [] true efaceconv_generated_test.go:134: [] true efaceconv_generated_test.go:134: [] true efaceconv_generated_test.go:134: [] true efaceconv_generated_test.go:134: [] true PASS
      
      







悪役! 可変タイプ用に作成されたとおりにすべてを実行し、コードに奇妙な動作が発生しました!



SWSS



UPD: 考えられない場合、起こりうる問題について



All Articles