Comments (2)
Gen0(int) int
から Gen19(int) int
までの 20 個の関数を呼び出すベンチマークを以下の 5 種類の場合において計測した。
BenchmarkStruct
:あるstruct
にインライン化を無効に設定したメソッドGen0
~Gen19
までが定義されていて、その struct を利用する(抽象化はできないが、以後のベンチマークの基準となる)BenchmarkSwitcher
:if
文を使って条件t
によって振る舞いを変えるインライン化を無効に設定したメソッドGen0
~Gen19
までが定義されているSwitcher
構造体を利用するGen0
~Gen19
までが定義されたインターフェイスInterface
が宣言されており、そのインターフェイスに適応したstruct0
からstruct999
までの 1000 種類の構造体が定義されている。その中からランダムに構造体の種類を選択して作成しInterface
経由で各メソッドを呼び出すGen0
~Gen19
までの関数ポインタをフィールドにもつ構造体FuncPointers
が定義されており、そのフィールドにGen0
~Gen19
までの関数を設定して呼び出す。FuncsPointeres
と同様にgen0
~gen19
までの関数ポインタをフィールドにもつ構造体FuncPointersInner
が定義されており、その関数ポインターを実行するGen0
~Gen19
メソッドを定義してこの公開メソッド経由で呼び出す。この公開メソッドはインライン化を無効にするフラグは付与しない。
結果は以下の通り。ベンチマークでは、1 op あたり 20 回関数呼び出しをしている。
$ go test -bench=.
goos: darwin
goarch: amd64
pkg: github.com/kawasin73/sample-go/interface
BenchmarkStruct-4 30000000 49.8 ns/op
BenchmarkSwitcher-4 20000000 59.2 ns/op
BenchmarkInterface-4 10000000 117 ns/op
BenchmarkFuncPointer-4 20000000 56.1 ns/op
BenchmarkFuncPointersInner-4 10000000 119 ns/op
PASS
ok github.com/kawasin73/sample-go/interface 6.909s
この結果を見ると、メソッドの中で条件分岐をして処理を切り分けるものと、関数ポインターからの呼び出しがほぼ同じくらい速く、通常の関数呼び出しとほぼ同じ速度で実行されていることがわかる。
また、インターフェイス経由の呼び出しとラップした関数経由で関数ポインターを実行するものはほぼ同じくらいの速度で、関数呼び出し 2 回分程度の時間がかかっていることがわかる。
関数をラップしたメソッドの場合は、内部で非公開関数を呼び出しているためインライン化がされず2回の関数呼び出しの時間がかかっているものと考えられる。
関数呼び出しのオーバーヘッドは 2 ~ 3 ns
程度であることがわかる。
また、インターフェイスに合致する構造体の数を変えてインターフェイス呼び出しを計測すると以下のようになり、結果がほぼ変わらないことがわかる。
インターフェイスに合致する構造体の数 | 結果 |
---|---|
1 | 113 ns/op |
1000 | 117 ns/op |
ここで、処理の抽象化を実現する方法として 条件分岐を使う手法 と 関数ポインタを実行する 手法が候補にあがる。
速度で見るとわずかに関数ポインタを実行する手法が速いが、関数ポインタは公開されたフィールドであるためパッケージの外部から差し替えられる可能性がある。非公開フィールドにして公開メソッド経由で呼び出すと 2 回分の関数呼び出しのオーバーヘッドが発生する。
条件分岐を利用する方法では条件分岐命令が遅くなることが懸念されるが、zcbit では設定されたバイトオーダーは変更されないため、分岐予測が有効に働きデメリットは軽減されることが予想される。
よって、条件分岐を利用して処理を切り分ける 手法を採用する。
Source Code
main_test.go
package main
import (
"testing"
"math/rand"
)
func BenchmarkStruct(b *testing.B) {
inter := struct0{n: 1}
for i := 0; i < b.N; i++ {
j := i
j = inter.Gen0(j)
j = inter.Gen1(j)
j = inter.Gen2(j)
j = inter.Gen3(j)
j = inter.Gen4(j)
j = inter.Gen5(j)
j = inter.Gen6(j)
j = inter.Gen7(j)
j = inter.Gen8(j)
j = inter.Gen9(j)
j = inter.Gen10(j)
j = inter.Gen11(j)
j = inter.Gen12(j)
j = inter.Gen13(j)
j = inter.Gen14(j)
j = inter.Gen15(j)
j = inter.Gen16(j)
j = inter.Gen17(j)
j = inter.Gen18(j)
j = inter.Gen19(j)
}
}
func BenchmarkSwitcher(b *testing.B) {
inter := switcher{t: 0, n: 1}
for i := 0; i < b.N; i++ {
j := i
j = inter.Gen0(j)
j = inter.Gen1(j)
j = inter.Gen2(j)
j = inter.Gen3(j)
j = inter.Gen4(j)
j = inter.Gen5(j)
j = inter.Gen6(j)
j = inter.Gen7(j)
j = inter.Gen8(j)
j = inter.Gen9(j)
j = inter.Gen10(j)
j = inter.Gen11(j)
j = inter.Gen12(j)
j = inter.Gen13(j)
j = inter.Gen14(j)
j = inter.Gen15(j)
j = inter.Gen16(j)
j = inter.Gen17(j)
j = inter.Gen18(j)
j = inter.Gen19(j)
}
}
func BenchmarkInterface(b *testing.B) {
var inter Interface = newStruct(rand.Intn(10))
for i := 0; i < b.N; i++ {
j := i
j = inter.Gen0(j)
j = inter.Gen1(j)
j = inter.Gen2(j)
j = inter.Gen3(j)
j = inter.Gen4(j)
j = inter.Gen5(j)
j = inter.Gen6(j)
j = inter.Gen7(j)
j = inter.Gen8(j)
j = inter.Gen9(j)
j = inter.Gen10(j)
j = inter.Gen11(j)
j = inter.Gen12(j)
j = inter.Gen13(j)
j = inter.Gen14(j)
j = inter.Gen15(j)
j = inter.Gen16(j)
j = inter.Gen17(j)
j = inter.Gen18(j)
j = inter.Gen19(j)
}
}
func BenchmarkFuncPointer(b *testing.B) {
p := newFuncs()
for i := 0; i < b.N; i++ {
j := i
j = p.Gen0(j, j)
j = p.Gen1(j, j)
j = p.Gen2(j, j)
j = p.Gen3(j, j)
j = p.Gen4(j, j)
j = p.Gen5(j, j)
j = p.Gen6(j, j)
j = p.Gen7(j, j)
j = p.Gen8(j, j)
j = p.Gen9(j, j)
j = p.Gen10(j, j)
j = p.Gen11(j, j)
j = p.Gen12(j, j)
j = p.Gen13(j, j)
j = p.Gen14(j, j)
j = p.Gen15(j, j)
j = p.Gen16(j, j)
j = p.Gen17(j, j)
j = p.Gen18(j, j)
j = p.Gen19(j, j)
}
}
func BenchmarkFuncPointersInner(b *testing.B) {
p := newFuncsInner()
for i := 0; i < b.N; i++ {
j := i
j = p.Gen0(j, j)
j = p.Gen1(j, j)
j = p.Gen2(j, j)
j = p.Gen3(j, j)
j = p.Gen4(j, j)
j = p.Gen5(j, j)
j = p.Gen6(j, j)
j = p.Gen7(j, j)
j = p.Gen8(j, j)
j = p.Gen9(j, j)
j = p.Gen10(j, j)
j = p.Gen11(j, j)
j = p.Gen12(j, j)
j = p.Gen13(j, j)
j = p.Gen14(j, j)
j = p.Gen15(j, j)
j = p.Gen16(j, j)
j = p.Gen17(j, j)
j = p.Gen18(j, j)
j = p.Gen19(j, j)
}
}
gen.go
package main
import (
"fmt"
"strconv"
"os"
"bytes"
)
type Interface interface {
Gen0(int) int
Gen1(int) int
Gen2(int) int
Gen3(int) int
Gen4(int) int
Gen5(int) int
Gen6(int) int
Gen7(int) int
Gen8(int) int
Gen9(int) int
Gen10(int) int
Gen11(int) int
Gen12(int) int
Gen13(int) int
Gen14(int) int
Gen15(int) int
Gen16(int) int
Gen17(int) int
Gen18(int) int
Gen19(int) int
}
type FuncPointers struct {
Gen0 func(int, int) int
Gen1 func(int, int) int
Gen2 func(int, int) int
Gen3 func(int, int) int
Gen4 func(int, int) int
Gen5 func(int, int) int
Gen6 func(int, int) int
Gen7 func(int, int) int
Gen8 func(int, int) int
Gen9 func(int, int) int
Gen10 func(int, int) int
Gen11 func(int, int) int
Gen12 func(int, int) int
Gen13 func(int, int) int
Gen14 func(int, int) int
Gen15 func(int, int) int
Gen16 func(int, int) int
Gen17 func(int, int) int
Gen18 func(int, int) int
Gen19 func(int, int) int
}
func Gen0(i int, j int) int {
return i + j
}
func Gen1(i int, j int) int {
return i + j
}
func Gen2(i int, j int) int {
return i + j
}
func Gen3(i int, j int) int {
return i + j
}
func Gen4(i int, j int) int {
return i + j
}
func Gen5(i int, j int) int {
return i + j
}
func Gen6(i int, j int) int {
return i + j
}
func Gen7(i int, j int) int {
return i + j
}
func Gen8(i int, j int) int {
return i + j
}
func Gen9(i int, j int) int {
return i + j
}
func Gen10(i int, j int) int {
return i + j
}
func Gen11(i int, j int) int {
return i + j
}
func Gen12(i int, j int) int {
return i + j
}
func Gen13(i int, j int) int {
return i + j
}
func Gen14(i int, j int) int {
return i + j
}
func Gen15(i int, j int) int {
return i + j
}
func Gen16(i int, j int) int {
return i + j
}
func Gen17(i int, j int) int {
return i + j
}
func Gen18(i int, j int) int {
return i + j
}
func Gen19(i int, j int) int {
return i + j
}
func newFuncs() *FuncPointers {
return &FuncPointers{
Gen0: Gen0,
Gen1: Gen1,
Gen2: Gen2,
Gen3: Gen3,
Gen4: Gen4,
Gen5: Gen5,
Gen6: Gen6,
Gen7: Gen7,
Gen8: Gen8,
Gen9: Gen9,
Gen10: Gen10,
Gen11: Gen11,
Gen12: Gen12,
Gen13: Gen13,
Gen14: Gen14,
Gen15: Gen15,
Gen16: Gen16,
Gen17: Gen17,
Gen18: Gen18,
Gen19: Gen19,
}
}
type FuncPointersInner struct {
gen0 func(int, int) int
gen1 func(int, int) int
gen2 func(int, int) int
gen3 func(int, int) int
gen4 func(int, int) int
gen5 func(int, int) int
gen6 func(int, int) int
gen7 func(int, int) int
gen8 func(int, int) int
gen9 func(int, int) int
gen10 func(int, int) int
gen11 func(int, int) int
gen12 func(int, int) int
gen13 func(int, int) int
gen14 func(int, int) int
gen15 func(int, int) int
gen16 func(int, int) int
gen17 func(int, int) int
gen18 func(int, int) int
gen19 func(int, int) int
}
func newFuncsInner() *FuncPointersInner {
return &FuncPointersInner{
gen0: Gen0,
gen1: Gen1,
gen2: Gen2,
gen3: Gen3,
gen4: Gen4,
gen5: Gen5,
gen6: Gen6,
gen7: Gen7,
gen8: Gen8,
gen9: Gen9,
gen10: Gen10,
gen11: Gen11,
gen12: Gen12,
gen13: Gen13,
gen14: Gen14,
gen15: Gen15,
gen16: Gen16,
gen17: Gen17,
gen18: Gen18,
gen19: Gen19,
}
}
func (f *FuncPointersInner) Gen0(i int, j int) int {
return f.gen0(i, j)
}
func (f *FuncPointersInner) Gen1(i int, j int) int {
return f.gen1(i, j)
}
func (f *FuncPointersInner) Gen2(i int, j int) int {
return f.gen2(i, j)
}
func (f *FuncPointersInner) Gen3(i int, j int) int {
return f.gen3(i, j)
}
func (f *FuncPointersInner) Gen4(i int, j int) int {
return f.gen4(i, j)
}
func (f *FuncPointersInner) Gen5(i int, j int) int {
return f.gen5(i, j)
}
func (f *FuncPointersInner) Gen6(i int, j int) int {
return f.gen6(i, j)
}
func (f *FuncPointersInner) Gen7(i int, j int) int {
return f.gen7(i, j)
}
func (f *FuncPointersInner) Gen8(i int, j int) int {
return f.gen8(i, j)
}
func (f *FuncPointersInner) Gen9(i int, j int) int {
return f.gen9(i, j)
}
func (f *FuncPointersInner) Gen10(i int, j int) int {
return f.gen10(i, j)
}
func (f *FuncPointersInner) Gen11(i int, j int) int {
return f.gen11(i, j)
}
func (f *FuncPointersInner) Gen12(i int, j int) int {
return f.gen12(i, j)
}
func (f *FuncPointersInner) Gen13(i int, j int) int {
return f.gen13(i, j)
}
func (f *FuncPointersInner) Gen14(i int, j int) int {
return f.gen14(i, j)
}
func (f *FuncPointersInner) Gen15(i int, j int) int {
return f.gen15(i, j)
}
func (f *FuncPointersInner) Gen16(i int, j int) int {
return f.gen16(i, j)
}
func (f *FuncPointersInner) Gen17(i int, j int) int {
return f.gen17(i, j)
}
func (f *FuncPointersInner) Gen18(i int, j int) int {
return f.gen18(i, j)
}
func (f *FuncPointersInner) Gen19(i int, j int) int {
return f.gen19(i, j)
}
type switcher struct {
t int
n int
}
//go:noinline
func (s switcher) Gen0(i int) int {
if s.t == 0 {
return s.n + i + 2
}
return s.n + i
}
//go:noinline
func (s switcher) Gen1(i int) int {
if s.t == 0 {
return s.n + i + 2
}
return s.n + i
}
//go:noinline
func (s switcher) Gen2(i int) int {
if s.t == 0 {
return s.n + i + 2
}
return s.n + i
}
//go:noinline
func (s switcher) Gen3(i int) int {
if s.t == 0 {
return s.n + i + 2
}
return s.n + i
}
//go:noinline
func (s switcher) Gen4(i int) int {
if s.t == 0 {
return s.n + i + 2
}
return s.n + i
}
//go:noinline
func (s switcher) Gen5(i int) int {
if s.t == 0 {
return s.n + i + 2
}
return s.n + i
}
//go:noinline
func (s switcher) Gen6(i int) int {
if s.t == 0 {
return s.n + i + 2
}
return s.n + i
}
//go:noinline
func (s switcher) Gen7(i int) int {
if s.t == 0 {
return s.n + i + 2
}
return s.n + i
}
//go:noinline
func (s switcher) Gen8(i int) int {
if s.t == 0 {
return s.n + i + 2
}
return s.n + i
}
//go:noinline
func (s switcher) Gen9(i int) int {
if s.t == 0 {
return s.n + i + 2
}
return s.n + i
}
//go:noinline
func (s switcher) Gen10(i int) int {
if s.t == 0 {
return s.n + i + 2
}
return s.n + i
}
//go:noinline
func (s switcher) Gen11(i int) int {
if s.t == 0 {
return s.n + i + 2
}
return s.n + i
}
//go:noinline
func (s switcher) Gen12(i int) int {
if s.t == 0 {
return s.n + i + 2
}
return s.n + i
}
//go:noinline
func (s switcher) Gen13(i int) int {
if s.t == 0 {
return s.n + i + 2
}
return s.n + i
}
//go:noinline
func (s switcher) Gen14(i int) int {
if s.t == 0 {
return s.n + i + 2
}
return s.n + i
}
//go:noinline
func (s switcher) Gen15(i int) int {
if s.t == 0 {
return s.n + i + 2
}
return s.n + i
}
//go:noinline
func (s switcher) Gen16(i int) int {
if s.t == 0 {
return s.n + i + 2
}
return s.n + i
}
//go:noinline
func (s switcher) Gen17(i int) int {
if s.t == 0 {
return s.n + i + 2
}
return s.n + i
}
//go:noinline
func (s switcher) Gen18(i int) int {
if s.t == 0 {
return s.n + i + 2
}
return s.n + i
}
//go:noinline
func (s switcher) Gen19(i int) int {
if s.t == 0 {
return s.n + i + 2
}
return s.n + i
}
func header() string {
return `
package main
`
}
func buildNew(n int) string {
var buf bytes.Buffer
buf.WriteString(`func newStruct(i int) Interface {
switch i {`)
format := `
case %v:
return struct%v{n: i}`
for i := 0; i < n; i++ {
buf.WriteString(fmt.Sprintf(format, i, i))
}
buf.WriteString(`
default:
return struct0{n: i}
}
}
`)
return buf.String()
}
func build(id int) string {
format := `
type %v struct {
n int
}
//go:noinline
func (s %v) Gen0(i int) int {
return s.n + i
}
//go:noinline
func (s %v) Gen1(i int) int {
return s.n + i
}
//go:noinline
func (s %v) Gen2(i int) int {
return s.n + i
}
//go:noinline
func (s %v) Gen3(i int) int {
return s.n + i
}
//go:noinline
func (s %v) Gen4(i int) int {
return s.n + i
}
//go:noinline
func (s %v) Gen5(i int) int {
return s.n + i
}
//go:noinline
func (s %v) Gen6(i int) int {
return s.n + i
}
//go:noinline
func (s %v) Gen7(i int) int {
return s.n + i
}
//go:noinline
func (s %v) Gen8(i int) int {
return s.n + i
}
//go:noinline
func (s %v) Gen9(i int) int {
return s.n + i
}
//go:noinline
func (s %v) Gen10(i int) int {
return s.n + i
}
//go:noinline
func (s %v) Gen11(i int) int {
return s.n + i
}
//go:noinline
func (s %v) Gen12(i int) int {
return s.n + i
}
//go:noinline
func (s %v) Gen13(i int) int {
return s.n + i
}
//go:noinline
func (s %v) Gen14(i int) int {
return s.n + i
}
//go:noinline
func (s %v) Gen15(i int) int {
return s.n + i
}
//go:noinline
func (s %v) Gen16(i int) int {
return s.n + i
}
//go:noinline
func (s %v) Gen17(i int) int {
return s.n + i
}
//go:noinline
func (s %v) Gen18(i int) int {
return s.n + i
}
//go:noinline
func (s %v) Gen19(i int) int {
return s.n + i
}
`
var args []interface{}
for i := 0; i < 21; i++ {
args = append(args, "struct"+strconv.Itoa(id))
}
return fmt.Sprintf(format, args...)
}
func main() {
file := "structs.go"
os.Remove(file)
f, err := os.Create(file)
if err != nil {
panic(err)
}
defer f.Close()
_, err = f.WriteString(header())
if err != nil {
panic(err)
}
count := 1000
for i := 0; i < count; i ++ {
_, err = f.WriteString(build(i))
if err != nil {
panic(err)
}
}
_, err = f.WriteString(buildNew(count))
if err != nil {
panic(err)
}
}
from bitset.
インターフェイスの内部実装
インターフェイスの内部実装については、「GoのInterfaceとは何者なのか #golang #go」 で詳しく解説されている。
型ごとに作成される itab
構造体の中にメソッドリストがあり、インターフェイスのメソッド呼び出しではそのリストから検索している。
また、itab
構造体はある型がインターフェイスに代入されるときに作成され(コンパイル時ではない)キャッシュされることでメモリ量と計算の省略化のトレードオフをバランスしている。
また、一次情報として、Russ Cox によって書かれた「Go Data Structures: Interfaces」 がある。
from bitset.
Related Issues (13)
- v0.1.0 HOT 1
- v0.3.0
- gometalinter:unsafe を使うと警告がでる
- TravisCI の QEMU 利用のリファクタリング HOT 2
- バイトスライスの GC 抑制 HOT 1
- バイトオーダーの検出 HOT 1
- バイトオーダーのスワップ HOT 1
- Little Endian と Big Endian のエミュレート HOT 8
- バイト列の自動拡張 HOT 4
- []byte から []uint64 への変換 HOT 1
- v0.2.0 HOT 1
- バイト列の変換とあまりの部分の処理 HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from bitset.