2017-10-19 16 views
3

インタフェース{}が与えられた関数を実装しているかどうかを効率的にテストしようとしています。私の解決策は、この関数だけでインタフェースを作成し、インタフェース{}がこの単一関数インタフェースを実装しているかどうかを調べることです。ここでの2つのオプションは、リフレクションまたはタイプアサーションのどちらかを使用しているようです。どちらも同じ動作をしているようですが、速度には大きな違いがあります。なぜType.Implements()は型アサーションよりもはるかに遅いですか?

Value.Implements()のコードを見ると、値に定義されている関数をリニアスキャンし、それらをインターフェイスと比較します。 しかし、タイプアサーションは(インターフェイスの関数の数に関係なく)一定の時間比較を行うように見えます。

Implements()が型アサーションだけではない理由はありますか?

ベンチマーク:

package benchmarks 

import (
    "reflect" 
    "testing" 
) 

type ITest interface { 
    Foo() 
} 

type Base struct{} 

func (Base) A() {} 
func (Base) B() {} 
func (Base) C() {} 
func (Base) D() {} 
func (Base) E() {} 
func (Base) F() {} 
func (Base) G() {} 
func (Base) H() {} 
func (Base) I() {} 
func (Base) J() {} 

var Interface = reflect.TypeOf((*ITest)(nil)).Elem() 

func BenchmarkReflection(b *testing.B) { 
    var iface interface{} 
    iface = Base{} 
    for i := 0; i < b.N; i++ { 
     if reflect.TypeOf(iface).Implements(Interface) { 
      b.FailNow() 
     } 
    } 
} 

func BenchmarkAssertion(b *testing.B) { 
    var iface interface{} 
    iface = Base{} 
    for i := 0; i < b.N; i++ { 
     if _, ok := iface.(ITest); ok { 
      b.FailNow() 
     } 
    } 
} 

結果:ゴーで

go test -run=XXX -bench=. so_test.go 
goos: linux 
goarch: amd64 
BenchmarkReflection-8   10000000     208 ns/op 
BenchmarkAssertion-8   200000000    9.24 ns/op 
PASS 
ok  command-line-arguments 5.115s 

答えて

7

型アサーションはruntime.assertE2I2と呼ばれる機能に依存しています。コードを調べると、getitabに依存していることがわかります(同じファイル内)additabに依存しています。

今、与えられたタイプがadditabの内部インターフェイスを実装している場合のチェックの実際のロジックがreflectImplementsと全く同じである - でも、このコメントで指摘された線形検索、:

// both inter and typ have method sorted by name, 
// and interface names are unique, 
// so can iterate over both in lock step; 
// the loop is O(ni+nt) not O(ni*nt). 

しかし、違いはadditabが実際にキャッシュを利用していることです。タイプアサーションの結果はハッシュマップに格納されるため、同じタイプの後続のタイプアサーションは一定時間内に実行されるため、パフォーマンスに大きな違いが見られます。

+0

Implements()がキャッシュしない理由はありますか?これは、何かがインターフェイスを実装するかどうかが変わる可能性があることを期待していることを示唆しています(ただし、これが真の場合は、型アサーションキャッシングは間違いでしょうか?) – bradleyjkemp

+0

なぜなら、 – Volker

+1

@bradleyjkemp、「コンピューティング・ザ・イテーブル」のセクション[こちら](https://research.swtch.com/interfaces)を参照することをお勧めします。私の推測では、あなたのコメントには、タイプアサーション/スイッチがあり、具体的な型の値をインタフェース型の変数に代入しても、その型の新しいitableを実行時に計算する必要があるかもしれないが、コンパイラは有限実行可能なときにGoプログラムがそれ自体を変更することはできないからです... – kostix

関連する問題