卷起來(lái)!Go泛型是怎么實(shí)現(xiàn)的?
Go 1.17中你就可以使用泛型了,可以參考我3月份的文章:Go 泛型嘗鮮, 編譯的時(shí)候需要加-gcflags=-G=3參數(shù),而當(dāng)前master分支,默認(rèn)已經(jīng)支持泛型,不需要加-G=3參數(shù)了。
你可以通過(guò)下面的步驟嘗試go最新分支:
go get golang.org/dl/gotip
gotip download
編譯代碼的時(shí)候使用gotip替換go命令即可。
隨著Go 1.17的發(fā)布,最近也涌現(xiàn)了很多的介紹Go泛型的文章,基本上都是簡(jiǎn)單介紹的文章。
最近Go泛型的變化是增加了兩個(gè)操作符: ~和|:
an approximation element ~Trestricts to all types whose underlying type is T: 代表底層類型是Ta union element T1 | T2 | ...restricts to any of the listed elements: 代表或,類型列表之一。這些不是我想介紹的內(nèi)容,今天我肝一篇介紹Go泛型實(shí)現(xiàn)原理的文章,介紹Go泛型實(shí)現(xiàn)的方案。
對(duì)于一個(gè)函數(shù)func Echo[T any](t T){},Go編譯器到底編譯成了什么代碼?
簡(jiǎn)單的說(shuō),當(dāng)前Go泛型實(shí)現(xiàn)的方案和下圖中的方案一樣:

在國(guó)內(nèi)的老破小小區(qū)的樓道中常見(jiàn)的一種高科技印刷技術(shù),通過(guò)一個(gè)鏤花模板,為每一種類型生成特化的類型,這個(gè)術(shù)語(yǔ)叫做stenciling。
但是如果再說(shuō)多一點(diǎn),那么就應(yīng)該從 Taylor和Griesemer說(shuō)起。
Go泛型提案中關(guān)于泛型實(shí)現(xiàn)的介紹
Go的泛型有別于其它語(yǔ)言的方案,在Go語(yǔ)言中泛型叫做Type Parameter(類型參數(shù)).
Taylor和Griesemer的提案Type Parameters Proposal更多的是泛型呈現(xiàn)形式和影響的思考,對(duì)具體的實(shí)現(xiàn)涉及甚少。
無(wú)論什么編程語(yǔ)言,根據(jù)Russ Cox的觀察,實(shí)現(xiàn)泛型至少要面對(duì)下面三條困境之一,那還是在2009年:
拖累程序員:比如C語(yǔ)言,增加了程序員的負(fù)擔(dān),需要曲折的實(shí)現(xiàn),但是不對(duì)增加語(yǔ)言的復(fù)雜性 拖累編譯器: 比如C++編程語(yǔ)言,增加了編譯器的負(fù)擔(dān),可能會(huì)產(chǎn)生很多冗余的代碼,重復(fù)的代碼還需要編譯器斟酌刪除,編譯的文件可能非常大。Rust的泛型也屬于這一類。 拖累執(zhí)行時(shí)間:比如Java,將一些裝箱成Object,進(jìn)行類型擦除。雖然代碼沒(méi)啥冗余了,空間節(jié)省了,但是需要裝箱拆箱操作,代碼效率低。很顯然, Go語(yǔ)言至簡(jiǎn)的設(shè)計(jì)哲學(xué)讓它的泛型實(shí)現(xiàn)不會(huì)選擇增加程序員的負(fù)擔(dān)的道路,所以它會(huì)在第二和第三種方案中做選擇。雖然提案中沒(méi)有最終說(shuō)明它選擇了哪種方案,但是從實(shí)際編譯的代碼可以看出,它選擇的是第二種方案。
三個(gè)方案
Keith H. Randall, MIT的博士,現(xiàn)在在Google/Go team做泛型方面的開(kāi)發(fā),提出了Go泛型實(shí)現(xiàn)的三個(gè)方案:
1.字典
在編譯時(shí)生成一組實(shí)例化的字典,在實(shí)例話一個(gè)泛型函數(shù)的時(shí)候會(huì)使用字典進(jìn)行蠟印(stencile)。
當(dāng)為泛型函數(shù)生成代碼的時(shí)候,會(huì)生成唯一的一塊代碼,并且會(huì)在參數(shù)列表中增加一個(gè)字典做參數(shù),就像方法會(huì)把receiver當(dāng)成一個(gè)參數(shù)傳入。字典包含為類型參數(shù)實(shí)例化的類型信息。
字典在編譯時(shí)生成,存放在只讀的data section中。
當(dāng)然字段可以當(dāng)成第一個(gè)參數(shù),或者最后一個(gè)參數(shù),或者放入一個(gè)獨(dú)占的寄存器。
當(dāng)然這種方案還有依稀問(wèn)題,比如字典遞歸的問(wèn)題,更重要的是,它對(duì)性能可能有比較大的影響,比如一個(gè)實(shí)例化類型int, x=y可能通過(guò)寄存器復(fù)制就可以了,但是泛型必須通過(guò)memmove。
2.蠟印(Stenciling)
或者翻譯成用模板印等。
就像下面的動(dòng)圖一樣,同一個(gè)泛型函數(shù),為每一個(gè)實(shí)例化的類型參數(shù)生成一套獨(dú)立的代碼,感覺(jué)和rust的泛型特化一樣。

這種方案和上面的字典方案正好相反。
比如下面一個(gè)泛型方法:
func f[T1, T2 any](x int, y T1) T2 {
...
}
如果有兩個(gè)不同的類型實(shí)例化的調(diào)用:
var a float64 = f[int, float64](7, 8.0)
var b struct{f int} = f[complex128, struct{f int}](3, 1+1i)
那么這個(gè)方案會(huì)生成兩套代碼:
func f1(x int, y int) float64 {
... identical bodies ...
}
func f2(x int, y complex128) struct{f int} {
... identical bodies ...
}
因?yàn)榫幾gf時(shí)是不知道它的實(shí)例化類型的,只有在調(diào)用它時(shí)才知道它的實(shí)例化的類型,所以需要在調(diào)用時(shí)編譯f。對(duì)于相同實(shí)例化類型的多個(gè)調(diào)用,同一個(gè)package下編譯器可以識(shí)別出來(lái)是一樣的,只生成一個(gè)代碼就可以了,但是不同的package就不簡(jiǎn)單了,這些函數(shù)表標(biāo)記為DUPOK,所以鏈接器會(huì)丟掉重復(fù)的函數(shù)實(shí)現(xiàn)。
這種策略需要更多的編譯時(shí)間,因?yàn)樾枰幾g泛型函數(shù)多次。因?yàn)閷?duì)于同一個(gè)泛型函數(shù),每種類型需要單獨(dú)的一份編譯的代碼,如果類型非常多,編譯的文件可能非常大,而且性能也比較差。
3.混合方案(GC Shape Stenciling)
混合前面的兩種方案。
對(duì)于實(shí)例類型的shape相同的情況,只生成一份代碼,對(duì)于shape類型相同的類型,使用字典區(qū)分類型的不同行為。
這種方案介于前兩者之間。
啥叫shape?
類型的shape是它對(duì)內(nèi)存分配器/垃圾回收器呈現(xiàn)的方式,包括它的大小、所需的對(duì)齊方式、以及類型哪些部分包含指針。
每一個(gè)唯一的shape會(huì)產(chǎn)生一份代碼,每份代碼攜帶一個(gè)字典,包含了實(shí)例化類型的信息。
這種方案的問(wèn)題是到底能帶來(lái)多大的收益,它會(huì)變得有多慢,以及其它的一些問(wèn)題。
從當(dāng)前的反編譯的代碼看,當(dāng)前Go采用的是第二種方案,盡管名稱中已經(jīng)帶了shape、dict的標(biāo)志,或許,Go的泛型方案還在進(jìn)化之中,進(jìn)化到第三種方案或者其它方案也不是沒(méi)有可能。
接下來(lái)我們看一個(gè)例子,看看Go泛型的方案是怎么實(shí)現(xiàn)的。
例子
下面是一個(gè)簡(jiǎn)單的例子,有一個(gè)泛型函數(shù)func echo[T any](t T) string {return fmt.Sprintf("%v", t)},使用不同的幾種實(shí)例化類型去調(diào)用它,并且使用shape一樣的int32和uint32做為實(shí)例化類型。
package generic
import (
"fmt"
"time"
)
func echo[T any](t T) string {
return fmt.Sprintf("%v", t)
}
func Test() {
echo(0)
echo(int32(0))
echo(uint32(0))
echo(uint64(0))
echo("hello")
echo(struct{}{})
echo(time.Now())
}
反編譯后代碼非常長(zhǎng),精簡(jiǎn)如下。編譯的時(shí)候禁止優(yōu)化和內(nèi)聯(lián),否則實(shí)例化的代碼內(nèi)聯(lián)后看不到效果了。
可以看到函數(shù)echo編譯成了不同的函數(shù):"".echo[.shape.int]、"".echo[.shape.int32]、"".echo[.shape.uint32]、"".echo[.shape.uint64]、"".echo[.shape.string]、"".echo[.shape.struct{}]、"".echo[.shape.struct{ time.wall uint64; time.ext int64; time.loc *time.Location }]不同的函數(shù),即使shape一樣的類型(int32、uint32)。調(diào)用這些函數(shù)時(shí),是通過(guò)""..dict.echo[uint64]這種方式調(diào)用的。
所以我謹(jǐn)慎懷疑,Go的泛型方式在逐步的向第三種方案進(jìn)化。
# command-line-arguments
"".Test STEXT size=185 args=0x0 locals=0x48 funcid=0x0
0x0000 00000 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:12) TEXT "".Test(SB), ABIInternal, $72-0
"".Test STEXT size=185 args=0x0 locals=0x48 funcid=0x0
0x0000 00000 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:12) TEXT "".Test(SB), ABIInternal, $72-0
0x0000 00000 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:12) CMPQ SP, 16(R14)
0x0004 00004 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:12) PCDATA $0, $-2
0x0004 00004 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:12) JLS 175
0x000a 00010 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:12) PCDATA $0, $-1
0x000a 00010 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:12) SUBQ $72, SP
0x000e 00014 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:12) MOVQ BP, 64(SP)
0x0013 00019 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:12) LEAQ 64(SP), BP
0x0018 00024 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:12) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0018 00024 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:12) FUNCDATA $1, gclocals·54241e171da8af6ae173d69da0236748(SB)
0x0018 00024 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:13) LEAQ ""..dict.echo[int](SB), AX
0x001f 00031 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:13) XORL BX, BX
0x0021 00033 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:13) PCDATA $1, $0
0x0021 00033 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:13) CALL "".echo[.shape.int](SB)
0x0026 00038 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:14) LEAQ ""..dict.echo[int32](SB), AX
0x002d 00045 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:14) XORL BX, BX
0x002f 00047 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:14) CALL "".echo[.shape.int32](SB)
0x0034 00052 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:15) LEAQ ""..dict.echo[uint32](SB), AX
0x003b 00059 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:15) XORL BX, BX
0x003d 00061 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:15) NOP
0x0040 00064 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:15) CALL "".echo[.shape.uint32](SB)
0x0045 00069 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:16) LEAQ ""..dict.echo[uint64](SB), AX
0x004c 00076 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:16) XORL BX, BX
0x004e 00078 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:16) CALL "".echo[.shape.uint64](SB)
0x0053 00083 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:17) LEAQ ""..dict.echo[string](SB), AX
0x005a 00090 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:17) LEAQ go.string."hello"(SB), BX
0x0061 00097 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:17) MOVL $5, CX
0x0066 00102 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:17) CALL "".echo[.shape.string](SB)
0x006b 00107 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:18) LEAQ ""..dict.echo[struct{}](SB), AX
0x0072 00114 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:18) CALL "".echo[.shape.struct{}](SB)
0x0077 00119 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:19) CALL time.Now(SB)
0x007c 00124 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:19) MOVQ AX, ""..autotmp_0+40(SP)
0x0081 00129 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:19) MOVQ BX, ""..autotmp_0+48(SP)
0x0086 00134 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:19) MOVQ CX, ""..autotmp_0+56(SP)
0x008b 00139 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:19) MOVQ CX, DI
0x008e 00142 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:19) MOVQ BX, CX
0x0091 00145 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:19) MOVQ AX, BX
0x0094 00148 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:19) LEAQ ""..dict.echo[time.Time](SB), AX
0x009b 00155 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:19) NOP
0x00a0 00160 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:19) CALL "".echo[.shape.struct{ time.wall uint64; time.ext int64; time.loc *time.Location }](SB)
0x00a5 00165 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:20) MOVQ 64(SP), BP
0x00aa 00170 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:20) ADDQ $72, SP
0x00ae 00174 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:20) RET
0x00af 00175 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:20) NOP
0x00af 00175 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:12) PCDATA $1, $-1
0x00af 00175 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:12) PCDATA $0, $-2
0x00af 00175 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:12) CALL runtime.morestack_noctxt(SB)
0x00b4 00180 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:12) PCDATA $0, $-1
0x00b4 00180 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:12) JMP 0
.................
"".echo[.shape.int] STEXT dupok size=268 args=0x10 locals=0x88 funcid=0x0
0x0000 00000 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:8) TEXT "".echo[.shape.int](SB), DUPOK|ABIInternal, $136-16
.................
0x00c2 00194 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:9) MOVQ DI, SI
0x00c5 00197 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:9) PCDATA $1, $0
0x00c5 00197 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:9) CALL fmt.Sprintf(SB)
.................
"".echo[.shape.int32] STEXT dupok size=266 args=0x10 locals=0x88 funcid=0x0
0x0000 00000 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:8) TEXT "".echo[.shape.int32](SB), DUPOK|ABIInternal, $136-16
.................
0x00bd 00189 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:9) MOVL $1, DI
0x00c2 00194 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:9) MOVQ DI, SI
0x00c5 00197 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:9) PCDATA $1, $0
0x00c5 00197 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:9) CALL fmt.Sprintf(SB)
.................
"".echo[.shape.uint32] STEXT dupok size=266 args=0x10 locals=0x88 funcid=0x0
0x0000 00000 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:8) TEXT "".echo[.shape.uint32](SB), DUPOK|ABIInternal, $136-16
.................
0x00c5 00197 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:9) PCDATA $1, $0
0x00c5 00197 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:9) CALL fmt.Sprintf(SB)
.................
"".echo[.shape.uint64] STEXT dupok size=268 args=0x10 locals=0x88 funcid=0x0
0x0000 00000 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:8) TEXT "".echo[.shape.uint64](SB), DUPOK|ABIInternal, $136-16
.................
0x00c2 00194 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:9) MOVQ DI, SI
0x00c5 00197 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:9) PCDATA $1, $0
0x00c5 00197 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:9) CALL fmt.Sprintf(SB)
.................
"".echo[.shape.string] STEXT dupok size=295 args=0x18 locals=0x88 funcid=0x0
0x0000 00000 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:8) TEXT "".echo[.shape.string](SB), DUPOK|ABIInternal, $136-24
.................
0x00d6 00214 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:9) PCDATA $1, $2
0x00d6 00214 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:9) CALL fmt.Sprintf(SB)
.................
"".echo[.shape.struct{}] STEXT dupok size=208 args=0x8 locals=0x88 funcid=0x0
0x0000 00000 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:8) TEXT "".echo[.shape.struct{}](SB), DUPOK|ABIInternal, $136-8
.................
0x0093 00147 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:9) PCDATA $1, $0
0x0093 00147 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:9) CALL fmt.Sprintf(SB)
.................
0x00cb 00203 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:8) JMP 0
.................
"".echo[.shape.struct{ time.wall uint64; time.ext int64; time.loc *time.Location }] STEXT dupok size=364 args=0x20 locals=0xa0 funcid=0x0
0x0000 00000 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:8) TEXT "".echo[.shape.struct{ time.wall uint64; time.ext int64; time.loc *time.Location }](SB), DUPOK|ABIInternal, $160-32
.................
0x00c5 00197 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:9) CMPL runtime.writeBarrier(SB), $0
0x00cc 00204 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:9) JEQ 208
0x00ce 00206 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:9) JMP 214
0x00d0 00208 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:9) MOVQ AX, 8(CX)
0x00d4 00212 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:9) JMP 221
0x00d6 00214 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:9) CALL runtime.gcWriteBarrier(SB)
.................
0x0167 00359 (/Users/chaoyuepan/go/src/github.com/smallnest/study/type_parameter/generic/generic.go:8) JMP 0
...............
"".echo[.shape.string].stkobj SRODATA static size=32
.......
"".echo[.shape.string].arginfo1 SRODATA static dupok size=9
....... ..........
"".echo[.shape.struct{}].stkobj SRODATA static size=32
.......
"".echo[.shape.struct{}].arginfo1 SRODATA static dupok size=5
.......
"".echo[.shape.struct{ time.wall uint64; time.ext int64; time.loc *time.Location }].stkobj SRODATA static size=56
......
"".echo[.shape.struct{ time.wall uint64; time.ext int64; time.loc *time.Location }].arginfo1 SRODATA static dupok size=11
0x0000 00 08 fe 08 08 10 08 18 08 fd ff ...........
泛型的性能 寫(xiě)一個(gè)簡(jiǎn)單的benchmark程序,沒(méi)看到明顯的性能變化。
package bench_test
import (
"fmt"
"testing"
)
func BenchmarkAdd_Generic(b *testing.B) {
for i := 0; i < b.N; i++ {
add(i, i)
}
}
func BenchmarkAdd_NonGeneric(b *testing.B) {
for i := 0; i < b.N; i++ {
addInt(i, i)
}
}
type Addable interface {
int
}
func add[T Addable](a, b T) T {
return a + b
}
func addInt(a, b int) int {
return a + b
}
func main() {
fmt.Println(add(1, 2))
fmt.Println(addInt(1, 2))
}
參考文檔
https://github.com/golang/proposal/blob/master/design/generics-implementation-dictionaries.md https://github.com/golang/proposal/blob/master/design/generics-implementation-gcshape.md https://github.com/golang/proposal/blob/master/design/generics-implementation-stenciling.md https://github.com/golang/proposal/blob/master/design/43651-type-parameters.md https://docs.google.com/document/d/1vrAy9gMpMoS3uaVphB32uVXX4pi-HnNjkMEgyAHX4N4/view#
想要了解關(guān)于 Go 的更多資訊,還可以通過(guò)掃描的方式,進(jìn)群一起探討哦~
