【问题标题】:How do I check the size of a Go project?如何检查 Go 项目的大小?
【发布时间】:2021-11-02 14:02:17
【问题描述】:

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

【问题讨论】:

  • 你所说的“大小”到底是什么意思?
  • 我的意思是字节大小。
  • 尺寸是什么?源文件?生成的可执行文件?
  • @Faminha102 更多地思考这个问题:关注 pkg 大小(基于它们的库文件大小)并不能给出完整的画面。我已经更新了答案来演示。

标签: go project


【解决方案1】:

您可以通过查看$GOPATH/pkg 目录来查看库二进制文件有多大(如果$GOPATH 未导出go 默认为$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

编辑:

库(包)大小是一回事,但在链接阶段之后在可执行文件中占用多少空间可能会有很大差异。这是因为包有它们自己的依赖项,并且随之而来的是额外的baggage,但这些包可能会被您导入的其他包共享。

一个例子最好地证明了这一点:

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 链接器会在构建过程中从各个库中去除很多包袱,但是为了真正了解导入某些包有多少额外的权重,我们必须查看所有的pkg 的子依赖项也是如此。

【讨论】:

    【解决方案2】:

    您可以使用go mod vendor 下载所有imported 模块,然后计算所有.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"))
    }
    

    【讨论】:

      【解决方案3】:

      这是另一个使用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
      }
      

      【讨论】:

        猜你喜欢
        • 2021-06-07
        • 2023-02-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-10-16
        • 2011-07-15
        • 1970-01-01
        • 2018-04-07
        相关资源
        最近更新 更多