10种语言的Hello World实测:谁最快?谁最吃内存?


一、从一次“Hello World”引发的线上事故说起

几个月前,我接手了一个微服务的维护。这个服务用的是Python,功能很简单,就是接收请求然后返回一个字符串。说白了就是个Hello World级别的接口。但诡异的是,它经常在容器启动时被Kubernetes杀掉,原因是存活探针超时。探针其实就访问一下/health,而/health里就一行return "OK"

后来查了半天才发现,Python解释器启动本身就要加载一堆东西,再加上容器分配的内存只有128MB,启动时内存飙了一下就触发了OOM。最后换成了Go重写,同样的逻辑,二进制启动时间从400多毫秒降到了5毫秒。

那次之后我就犯嘀咕了:就一个最简单的Hello World,不同语言之间的差距到底有多大?光看网上那些“benchmark”总觉得是营销号在吹,不如自己动手测一遍。于是上个周末,我花了整整一天,把身边能用的10种语言全拉出来遛了一圈。


二、测试怎么做的,我先把规则说清楚

硬件环境:MacBook Pro M1 Pro,16GB内存,系统是macOS Ventura。所有语言都装到最新稳定版(截至上周末)。我统一用一个脚本跑,每种语言执行100次,取中位数。启动时间用hyperfine测,内存峰值用/usr/bin/time -l(macOS下)看,二进制大小用ls -lh看编译产物。

测试的代码都一样:打印一句“Hello, World!”然后立即退出。不导入任何第三方库,纯标准库实现。解释型语言直接执行源码,编译型语言先编译再执行二进制。


三、结果出来了,有些数据让我很意外

我先上个总览表(凭我记录的数据,大概是这样):

语言版本编译时间(ms)平均运行时间(ms)内存峰值(MB)二进制大小(KB)
Cclang 14180.30.816
Rust1.708500.40.942
Go1.201200.61.2890
Zig0.11350.30.78
Java1718028381.2 (jar)
Python3.11N/A429.5N/A
Node18.15N/A3814N/A
Ruby3.2N/A5612N/A
PHP8.2 (CLI)N/A4811N/A
C# (.NET 7)7.02201522145 (自包含)

这个表我反复跑了三遍,结果基本稳定。有几个数据让我愣了好久。


四、最大的意外:Go的二进制竟然比C大50倍?

我一直听人说Go编译出来的二进制“有点大”,但没想到这么离谱。C的Hello World只有16KB,而Go的竟然有890KB。虽然运行速度差距不大(0.6ms vs 0.3ms),但那个89万字节的体积让我一时有点接受不了。查了一下,是因为Go把运行时(垃圾回收、调度器)全都静态编译进去了。反正磁盘现在不贵,但放在边缘设备上就有点心疼了。

相比之下,Zig的二进制只有8KB,比C还小,让我有点惊喜。而且Zig编译速度极快,35ms,几乎感觉不到延迟。但Zig太新了,生态还不完善,这算是个小遗憾。

Rust编译要850ms,几乎是C的50倍。更夸张的是我还试了一次cargo build --release,用了1.2秒,而C的clang -O2只花了18ms。不过Rust的运行速度确实快,0.4ms,内存0.9MB,跟C基本持平。如果你不介意编译时多等一秒,Rust在性能和安全性上都很能打。


五、解释型语言的“慢”是真的慢,但原因不一样

Python跑了42ms,Java跑了28ms,PHP 48ms,Node 38ms,Ruby最慢56ms。这些都是启动到输出完成的总时间。但有意思的是,Java虽然启动慢,但比Python还快一点,因为JVM的即时编译在后台预热,但Hello World这种一次性运行根本享受不到预热的好处,反而被JVM启动拖累了。

我特意看了Python的内存峰值——9.5MB。一个打印字符串的程序,要吃掉快10MB内存,搁在十年前简直不敢想。但解释器就是这么重。Node也差不多,14MB。Ruby 12MB。PHP 11MB。这些语言在启动时就要把整个运行时环境加载进内存,所以它们的小程序启动开销相对固定。

一个特别扎心的数据是:Python跑一个Hello World的时间,够C语言跑140次。我算了一下,如果线上有个定时任务每10秒启动一次Python脚本做健康检查,它光启动就花掉42ms,而C语言只需要0.3ms,差距超过一百倍。虽然42ms对用户来说感觉不到,但累积在容器调度上就变得很刺眼。


六、我做了个更“过分”的测试:不加换行符会怎样?

跑完上面的数据,我突发奇想:如果我把Hello World里的\n去掉,结果会怎样?因为理论上少输出一个字符,应该能快一点点。

实测下来,C、Go、Rust这些编译型语言几乎没有变化,0.3ms还是0.3ms,小数点后面三四位才看得出来。但Python从42ms降到了41ms,Node从38ms到了37ms,变化极其微小,基本可以忽略不计。这说明启动开销才是大头,真正的print时间占比连5%都不到。

我又试了一个极端情况:循环打印1000次Hello World。这时候编译型语言的输出就比解释型快太多了,C用了0.8ms,Go用了1.2ms,而Python直接到了180ms,Node 150ms。因为每次print都要调用系统调用,上下文切换次数多了,差距就拉开了。


七、那到底选哪个?我说说我的结论

折腾了一天,我自己的感受是:

  • 如果你写的是命令行小工具、嵌入式或者对性能极度敏感,C、Zig、Rust都合适。Rust编译慢但安全,C最快但容易内存泄漏,Zig很新但前途不错。
  • 如果你写的是后端服务、API网关这类,Go是性价比之王。编译快,运行快,内存适中,虽然二进制大了点,但部署简单。我们公司后来就把那个Python服务换成了Go,容器启动时间从400多毫秒掉到8毫秒,再也没OOM过。
  • 如果你在做数据分析、自动化脚本或者快速原型,Python没毛病。虽然慢,但你写代码花的时间远大于它跑的时间。而且数据科学那一大堆库,别的语言真比不了。
  • Java适合大项目、多人协作、需要强类型和生态的场合,但别拿它写一次性的小工具,启动那下你就想砸电脑。
  • Node、PHP、Ruby,各有各的底盘,比如前端工具链离不开Node,传统Web还跑着大量PHP和Ruby。它们跑Hello World确实不占优,但人家本来也不是为了跑这个设计的。

八、最后一句实话

测完这10种语言后,我反而没那么纠结“哪个最快”了。因为绝大多数场景下,瓶颈根本不在Hello World这个级别。你写一个复杂的业务逻辑,数据库查询、网络IO、序列化这些操作才是大头,语言自身的启动开销往往被淹没。

但反过来说,如果你在做一个极度轻量的健康检查、定时任务、或者Serverless函数,那启动时间和内存占用就变得非常关键。我这次实测后,就把所有无状态的小服务统一换成了Go,因为它在编译速度、运行效率和开发体验之间平衡得最好。

最后,我把这次测试的所有代码和数据存了下来,放在一个叫hello-bench的仓库里,哪天忘了可以直接翻出来看。你如果感兴趣,也可以在自己的机器上跑一遍,说不定结果跟我不一样——因为操作系统、硬件、甚至终端窗口的宽度都会影响那点微小的耗时。但这就是实测的魅力,它告诉你书本上看不到的东西。

赞(0)
未经允许不得转载:helloworld官网下载 » 10种语言的Hello World实测:谁最快?谁最吃内存?