溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何通過(guò)禁止比較讓Go二進(jìn)制文件變小

發(fā)布時(shí)間:2021-10-28 16:15:24 來(lái)源:億速云 閱讀:123 作者:柒染 欄目:編程語(yǔ)言

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)如何通過(guò)禁止比較讓Go二進(jìn)制文件變小,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

大家常規(guī)的認(rèn)知是,Go  程序中聲明的類型越多,生成的二進(jìn)制文件就越大。這個(gè)符合直覺,畢竟如果你寫的代碼不去操作定義的類型,那么定義一堆類型就沒(méi)有意義了。然而,鏈接器的部分工作就是檢測(cè)沒(méi)有被程序引用的函數(shù)(比如說(shuō)它們是一個(gè)庫(kù)的一部分,其中只有一個(gè)子集的功能被使用),然后把它們從最后的編譯產(chǎn)出中刪除。常言道,“類型越多,二進(jìn)制文件越大”,對(duì)于多數(shù)  Go 程序還是正確的。

我會(huì)深入講解在 Go 程序的上下文中“相等”的意義,以及為什么像這樣的修改會(huì)對(duì) Go 程序的大小有重大的影響。

定義兩個(gè)值相等

Go 的語(yǔ)法定義了“賦值”和“相等”的概念。賦值是把一個(gè)值賦給一個(gè)標(biāo)識(shí)符的行為。并不是所有聲明的標(biāo)識(shí)符都可以被賦值,如常量和函數(shù)就不可以。相等是通過(guò)檢查標(biāo)識(shí)符的內(nèi)容是否相等來(lái)比較兩個(gè)標(biāo)識(shí)符的行為。

作為強(qiáng)類型語(yǔ)言,“相同”的概念從根源上被植入標(biāo)識(shí)符的類型中。兩個(gè)標(biāo)識(shí)符只有是相同類型的前提下,才有可能相同。除此之外,值的類型定義了如何比較該類型的兩個(gè)值。

例如,整型是用算數(shù)方法進(jìn)行比較的。對(duì)于指針類型,是否相等是指它們指向的地址是否相同。映射和通道等引用類型,跟指針類似,如果它們指向相同的地址,那么就認(rèn)為它們是相同的。

上面都是按位比較相等的例子,即值占用的內(nèi)存的位模式是相同的,那么這些值就相等。這就是所謂的 memcmp,即內(nèi)存比較,相等是通過(guò)比較兩個(gè)內(nèi)存區(qū)域的內(nèi)容來(lái)定義的。

記住這個(gè)思路,我過(guò)會(huì)兒再來(lái)談。

結(jié)構(gòu)體相等

除了整型、浮點(diǎn)型和指針等標(biāo)量類型,還有復(fù)合類型:結(jié)構(gòu)體。所有的結(jié)構(gòu)體以程序中的順序被排列在內(nèi)存中。因此下面這個(gè)聲明:

type S struct {    a, b, c, d int64}

會(huì)占用 32 字節(jié)的內(nèi)存空間;a 占用 8 個(gè)字節(jié),b 占用 8 個(gè)字節(jié),以此類推。Go 的規(guī)則說(shuō)如果結(jié)構(gòu)體所有的字段都是可以比較的,那么結(jié)構(gòu)體的值就是可以比較的。因此如果兩個(gè)結(jié)構(gòu)體所有的字段都相等,那么它們就相等。

a := S{1, 2, 3, 4}b := S{1, 2, 3, 4}fmt.Println(a == b) // 輸出 true

編譯器在底層使用 memcmp 來(lái)比較 a 的 32 個(gè)字節(jié)和 b 的 32 個(gè)字節(jié)。

填充和對(duì)齊

然而,在下面的場(chǎng)景下過(guò)分簡(jiǎn)單化的按位比較的策略會(huì)返回錯(cuò)誤的結(jié)果:

type S struct {    a byte    b uint64    c int16    d uint32} func main()    a := S{1, 2, 3, 4}    b := S{1, 2, 3, 4}    fmt.Println(a == b) // 輸出 true}

編譯代碼后,這個(gè)比較表達(dá)式的結(jié)果還是 true,但是編譯器在底層并不能僅依賴比較 ab 的位模式,因?yàn)榻Y(jié)構(gòu)體有填充。

Go 要求結(jié)構(gòu)體的所有字段都對(duì)齊。2 字節(jié)的值必須從偶數(shù)地址開始,4 字節(jié)的值必須從 4 的倍數(shù)地址開始,以此類推 1。編譯器根據(jù)字段的類型和底層平臺(tái)加入了填充來(lái)確保字段都對(duì)齊。在填充之后,編譯器實(shí)際上看到的是 2

type S struct {    a byte    _ [7]byte // 填充    b uint64    c int16    _ [2]int16 // 填充    d uint32}

填充的存在保證了字段正確對(duì)齊,而填充確實(shí)占用了內(nèi)存空間,但是填充字節(jié)的內(nèi)容是未知的。你可能會(huì)認(rèn)為在 Go 中 填充字節(jié)都是 0,但實(shí)際上并不是 — 填充字節(jié)的內(nèi)容是未定義的。由于它們并不是被定義為某個(gè)確定的值,因此按位比較會(huì)因?yàn)榉植荚?s 的 24 字節(jié)中的 9 個(gè)填充字節(jié)不一樣而返回錯(cuò)誤結(jié)果。

Go 通過(guò)生成所謂的相等函數(shù)來(lái)解決這個(gè)問(wèn)題。在這個(gè)例子中,s 的相等函數(shù)只比較函數(shù)中的字段略過(guò)填充部分,這樣就能正確比較類型 s 的兩個(gè)值。

類型算法

呵,這是個(gè)很大的設(shè)置,說(shuō)明了為什么,對(duì)于 Go  程序中定義的每種類型,編譯器都會(huì)生成幾個(gè)支持函數(shù),編譯器內(nèi)部把它們稱作類型的算法。如果類型是一個(gè)映射的鍵,那么除相等函數(shù)外,編譯器還會(huì)生成一個(gè)哈希函數(shù)。為了維持穩(wěn)定,哈希函數(shù)在計(jì)算結(jié)果時(shí)也會(huì)像相等函數(shù)一樣考慮諸如填充等因素。

憑直覺判斷編譯器什么時(shí)候生成這些函數(shù)實(shí)際上很難,有時(shí)并不明顯,(因?yàn)椋┻@超出了你的預(yù)期,而且鏈接器也很難消除沒(méi)有被使用的函數(shù),因?yàn)榉瓷渫鶎?dǎo)致鏈接器在裁剪類型時(shí)變得更保守。

通過(guò)禁止比較來(lái)減小二進(jìn)制文件的大小

現(xiàn)在,我們來(lái)解釋一下 Brad 的修改。向類型添加一個(gè)不可比較的字段 3,結(jié)構(gòu)體也隨之變成不可比較的,從而強(qiáng)制編譯器不再生成相等函數(shù)和哈希函數(shù),規(guī)避了鏈接器對(duì)那些類型的消除,在實(shí)際應(yīng)用中減小了生成的二進(jìn)制文件的大小。作為這項(xiàng)技術(shù)的一個(gè)例子,下面的程序:

package main import "fmt" func main() {    type t struct {        // _ [0][]byte // 取消注釋以阻止比較        a byte        b uint16        c int32        d uint64    }    var a t    fmt.Println(a)}

用 Go 1.14.2(darwin/amd64)編譯,大小從 2174088 降到了 2174056,節(jié)省了 32 字節(jié)。單獨(dú)看節(jié)省的這  32  字節(jié)似乎微不足道,但是考慮到你的程序中每個(gè)類型及其傳遞閉包都會(huì)生成相等和哈希函數(shù),還有它們的依賴,這些函數(shù)的大小隨類型大小和復(fù)雜度的不同而不同,禁止它們會(huì)大大減小最終的二進(jìn)制文件的大小,效果比之前使用  -ldflags="-s -w" 還要好。

最后總結(jié)一下,如果你不想把類型定義為可比較的,可以在源碼層級(jí)強(qiáng)制實(shí)現(xiàn)像這樣的奇技淫巧,會(huì)使生成的二進(jìn)制文件變小。

上述就是小編為大家分享的如何通過(guò)禁止比較讓Go二進(jìn)制文件變小了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI