91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

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

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何通過禁止比較讓Go二進制文件變小

發布時間:2021-10-28 16:15:24 來源:億速云 閱讀:131 作者:柒染 欄目:編程語言

這期內容當中小編將會給大家帶來有關如何通過禁止比較讓Go二進制文件變小,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

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

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

定義兩個值相等

Go 的語法定義了“賦值”和“相等”的概念。賦值是把一個值賦給一個標識符的行為。并不是所有聲明的標識符都可以被賦值,如常量和函數就不可以。相等是通過檢查標識符的內容是否相等來比較兩個標識符的行為。

作為強類型語言,“相同”的概念從根源上被植入標識符的類型中。兩個標識符只有是相同類型的前提下,才有可能相同。除此之外,值的類型定義了如何比較該類型的兩個值。

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

上面都是按位比較相等的例子,即值占用的內存的位模式是相同的,那么這些值就相等。這就是所謂的 memcmp,即內存比較,相等是通過比較兩個內存區域的內容來定義的。

記住這個思路,我過會兒再來談。

結構體相等

除了整型、浮點型和指針等標量類型,還有復合類型:結構體。所有的結構體以程序中的順序被排列在內存中。因此下面這個聲明:

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

會占用 32 字節的內存空間;a 占用 8 個字節,b 占用 8 個字節,以此類推。Go 的規則說如果結構體所有的字段都是可以比較的,那么結構體的值就是可以比較的。因此如果兩個結構體所有的字段都相等,那么它們就相等。

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

編譯器在底層使用 memcmp 來比較 a 的 32 個字節和 b 的 32 個字節。

填充和對齊

然而,在下面的場景下過分簡單化的按位比較的策略會返回錯誤的結果:

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}

編譯代碼后,這個比較表達式的結果還是 true,但是編譯器在底層并不能僅依賴比較 ab 的位模式,因為結構體有填充

Go 要求結構體的所有字段都對齊。2 字節的值必須從偶數地址開始,4 字節的值必須從 4 的倍數地址開始,以此類推 1。編譯器根據字段的類型和底層平臺加入了填充來確保字段都對齊。在填充之后,編譯器實際上看到的是 2

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

填充的存在保證了字段正確對齊,而填充確實占用了內存空間,但是填充字節的內容是未知的。你可能會認為在 Go 中 填充字節都是 0,但實際上并不是 — 填充字節的內容是未定義的。由于它們并不是被定義為某個確定的值,因此按位比較會因為分布在 s 的 24 字節中的 9 個填充字節不一樣而返回錯誤結果。

Go 通過生成所謂的相等函數來解決這個問題。在這個例子中,s 的相等函數只比較函數中的字段略過填充部分,這樣就能正確比較類型 s 的兩個值。

類型算法

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

憑直覺判斷編譯器什么時候生成這些函數實際上很難,有時并不明顯,(因為)這超出了你的預期,而且鏈接器也很難消除沒有被使用的函數,因為反射往往導致鏈接器在裁剪類型時變得更保守。

通過禁止比較來減小二進制文件的大小

現在,我們來解釋一下 Brad 的修改。向類型添加一個不可比較的字段 3,結構體也隨之變成不可比較的,從而強制編譯器不再生成相等函數和哈希函數,規避了鏈接器對那些類型的消除,在實際應用中減小了生成的二進制文件的大小。作為這項技術的一個例子,下面的程序:

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,節省了 32 字節。單獨看節省的這  32  字節似乎微不足道,但是考慮到你的程序中每個類型及其傳遞閉包都會生成相等和哈希函數,還有它們的依賴,這些函數的大小隨類型大小和復雜度的不同而不同,禁止它們會大大減小最終的二進制文件的大小,效果比之前使用  -ldflags="-s -w" 還要好。

最后總結一下,如果你不想把類型定義為可比較的,可以在源碼層級強制實現像這樣的奇技淫巧,會使生成的二進制文件變小。

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

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

台湾省| 涿州市| 宣武区| 兴城市| 景洪市| 广水市| 平舆县| 文登市| 宽甸| 潼南县| 旌德县| 延川县| 淮滨县| 调兵山市| 新化县| 宣武区| 和平区| 乌拉特前旗| 哈密市| 苗栗县| 西畴县| 大足县| 资兴市| 安宁市| 新邵县| 莱芜市| 敦煌市| 虞城县| 鸡西市| 县级市| 孟村| 海安县| 东方市| 双柏县| 饶阳县| 兰考县| 洛扎县| 搜索| 崇礼县| 永康市| 囊谦县|