sizegame
这个仓库包含多种语言的经典"Hello World"程序,以及一个GitHub Action,用于编译这些程序并总结大小信息。
所有内容都是开源的 - 通过查看action定义和检查日志可以了解GitHub Action如何生成数据。
第3轮结果
语言 | 大小 (kB) | 编译器 | 备注 |
---|---|---|---|
汇编 | 2 | Microsoft Macro Assembler Version 14.35.32217.1 | |
Zig | 5 | 0.11.0-dev.4191+1bf16b172 | |
Nim | 123 | Nim Compiler Version 1.6.14 [Windows: amd64] | |
C | 135 | Microsoft C/C++ Optimizing Compiler Version 19.35.32217.1 for x64 | |
C++ | 230 | Microsoft C/C++ Optimizing Compiler Version 19.35.32217.1 for x64 | |
Rust | 250 | rustc 1.71.0 (8ede3aae2 2023-07-12) | |
D | 491 | DMD64 D Compiler v2.104.2-dirty | |
C# | 1087 | 8.0.100-preview.6.23330.14 | |
Scala | 1435 | Scala 3.2.2 Scala-native 0.4.10 | |
Go | 1928 | go version go1.20.6 windows/amd64 | |
Crystal | 1964 | Crystal 1.9.2 [1908c81] (2023-07-19) | |
F# | 3319 | 8.0.100-preview.6.23330.14 | |
Dart | 4669 | Dart SDK version: 3.0.6 (stable) (Tue Jul 11 18:49:07 2023 +0000) on "windows_x64" | |
Swift | 6100 | compnerd.org Swift version 5.8.1 (swift-5.8.1-RELEASE) | 包括:swiftCore.dll, vcruntime140.dll, vcruntime140_1.dll, msvcp140.dll |
Java | 6586 | native-image 17.0.8 2023-07-18 | 包括 vcruntime140.dll |
Kotlin | 6594 | kotlinc-jvm 1.8.10 | 包括 vcrtuntime140.dll,使用GraalVM进行AOT编译,版本与Java基准测试相同 |
Haskell | 11388 | The Glorious Glasgow Haskell Compilation System, version 9.6.2 |
规则
- 程序应预先编译。默认使用即时编译/解释的语言应以规范方式预先编译。
- 程序应以惯用方式打印Hello World,使用语言自带的标准库。理想情况下,使用该语言官方入门教程中的相同代码片段。
- 程序应使用编译器默认设置进行编译。
- 如果默认未启用优化,可以启用优化。如果编译器有一个主开关"启用优化",应使用该开关。只要不改变语义,可以将优化偏好设置为大小优化。
- 程序应在原生操作系统安装上运行。理想情况下,应静态链接非操作系统依赖项。如果无法实现,报告的大小将包括原生操作系统安装未提供的所有动态库的大小。
这些规则的动机很简单 - 经典的Hello World和经典的编译器设置衡量的是经典的用户体验。所有测量的语言都有方法产生更小的Hello World。例如,在Go中禁用文本回溯,在C中不使用标准库,对任何这些语言修改链接器开关等。
但是,语言/编译器开发人员选择这些默认设置是有原因的 - 默认设置符合语言和标准库的宣传。因此,这是一个客观的衡量标准。如果你不同意默认设置的含义,请向编译器/语言维护者提出。这个仓库不是讨论这个问题的地方。
如果没有这些规则,这个仓库就会变成一场比拼谁最小的竞赛。我们知道有人让Rust编译成了464字节。我们知道C#可以编译成几千字节。这也不有趣,因为这些解决方案不再履行语言或标准库的承诺。它们仅仅是艺术项目。中间有很多灰色地带,但灰色地带只是争议的地方。
常见问题
我可以从中得出关于语言X的什么结论?
你可以得出的结论是,以规范方式用语言X编写的Hello World,使用规范设置编译后,编译为Y字节。仅此而已。
这些程序执行相同的操作吗?这是臃肿吗?
虽然向控制台写入看起来是一个简单的问题,但实际上相当复杂。如果输出重定向到文件,而目标文件系统没有空间,写入可能会失败。在Windows上,我们必须处理控制台的活动代码页。一些语言通过流抽象进行控制台写入。
控制台写入通常由标准库定制,以匹配该语言中的一般开发人员期望。
例如,虽然Zig可执行文件的大小令人印象深刻,但如果我们将"Hello, World!"字符串替换为"Kŕdeľ ďatľov učí koňa žrať kôru",在我的机器上,Zig程序会打印以下文本:"K┼òde─╛ ─Åat─╛ov u─ì├¡ ko┼êa ┼╛ra┼Ñ k├┤ru。"(将UTF-8发送到不支持UTF-8的地方),而C#(可能还有其他语言)会写入"Krdel datlov ucí kona zrat kôru。",因为它们会先进行到控制台代码页的有损转换。只要符合开发人员的预期,这就没问题。
同样,并非所有语言都以相同方式处理错误条件。如果操作系统处于低内存状况,可能无法将文本转换为控制台代码页。如果stdout重定向到文件而磁盘已满,标准库可能需要引发异常。引发异常可能需要打印回溯。所有这些都会在磁盘上"占用"一些空间。有些语言和标准库比其他语言更关注这些问题。这并不是免费的。
我可以通过做Y使语言X编译成更小的东西
如果我们使用printf
而不是流,C++示例可以更小。同样,如果我们使用puts
而不是printf
,C示例可以更小。同样,如果我们从libc调用puts
而不是System.Console
,C#示例可以更小。如规则部分所述,我们测量的是规范的东西,使用默认的编译器设置。否则就不可能划定界限。一个图表中大多数东西编译到<1 kB,而一个"Rust"程序主要由内联汇编组成以达到那个大小并不会产生有趣的图表。即使C#也会在几kB范围内。这将是一个无聊的图表,几乎无法反映现实。