为什么使用 CGO_ENABLED=0 编译速度较慢?
Why is compiling with CGO_ENABLED=0 slower?
在编写利用网络的程序时,您会发现 CGO_ENABLED=0
的编译速度明显变慢。
例如最简单的HTTP服务器:
package main
import (
"flag"
"fmt"
"log"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi! glad you requested %s.\n", r.URL.Path[1:])
}
func main() {
port := flag.Int("port", 9000, "")
flag.Parse()
http.HandleFunc("/", handler)
err := http.ListenAndServe(fmt.Sprintf(":%d", *port), nil)
if err != nil {
log.Fatal(err)
}
}
时间安排是:
% time go build
go build 0.46s user 0.06s system 131% cpu 0.396 total
% time CGO_ENABLED=0 go build
CGO_ENABLED=0 go build 3.93s user 0.15s system 143% cpu 2.849 total
到目前为止,我没有使用 C 的绑定,所以 CGo 处理与否似乎无关紧要,我想做的是编译 100% 静态二进制文件,但如果速度变慢则不会。
这种行为的原因是什么?
这是重新构建依赖项所花费的时间。
默认情况下,Go build 不会保存重建的依赖项。查看 -i
标志:
The -i flag installs the packages that are dependencies of the target.
让我们用 -i 试试你的程序:
$ time go build -i .
real 0m0.337s
user 0m0.343s
sys 0m0.121s
$ time CGO_ENABLED=0 go build -i .
real 0m2.135s
user 0m3.098s
sys 0m0.196s
$ time CGO_ENABLED=0 go build .
real 0m0.329s
user 0m0.367s
sys 0m0.085s
$ time go build .
real 0m2.588s
user 0m3.393s
sys 0m0.300s
第一次切换cgo模式需要重建依赖。如果您指定 -i
,它将保存它们并且第二次构建调用会快得多。
问题是标准库包是在没有标志的情况下构建的。 CGO_ENABLED
更改了构建标志,因此它无法使用预构建的包,因此需要重建大部分标准库。
正如另一个答案提到的,go build -i
将安装使用新标志构建的包,但这并不能真正解决很多问题,因为如果您安装使用 CGO_ENABLED=0
构建的包,它会加快速度使用 CGO_ENABLED=0
启动所有未来的构建,但没有它会减慢所有构建。
不幸的是,今天默认安装预构建包的方式非常低效,因为无论它是如何构建的,所有东西都以相同的名称进入同一个目录。如果你希望能够使用不同的标志快速构建 go 程序,除了 go build -i
你还需要使用 -installsuffix
and/or -pkgdir
标志。在我工作的系统中,我们有几种不同的编译模式,每种模式都有不同的标志(因为我们使用了很多旧的 C 代码)并且每种模式也有自己的 -pkgdir
.
在编写利用网络的程序时,您会发现 CGO_ENABLED=0
的编译速度明显变慢。
例如最简单的HTTP服务器:
package main
import (
"flag"
"fmt"
"log"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi! glad you requested %s.\n", r.URL.Path[1:])
}
func main() {
port := flag.Int("port", 9000, "")
flag.Parse()
http.HandleFunc("/", handler)
err := http.ListenAndServe(fmt.Sprintf(":%d", *port), nil)
if err != nil {
log.Fatal(err)
}
}
时间安排是:
% time go build
go build 0.46s user 0.06s system 131% cpu 0.396 total
% time CGO_ENABLED=0 go build
CGO_ENABLED=0 go build 3.93s user 0.15s system 143% cpu 2.849 total
到目前为止,我没有使用 C 的绑定,所以 CGo 处理与否似乎无关紧要,我想做的是编译 100% 静态二进制文件,但如果速度变慢则不会。
这种行为的原因是什么?
这是重新构建依赖项所花费的时间。
默认情况下,Go build 不会保存重建的依赖项。查看 -i
标志:
The -i flag installs the packages that are dependencies of the target.
让我们用 -i 试试你的程序:
$ time go build -i .
real 0m0.337s
user 0m0.343s
sys 0m0.121s
$ time CGO_ENABLED=0 go build -i .
real 0m2.135s
user 0m3.098s
sys 0m0.196s
$ time CGO_ENABLED=0 go build .
real 0m0.329s
user 0m0.367s
sys 0m0.085s
$ time go build .
real 0m2.588s
user 0m3.393s
sys 0m0.300s
第一次切换cgo模式需要重建依赖。如果您指定 -i
,它将保存它们并且第二次构建调用会快得多。
问题是标准库包是在没有标志的情况下构建的。 CGO_ENABLED
更改了构建标志,因此它无法使用预构建的包,因此需要重建大部分标准库。
正如另一个答案提到的,go build -i
将安装使用新标志构建的包,但这并不能真正解决很多问题,因为如果您安装使用 CGO_ENABLED=0
构建的包,它会加快速度使用 CGO_ENABLED=0
启动所有未来的构建,但没有它会减慢所有构建。
不幸的是,今天默认安装预构建包的方式非常低效,因为无论它是如何构建的,所有东西都以相同的名称进入同一个目录。如果你希望能够使用不同的标志快速构建 go 程序,除了 go build -i
你还需要使用 -installsuffix
and/or -pkgdir
标志。在我工作的系统中,我们有几种不同的编译模式,每种模式都有不同的标志(因为我们使用了很多旧的 C 代码)并且每种模式也有自己的 -pkgdir
.