交叉编译

交叉编译主要是两个编译环境参数 $GOOS 和 $GOARCH 的设定。$GOOS代表编译的目标系统,$GOARCH代表编译的处理器体系结构。

$GOOS可选值如下:

$GOARCH可选值如下:

选择性编译

xxx_windows.go xxx.Linux.goif else

此标记必须出现在文件顶部,仅由空行或其他注释行开头。也就是必须在Package 语句前。

 // +build A,B !C,D 逗号为且,空格为或,!为非

1.17以及后续版本有了新写法

很不幸,上面+build的写法在1.17后续版本中会被改进,以后推荐用新版本书写。

新版本go在编译时候只需要新写法,当前新老写法可以同时存在,但是只会采用新写法编译。老版本不识别的情况下,只能提供+build的写法。

编译选项

排除调试信息 -ldflags="-s -w"

Go 编译器默认编译出来的程序会带有符号表和调试信息,一般来说 release 版本可以去除调试信息以减小二进制体积。

  • -s:忽略符号表和调试信息。
  • -w:忽略DWARFv3调试信息,使用该选项后将无法使用gdb进行调试。

逃逸分析 -gcflags=-m

mallocfree
-gcflags=-m

利用逃逸分析提升性能–传值vs传指针

传值会拷贝整个对象,而传指针只会拷贝指针地址,指向的对象是同一个。传指针可以减少值的拷贝,但是会导致内存分配逃逸到堆中,增加垃圾回收(GC)的负担。在对象频繁创建和删除的场景下,传递指针导致的 GC 开销可能会严重影响性能。

一般情况下,对于需要修改原对象值,或占用内存比较大的结构体,选择传指针。对于只读的占用内存较小的结构体,直接传值能够获得更好的性能。

参考阅读:

upx压缩可执行程序

upx 是一个常用的压缩动态库和可执行文件的工具,通常可减少 50-70% 的体积。

1-919
$ go build -o server main.go && upx -9 server

汇编分析

要想深入研究编程语言的性能,查看汇编代码是最好的办法,他能让你很直观的了解到,这样写代码CPU究竟是如何运行的。这方面的知识参考下面文章。

参考:

Go汇编代码特点

要了解 Go 的汇编器最重要的是要知道 Go 的汇编器不是对底层机器的直接表示,即 Go 的汇编器没有直接使用目标机器的汇编指令。Go 汇编器所用的指令,一部分与目标机器的指令一一对应,而另外一部分则不是。这是因为编译器套件不需要汇编器直接参与常规的编译过程。

相反,编译器使用了一种半抽象的指令集,并且部分指令是在代码生成后才被选择的。汇编器基于这种半抽象的形式工作,所以虽然你看到的是一条 MOV 指令,但是工具链针对对这条指令实际生成可能完全不是一个移动指令,也许会是清除或者加载。也有可能精确的对应目标平台上同名的指令。概括来说,特定于机器的指令会以他们的本尊出现, 然而对于一些通用的操作,如内存的移动以及子程序的调用以及返回通常都做了抽象。细节因架构不同而不一样,我们对这样的不精确性表示歉意,情况并不明确。

汇编器程序的工作是对这样半抽象指令集进行解析并将其转变为可以输入到链接器的指令。

如何输出 Go 汇编

对于写好的 go 源码,生成对应的 Go 汇编,大概有下面几种

go build -gcflags "-N -l" main.gogo tool objdump -s "main." main
"main.""main.main"
go tool compile -S -N -l main.gogo build -gcflags="-N -l -S" main.go

更多参考:

(完)