编辑/澄清:
看来我在这里无法解释自己。我不是在批评Go,不是运行时,也不是可执行文件很大的事实。我也不是想说Go不好而C很好。
我只是指出,编译后的可执行文件似乎总是至少约1MB(大概是运行时的开销),并且导入软件包似乎会将整个软件包放入其中,而不管其用途如何。
我的实际问题是,这两个点是默认行为还是唯一行为?我给出了一些C语言程序的示例,这些示例在代码方面等效于Go程序,但是我为它们精心挑选了编译器和链接器标志,以避免与任何外部C运行时进行链接(我仅通过Dependency Walker进行了验证,确定)。 C示例的目的是演示实际代码有多小,并演示您确实需要某些东西而仅导入所需内容的情况。
我实际上认为这种行为(以防万一)是默认情况下的一个好设置,但是我认为可能会有一些编译器或链接器标志来更改此设置。现在,我不认为您不希望运行时或运行时的一部分是明智的。但是,我认为有选择地包括包装的各个部分并不是一件奇怪的事情。让我解释一下:
比方说,我们正在用C / C ++编写程序,其中包含一个带有大量函数的巨大头文件,但我们仅使用其中的一小部分。在这种情况下,最终可能会得到一个不包含该头文件中未使用的代码的可执行文件。 (实际示例:一个支持2D / 3D / 4D向量和矩阵,四元数等的数学库。所有这些库都有2个版本,一个用于32位浮点数,一个用于64位浮点数+从一个到另一个的转换)< / p>
这是我一直在寻找的东西。我完全理解这样做可能会在某些情况下引起问题,但仍然如此。并不是说Go没有其他可能导致严重问题的东西。.他们拥有“不安全”软件包,如果需要的话,可以使用该软件包,但是就像“自担风险”之类的软件包。
原始问题:
使用Go(golang)一段时间后,我决定研究它生成的可执行文件。我看到我的项目仅针对可执行文件的时钟就超过4.5MB,而另一个在复杂度和范围上相似但使用C / C ++(与MSVC编译)编写的项目却不到100KB。
所以我决定尝试一些尝试。我已经用C和Go编写了非常愚蠢和无用的简单程序来比较输出。 对于C,我正在使用MSVC,以发布模式编译64位可执行文件,并且未与C运行时链接(据我所知,Go可执行文件仅在使用CGO时才与之链接)
第一次运行:一个简单的无穷循环,就是这样。没有打印,没有与操作系统的交互,什么都没有。
C:
#include "windows.h"
int main()
{
while (true);
}
void mainCRTStartup()
{
main();
}
GO:
package main
func main() {
for {
}
}
结果:
C:3KB可执行文件。一无所有
GO:1,057 KB可执行文件。取决于KERNEL32.DLL
的29个过程那里存在巨大差异,但是我认为这可能是不公平的。因此,下一个测试我决定删除循环,只是编写一个程序,该程序立即以退出代码13返回:
C:
#include "windows.h"
int main()
{
return 13;
}
void mainCRTStartup()
{
ExitProcess(main());
}
GO:
package main
import "os"
func main() {
os.Exit(13)
}
结果:
C:4KB可执行文件。取决于KERNEL32.DLL的1个过程
GO:1,281 KB可执行文件。取决于KERNEL32.DLL中的31个过程
Go可执行文件似乎“ blo肿”。我了解到,与C不同,Go将大量运行时代码放入可执行文件中,这是可以理解的,但不足以解释其大小。
另外,Go似乎可以在软件包粒度上运行。我的意思是,它不会塞入您不使用的可执行程序包中,但是,即使您只需要一个很小的子集,即使您根本不使用它,也可以导入所有包。 。例如,仅导入“ fmt”而未在其中调用任何内容,会将先前的可执行文件从1,281KB扩展到1,777KB。
我是否错过了一些类似于Go编译器的标志来告知它不那么(肿(我知道我可以设置很多标志,还为本机编译器和链接器提供标志,但我对此没有找到特别是),或者这仅仅是2019年没人关心的事情了,因为实际上只有几兆字节?
答案 0 :(得分:0)
以下是Go程序所包含的一些内容,而C程序所没有的内容:
容器类型,例如哈希映射和数组及其关联函数
内存分配器,针对多线程程序进行了优化
并发垃圾收集器
线程类型和函数,例如互斥锁,条件变量,通道和线程
调试工具,例如堆栈跟踪转储和SIGQUIT处理程序
反射代码
(如果您确切想知道其中包含的内容,可以使用调试工具查看二进制文件中的符号。在macOS和Linux上,可以使用nm
将符号转储到程序中。)
问题是-大多数Go程序都使用所有这些功能!很难想象不使用垃圾回收器的Go程序。因此,Go的创建者尚未创建从程序中删除此代码的特殊方法-因为没人需要此功能。毕竟,您真的关心"Hello, world!"
有多大吗? 不,您没有。
通过常见问题解答Why is my trivial program such a large binary?
gc工具链中的链接器默认情况下会创建静态链接的二进制文件。因此,所有Go二进制文件都包含Go运行时,以及支持动态类型检查,反射甚至紧急时间堆栈跟踪所必需的运行时类型信息。
还请记住,如果要在Windows上使用MSVC进行编译,则可能正在使用DLL运行时,例如MSVCR120.DLL ...,大约1 MB。