为什么CGO_ENABLED = 0的编译速度较慢?

时间:2017-12-08 12:18:08

标签: go compilation cgo

在编写利用网络的程序时,您可以看到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%静态二进制文件,但如果有这样的减速则不行。

这种行为的原因是什么?

2 个答案:

答案 0 :(得分:5)

问题是标准库包是在没有标志的情况下构建的。 CGO_ENABLED更改了构建标志,因此无法使用预构建的软件包,因此需要重建大部分标准库。

正如提到的另一个答案,go build -i将安装使用新标志构建的软件包,但这并不会真正解决太多因为如果您安装使用CGO_ENABLED=0构建的软件包,它将加速所有未来使用CGO_ENABLED=0构建,但如果没有它,它将减慢所有构建。

不幸的是,今天默认安装预构建软件包的方式非常低效,因为无论它是如何构建的,所有内容都以相同的名称进入同一目录。如果您希望能够快速构建带有不同标志的go程序,除了执行go build -i之外,您还需要使用-installsuffix和/或-pkgdir标志。在我工作的系统中,我们有一些不同的编译模式,每个模式都有不同的标志(因为我们接口的很多旧C代码),每个模式也有自己的-pkgdir

答案 1 :(得分:2)

这是重建依赖项所花费的时间。 默认情况下,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,它将保存它们,第二次构建调用会更快。