如何检查Golang项目的大小?

时间:2019-03-13 00:04:13

标签: go project

是否有一种简单的方法来检查Golang项目的大小?它不是可执行文件,而是我要在自己的项目中导入的软件包。

3 个答案:

答案 0 :(得分:5)

您可以通过查看$GOPATH/pkg目录来查看库二进制文件的大小(如果未导出$GOPATHgo默认为$HOME/go)。

因此要检查某些gorilla http pkgs的大小。首先安装它们:

$ go get -u github.com/gorilla/mux
$ go get -u github.com/gorilla/securecookie
$ go get -u github.com/gorilla/sessions

我的64位MacOS(darwin_amd64)上的KB二进制大小:

$ cd $GOPATH/pkg/darwin_amd64/github.com/gorilla/
$ du -k *

284 mux.a
128 securecookie.a
128 sessions.a

编辑:

库(包)的大小是一回事,但是在链接阶段之后,可执行文件中占用的空间可能有很大的不同。这是因为程序包具有自己的依赖关系,并附带了额外的行李,但行李可能会与您导入的其他程序包共享。

一个例子很好地说明了这一点:

empty.go:

package main

func main() {}

http.go:

package main

import "net/http"

var _ = http.Serve

func main() {}

mux.go:

package main

import "github.com/gorilla/mux"

var _ = mux.NewRouter

func main() {}

所有3个程序在功能上都是相同的-执行零用户代码-但它们的依赖性不同。在KB中产生的二进制大小:

$ du -k *

1028    empty
5812    http
5832    mux

这告诉我们什么?核心go pkg net/http给我们的可执行文件增加了很大的空间。 mux pkg本身并不大,但是对net/http pkg具有导入依赖性-因此,它的文件大小也很大。 muxhttp之间的增量仅为20KB,而mux.a库列出的文件大小为284KB。因此,我们不能简单地添加库的pkg大小来确定其实际占用空间。

结论: go链接器会在构建过程中从各个库中剥离很多行李,但是为了真正了解导入某些程序包的 weight 额外的多少,必须查看所有pkg的子依赖项

答案 1 :(得分:1)

您可以使用 import 下载所有 go mod vendored 模块,然后计数 不是测试文件的所有 .go 文件的行:

package main

import (
   "bytes"
   "fmt"
   "io/fs"
   "os"
   "os/exec"
   "path/filepath"
   "strings"
)

func count(mod string) int {
   imp := fmt.Sprintf("package main\nimport _ %q", mod)
   os.WriteFile("size.go", []byte(imp), os.ModePerm)
   exec.Command("go", "mod", "init", "size").Run()
   exec.Command("go", "mod", "vendor").Run()
   var count int
   filepath.WalkDir("vendor", func(s string, d fs.DirEntry, err error) error {
      if strings.HasSuffix(s, ".go") && !strings.HasSuffix(s, "_test.go") {
         data, err := os.ReadFile(s)
         if err != nil {
            return err
         }
         count += bytes.Count(data, []byte{'\n'})
      }
      return nil
   })
   return count
}

func main() {
   println(count("github.com/klauspost/compress/zstd"))
}

答案 2 :(得分:0)

这是另一种利用 https://pkg.go.dev/golang.org/x/tools/go/packages

的解决方案

我采用了作者提供的 example,并使用可用的演示二进制文件 here 对其进行了轻微更新。

package main

import (
    "flag"
    "fmt"
    "log"
    "os"
    "sort"

    "golang.org/x/tools/go/packages"
)

func main() {
    flag.Parse()

    // Many tools pass their command-line arguments (after any flags)
    // uninterpreted to packages.Load so that it can interpret them
    // according to the conventions of the underlying build system.
    cfg := &packages.Config{Mode: packages.NeedFiles |
        packages.NeedSyntax |
        packages.NeedImports,
    }
    pkgs, err := packages.Load(cfg, flag.Args()...)
    if err != nil {
        fmt.Fprintf(os.Stderr, "load: %v\n", err)
        os.Exit(1)
    }
    if packages.PrintErrors(pkgs) > 0 {
        os.Exit(1)
    }

    // Print the names of the source files
    // for each package listed on the command line.
    var size int64
    for _, pkg := range pkgs {
        for _, file := range pkg.GoFiles {
            s, err := os.Stat(file)
            if err != nil {
                log.Println(err)
                continue
            }
            size += s.Size()
        }
    }
    fmt.Printf("size of %v is %v b\n", pkgs[0].ID, size)

    size = 0
    for _, pkg := range allPkgs(pkgs) {
        for _, file := range pkg.GoFiles {
            s, err := os.Stat(file)
            if err != nil {
                log.Println(err)
                continue
            }
            size += s.Size()
        }
    }
    fmt.Printf("size of %v and deps is %v b\n", pkgs[0].ID, size)
}

func allPkgs(lpkgs []*packages.Package) []*packages.Package {
    var all []*packages.Package // postorder
    seen := make(map[*packages.Package]bool)
    var visit func(*packages.Package)
    visit = func(lpkg *packages.Package) {
        if !seen[lpkg] {
            seen[lpkg] = true

            // visit imports
            var importPaths []string
            for path := range lpkg.Imports {
                importPaths = append(importPaths, path)
            }
            sort.Strings(importPaths) // for determinism
            for _, path := range importPaths {
                visit(lpkg.Imports[path])
            }

            all = append(all, lpkg)
        }
    }
    for _, lpkg := range lpkgs {
        visit(lpkg)
    }
    return all
}