女主宣言
ps:豐富的一線技術、多元化的表現形式,盡在「360雲計算」,點關注哦!
迴圈是程式設計中很強大的乙個概念,而且非常容易處理。但是,必須將其翻譯成機器可理解的基本指令。它的編譯方式也可能影響標準庫中的其他元件。讓我們開始分析一下範圍迴圈。
迴圈彙編
範圍迴圈可以迭代陣列,切片或通道。下面函式展示了,對分片進行迴圈並將數字相加:
func main()
t := 0
for _, v := range l
println(t)
}
執行 go tool compile -s main.go 可以轉儲生成彙編**,下面為範圍迴圈的相關**。
0x0041 00065 (main.go:4) xorl ax, ax
0x0043 00067 (main.go:4) xorl cx, cx
0x0045 00069 (main.go:7) jmp 82
0x0047 00071 (main.go:7) movq ""..autotmp_5+16(sp)(ax*8), dx
0x004c 00076 (main.go:7) incq ax
0x004f 00079 (main.go:8) addq dx, cx
0x0052 00082 (main.go:7) cmpq ax, $5
0x0056 00086 (main.go:7) jlt 71
0x0058 00088 (main.go:11) movq cx, "".t+8(sp)
我們把指令分為兩部分:初始化及迴圈本身。最開始兩行指令用來初始化兩個暫存器為0。
0x0041 00065 (main.go:4) xorl ax, ax
0x0043 00067 (main.go:4) xorl cx, cx
暫存器ax包含迴圈中的當前位置,而cx包含變數 t 的值。下面是帶有指令和通用暫存器的直觀表示:
該迴圈指令 jmp 82 開始,表示跳轉到指令82。可以通過第二列來標識此目標指令:
下一條指令 cmpq ax, $5 表示「比較暫存器ax和數值5」。它實際上是從ax中減去暫存器dx的值,並將結果儲存到另乙個暫存器中。現在,可以在下一條指令jlt 71中使用該值,該指令表示「如果小於0,則跳轉到指令71。」下面是更新後的圖:
如果條件不滿足,則程式將不會跳轉執行迴圈後面的下一條指令。
因此,我們現在有了迴圈的結構。下面是轉換回 go 的迴圈:
goto end
start:
?end:
if i < 5
println(t)
該迴圈的主體是缺失的,下面是指令:
0x0047 00071 (main.go:7) movq ""..autotmp_5+16(sp)(ax*8), dx
0x004c 00076 (main.go:7) incq ax
0x004f 00079 (main.go:8) addq dx, cx
第乙個指令 movq ""..autotmp_5+16(sp)(ax*8), dx 表示「將記憶體從源移動到目標」。由以下內容組成:
然後,incq 代表「遞增」,並將遞增迴圈的當前位置:
迴圈體的最後一條指令是 addq dx, cx 表示「將dx新增到cx」。之前我們已經看到dx包含迴圈的當前值,而cx是包含變數 t 內容的暫存器:
它將一直迴圈直到迴圈計數器到達5。然後,迴圈之後的指令顯示暫存器cx將其值移至 t :
0x0058 00088 (main.go:11) movq cx, "".t+8(sp)
這是處於最終狀態的圖:
我們還可以在go中完成迴圈的翻譯:
func main()
t := 0
i := 0
var tmp int
goto end
start:
tmp = l[i]
i++t += tmp
end:
if i < 5
println(t)
}
為這個新程式生成彙編**,將提供完全相同的輸出。
改進
內部轉換迴圈的方式可能會對其他功能(例如go排程程式)產生影響。在go 1.10之前,編譯的迴圈類似於以下**:
func main()
t := 0
i := 0
var tmp int
p := uintptr(unsafe.pointer(&l[0]))
if i >= 5
body:
tmp = *(*int)(unsafe.pointer(p))
p += unsafe.sizeof(l[0])
i++t += tmp
if i < 5
end:
println(t)
}
這種實現方式的問題是,當達到5時,指標p超過了分配的末尾。這個問題使迴圈不容易被搶占,因為它的主體不安全。迴圈編譯的優化確保它不會建立任何過去的指標。為準備go排程程式中的非合作式搶占而進行了此改進。
360雲計算
Golang彙編快速指南
有些符號,例如pc,r0和sp,是預定義的並且是對乙個暫存器的引用。另外還有兩種預定義的符號,sb static base 和fp frame pointer 所有使用者定義的符號,除了標籤跳轉之外,都是對偽暫存器的offsets操作。1 sb偽暫存器可以想象成記憶體的位址,所以符號foo sb 是...
golang逃逸分析
帶gc語言給我們程式的編寫帶來了極大的便利,但是與此同時遮蔽了很多底層的細節,比如乙個物件是在棧上分配還是在堆上分配。對於普通的 來說雖然不需要關心這麼多,但是作為強迫症程式猿,還是希望能讓自己寫出來的 效能最優,所以還是需要了解什麼是逃逸,以及如何判斷是否發生了逃逸。首先需要知道,我們說的堆和棧是...
Golang中的struct比較
可比較的資料型別除了上述三種外,還有boolean,complex,pointer,channel,inte ce和array 不可比較的資料型別包括,slice,map,和function 不包含不可比較的成員變數 reflect.deepequal函式,指標和例項均可以比較 無論有沒有包含不可比...