どのように作業するのですか?
簡単です:
- インストール:github.com/t0pep0/efaceconvを取得します
- go generate // go:efaceconvをソースに生成する呼び出しを追加します
- 変換が必要なタイプを説明します(以下を参照)
- 生成してお楽しみください(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: 考えられない場合、起こりうる問題について 。