web-dev-qa-db-fra.com

Pourquoi la compilation avec CGO_ENABLED = 0 est-elle plus lente?

Lors de l'écriture de programmes utilisant le réseau, vous pouvez constater un ralentissement assez notable de la compilation avec CGO_ENABLED=0.

Par exemple, le serveur HTTP le plus simple:

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)
    }
}

les horaires sont:

% 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

Jusqu'à présent, je n'utilise pas de liaisons vers C, donc la gestion de CGo ou non ne semble pas pertinente, ce que j'aimerais faire, c'est compiler 100% de binaires statiques, mais pas s'il y a un tel ralentissement.

Quelle est la cause d'un tel comportement?

7
Wojciech Kaczmarek

Le problème est que les packages de bibliothèque standard sont construits sans indicateurs. CGO_ENABLED change les drapeaux de construction et ne peut donc pas utiliser les packages pré-construits, donc la plupart de la bibliothèque standard doit être reconstruite.

Comme l'autre réponse l'a mentionné, go build -i installera les packages construits avec les nouveaux drapeaux, mais cela ne résoudra pas vraiment grand-chose car si vous installez des packages construits avec CGO_ENABLED=0, cela accélérera toutes les futures versions avec CGO_ENABLED=0, mais cela ralentira toutes les versions sans lui.

Malheureusement, la façon dont les packages pré-construits sont installés par défaut aujourd'hui est assez inefficace car tout va dans le même répertoire sous les mêmes noms, quelle que soit la façon dont il est construit. Si vous voulez avoir des builds rapides de programmes go avec différents drapeaux, en plus de faire go build -i vous devez également utiliser le -installsuffix et/ou le -pkgdir drapeaux. Dans le système dans lequel je travaille, nous avons une poignée de modes de compilation différents, chaque mode a des drapeaux différents (en raison de beaucoup de vieux code C avec lesquels nous nous connectons) et chaque mode a également son propre -pkgdir.

11
Art

Il s'agit du temps passé à reconstruire les dépendances. Go build par défaut n'enregistre pas les dépendances reconstruites. Voir le -i drapeau: The -i flag installs the packages that are dependencies of the target.

Essayons plutôt votre programme avec -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

La première fois que vous changez de mode cgo, il doit reconstruire les dépendances. Si vous spécifiez -i, il les enregistrera et le deuxième appel de build sera beaucoup plus rapide.

4
Marc