Go:讲一个故事说明使用汇编语言的必要性
2022-12-27 08:54:48 Author: Go语言中文网(查看原文) 阅读量:23 收藏

- 1 -

有一天,有一个程序员叫做 Bob,他写了一个用 Go 语言实现的阶乘函数。但是,当数据规模变大时,运行速度非常慢。于是,他的老板让他改成用汇编语言实现。Bob 很不情愿,但还是学习了汇编语言,并写出了一个能跑得飞快的阶乘函数。

最后,Bob 成为了一名出色的汇编程序员,并因为他的阶乘函数而获得了巨大的成功。他明白,汇编语言可以让他的程序更快、更简洁。

所以,如果你想写出高效、简洁的程序,就要学习汇编语言。


- 2 -

咱们先从简单的入手,用 Go 实现一个完整的阶乘函数:

package main
import "fmt"
func factorial(n int) int { result := 1 for i := 1; i <= n; i++ { result *= i } return result}
func main() { fmt.Println(factorial(5)) // Output: 120}

- 3 -

接下来,我们把 factorial 函数转换成汇编语言。

首先,我们要在 Go 代码里将 factorial 函数的函数体移除,只包含函数定义(声明),这告诉 Go 编译器,该函数会在另一个文件里由汇编实现。

package main
import "fmt"
func factorial(n int) int
func main() { fmt.Println(factorial(5))}

然后,我们新建一个文件 fac.s,内容如下:

TEXT ·factorial(SB), $0-8    MOVQ n+0(FP), CX    MOVQ $1, DXLOOP:    IMULQ CX, DX    DECQ CX    JNZ LOOP    MOVQ DX, result+8(FP)    RET

编译运行:

$ go run . 

120


- 4 -

这段代码是一个用汇编语言实现的阶乘函数。它的作用是计算给定数字 n 的阶乘。

它的实现方式是使用一个无限循环来计算阶乘。在循环的每一次迭代中,它将结果与当前的数字相乘,然后将当前的数字减 1。最后,它将结果存储到 result 变量中,并退出函数。

接下来咱们逐句分析这段代码:

  1. TEXT ·factorial(SB), $0-8

    1. TEXT 表示这个方法在 TEXT 段中

    2. · 是Unicode的「中点」(中文输入法,1左边的按键),前面省略了包名,表示这是 main 包的 factorial 函数

    3. SB 是 stack base pointer,Go ASM 中的「伪寄存器」(不是硬件寄存器),大致等同于程序的起始地址

    4. $0-8:0 表示这个函数没有局部变量,8 表示返回值占用8个字节

  2. MOVQ n+0(FP), CX

    1. MOVQ 的 Q 表示 8 个字节

    2. n+0(FP) 表示变量 n 在 FP(Frame Pointer,伪寄存器,表示这个函数的栈帧起始位置) + 0 的位置(即第一个参数)。注意 Go ASM 要求形式上必须是「变量名+偏移量(FP)」这个写法,但是变量名n没有实际意义,只是用来助记。

    3. CX 即 x86/x86_64 的 CX(16bit),ECX(32bit),RCX(64bit) 寄存器,具体多长取决于前面的指令(MOVQ是64bit)

    4. 这句的意思是把第一个参数的值写入 RCX

  3. MOVQ $1, DX

    1. $1:$开头的是立即数

    2. 这句的意思是给 RDX 赋值为 1

  4. IMULQ CX, DX

    1. DX = DX * CX

  5. DECQ CX

    1. CX = CX - 1

  6. JNZ LOOP

    1. JNZ: Jump if Not Zero

    2. 当CX 不等于 0 时跳转到 LOOP

  7. MOVQ DX, result+8(FP)

    1. 将 RDX 的值写入到 FP+8 的位置。

  8. RET

    1. 返回到调用方。


- 5 -

需要注意的是,为了实现上更简洁,这段汇编代码和Go代码并不是等价的。

如果输入的 n 为负数,会导致代码出错。这是因为,这段代码中没有判断边界条件,所以如果输入的 n 为负数,就会无限循环下去,造成程序运行时间过长或者程序崩溃。


- 6 -

最后,这篇文章是 ChatGPT 和我一起完成的。

细心如你,应该发现了哪些是它的贡献,以及它犯的一个错误。

p.s. 题图由 6pen.art 生成,关键词「Golang 汇编语言 阶乘」

参考:

[1] Golang ASM 简明教程:https://jiajunhuang.com/articles/2020_04_22-go_asm.md.html

[2] A Quick Guide to Go's Assembler:https://go.dev/doc/asm


推荐阅读

福利
我为大家整理了一份从入门到进阶的Go学习资料礼包,包含学习建议:入门看什么,进阶看什么。关注公众号 「polarisxu」,回复 ebook 获取;还可以回复「进群」,和数万 Gopher 交流学习。


文章来源: http://mp.weixin.qq.com/s?__biz=MzAxMTA4Njc0OQ==&mid=2651453912&idx=1&sn=9165100dccb16d411f66a92fc66d58a5&chksm=80bb272ab7ccae3cd5ed568b006b8348362fcc9e0674cd11a52271c7c0f12a5ed02b2f961ee2#rd
如有侵权请联系:admin#unsafe.sh