web-dev-qa-db-ja.com

CGO_ENABLED = 0でのコンパイルが遅いのはなぜですか?

ネットワークを利用するプログラムを作成すると、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%の静的バイナリをコンパイルすることをお勧めしますが、そのような速度低下がある場合はそうしません。

そのような行動の原因は何ですか?

7

問題は、標準ライブラリパッケージがフラグなしでビルドされていることです。 CGO_ENABLEDはビルドフラグを変更するため、ビルド済みのパッケージを使用できないため、ほとんどの標準ライブラリを再ビルドする必要があります。

他の回答が述べたように、go build -iは新しいフラグでビルドされたパッケージをインストールしますが、CGO_ENABLED=0でビルドされたパッケージをインストールすると、将来のすべてのビルドが高速化されるため、実際にはそれほど解決しませんCGO_ENABLED=0、しかしそれなしではすべてのビルドが遅くなります。

残念ながら、今日の既定のビルド済みパッケージのインストール方法は、ビルド方法に関係なく、すべてが同じ名前で同じディレクトリに入るので、かなり非効率的です。異なるフラグを持つgoプログラムの高速ビルドを可能にしたい場合は、go build -iを実行する以外に、-installsuffixフラグまたは-pkgdirフラグも使用する必要があります。私が作業しているシステムでは、いくつかの異なるコンパイルモードがあり、各モードにはさまざまなフラグがあります(インターフェイスとなる古いCコードがたくさんあるため)。各モードにも独自の-pkgdirがあります。

11
Art

これは、依存関係の再構築に費やされた時間です。デフォルトでGoビルドは、再構築された依存関係を保存しません。 -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を指定すると、それらが保存され、2番目のビルド呼び出しがはるかに速くなります。

4
Marc