【问题标题】:How to remove unused code at compile time?如何在编译时删除未使用的代码?
【发布时间】:2017-08-07 03:30:30
【问题描述】:

我们已经构建了一个被我们很多人使用的 Go 包。

它是使用标准import ("package-name") 方法导入的。

但在编译时,我们所有的实用程序,包括非常小的实用程序,最终都会变成非常大的二进制文件。

我们提取了实用程序中的所有字符串,发现整个包都被编译到每个实用程序中。包括那些实用程序未使用的功能。

编辑 1:

感谢回答这个问题的人。

这是我们所看到的:

main.go

package main

import "play/subplay"

func main() {
    subplay.A()
}

play/subplay.go

package subplay

func A() {
    fmt.Printf("this is function A()")
}

func B() {
    fmt.Printf("secret string")
}

函数 B() 永远不会被调用。然而,在构建二进制文件后,我们在 main.exe 中找到了字符串“秘密字符串”。

我们如何在编译时从 Go 程序中删除未使用的代码?

【问题讨论】:

  • 你使用的是什么版本的 Go?因为如果没有调用subplay.B()"secret string" 确实不会出现在我的可执行文件中(Go 1.8,同时针对 linux 和 windows)!

标签: go compilation compiler-optimization


【解决方案1】:

编译器已经这样做了。所有代码都以包文件 (.a) 结尾,但在可执行二进制文件中,Go 工具不包含导入包中的所有内容,仅包含需要的内容(或更准确地说:它排除了可以证明无法访问的内容)。

查看可能重复的Splitting client/server code

这里要注意一点:如果一个导入的包(您只想从中包含您引用的东西)导入其他包,这当然必须递归完成。

例如如果你导入这个包:

package subplay

func A() {}

什么也不叫:

package main

import _ "play/subplay"

func main() {
}

结果二进制文件(Go 1.8、linux、amd64)将是 960,134 字节(大约 1 MB)。

如果您将subplay 更改为导入net/http

package subplay

import _ "net/http"

func A() {}

但仍然不要从net/http 调用任何东西,结果将是:5,370,935 字节(大约 5 MB)! (注意net/http 也会导入39 other packages!)

现在,如果你继续使用 net/http 中的东西:

package subplay

import "net/http"

func A() {
    http.ListenAndServe("", nil)
}

但在main包中你仍然不调用subplay.A(),可执行二进制文件的大小没有改变:仍然是5,370,935字节

当您将main 包更改为调用subplay.A() 时:

package main

import "play/subplay"

func main() {
    subplay.A()
}

结果二进制增长:5,877,919 字节

如果您将http.ListenAndServe() 更改为http.ListenAndServeTLS()

func A() {
    http.ListenAndServeTLS("", "", "", nil)
}

结果二进制为:6,041,535 字节

如您所见,编译成可执行二进制文件的内容取决于您从导入的包中调用/使用的内容

另外不要忘记 Go 是一种静态链接语言,结果可执行二进制文件必须包含它需要的所有内容。查看相关问题:Reason for huge size of compiled executable of Go

【讨论】:

  • 您好,Icza,感谢您的回复。我们看到了不同的结果。请参阅上面的更新消息以及详细信息。提前感谢您对此进行调查!
  • 值得注意的是,导入一个包,无论你是否使用它,都会导致包变量被初始化并且init()函数运行。这些可以引用包的其他部分,而包的其他部分又可以引用其他部分......所以,仅仅因为您没有引用包中的任何内容,并不意味着导入它会使整个包不使用,或者编译器将能够忽略它。
  • @Adrian 是的,这正是我的第二个示例应该证明的:没有使用来自 subplay 的任何内容,但可执行二进制文件增加了 4 MB,因为它引用了 net/http。跨度>
  • 只是对给出的原始数字提供一些解释。
【解决方案2】:

如果您不介意这些限制,您可以将您的软件包转换为 plugin(插件需要 Go 1.8,目前仅适用于 Linux)。

[编辑] 因为您使用的是 Windows,所以这不适合您。

【讨论】:

  • 而且每个插件本身都是静态链接的,因此会更大。
  • 将包解压到插件中意味着包的二进制代码只会在一个 .so 文件中,而不是在实用程序的可执行文件中。因此,该代码会按要求从实用程序中删除。当然插件本身是静态链接的,但实用程序会动态加载它,它们不会导入它。
  • 虽然插件在某些情况下可能有助于减小可执行二进制+插件文件的大小,但它们也可能会增加它!例如,如果插件导入 fmt 并且您的主应用程序也是如此,则 fmt 包将包含在可执行二进制文件和插件文件中!更不用说加载和使用插件比导入和使用包方便得多。更不用说性能了......
  • 他们仍然实现了在每个使用它的命令中不包含包中未使用(或已使用)的代码的目标。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-24
  • 2014-08-29
  • 1970-01-01
相关资源
最近更新 更多