常见编程语言性能对比之计算斐波拉契数

前言

最近简单学了下Rust,以我这种菜鸟水平,没感受到什么安全、性能什么方面的优势,只觉得概念太多,编译各种报错。暂时也写不出来什么玩法,索性整个活,对比下各种学过的语言的性能。部分语言很早之前学过,很久不用就忘了,所以有些是用GPT写的。但运行逻辑很简单,所以应该没什么影响。具体的代码可以见"实验代码"部分。

对比方法是在同一台机器上计算斐波拉契数,获取运行时长和内存占用。对比方法很野鸡,看看当个乐就行。

根据个人工作经验来说,大部分业务场景性能只要够用就行,能尽快下班的语言就是好语言。

实验准备

  • 测试主机:虚拟机
    • 系统:Debian 12.5 x86_64
    • CPU:4 核
    • 内存:4 GB

使用time命令计算运行时长和内存消耗。示例,计算C二进制程序的运行时长和内存

bash 复制代码
/usr/bin/time -f 'Elapsed Time: %e s Max RSS: %M kbytes' ./fib-c 45

# 输出结果
The 45th Fibonacci number is 1134903170
Elapsed Time: 8.36 s Max RSS: 1444 kbytes

部分语言需要编译,二进制文件和代码如下,C编译出来的二进制文件只有16KB。

bash 复制代码
-rwxr-xr-x 1 atlas atlas  16K Mar 16 16:21 fib-c  # C编译的二进制文件
-rwxr-xr-x 1 atlas atlas 1.8M Mar 16 15:56 fib-go  # Go编译的二进制文件
-rwxr-xr-x 1 atlas atlas 7.7M Mar 16 17:47 fib-graalvm  # GraalVM编译的二进制文件
-rw-r--r-- 1 atlas atlas  175 Mar 16 18:06 fib-js.js  # JavaScipt源代码文件
-rw-r--r-- 1 atlas atlas  643 Mar 16 16:48 fib-lua.lua  # lua 代码文件
-rw-r--r-- 1 atlas atlas  377 Mar 16 16:49 fib-lua.luac  # LuaJIT编译文件
-rw-r--r-- 1 atlas atlas  981 Mar 16 19:17 Fibonacci.class  # javac编译文件
-rw-r--r-- 1 atlas atlas  622 Mar 16 17:18 Fibonacci.java  # Java源代码文件
-rw-r--r-- 1 atlas atlas  322 Mar 16 16:31 fib-python.py  # python源代码文件
-rwxr-xr-x 1 atlas atlas 3.7M Mar 16 15:56 fib-rust  # rust编译的二进制文件

实验结果

递归计算数值 45 的斐波拉契数,确保计算出来的值都是1134903170。

根据结果数据来看,Java(OpenJDK)计算最快,但内存占用相当高,接近rust运行内存的20倍。换成GraalVM编译后,内存占用会少很多,但还是比Rust和C要多。

C的内存占用最低,Rust和C基本齐平,二者运行时长也差不多。

Python最慢,意料之中。。但Python平常个人写的也很多,开发速度相当快。

Go平常也经常写,速度和内存占用都尚可,语法也很简单。写习惯了Go的并发语法,再写其它语言就有点感觉怪怪的。

Lua使用本身的解释器运行是很慢的,用luajit编译后效率提升很多。

JS并不熟,完全用GPT给的测试方案。运行速度还行,就是内存占用比Java都高。貌似也有其它js运行时,可能性能比nodejs要好。

语言 版本 运行时长(seconds) 环比Rust(越低越好) 最大内存占用(kbytes) 环比Rust(越低越好)
Java OpenJDK 17.0.2 2.75 -69.7% 38056 +1932.9%
Java GraalVM 21.0.2+13.1 4.37 -51.8% 6980 +272.9%
Python 3.11.2 112.13 +1136.3% 9364 +400.2%
Go 1.21.6 4.88 -46.2% 3396 +81.4%
C gcc 12.2.0 8.36 -7.8% 1444 -22.9%
Rust 1.76.0 9.07 0 1872 0
Lua Lua 5.4 74.22 +718.3% 2668 +42.5%
Lua LuaJIT 2.1.0-beta3 17.09 +88.42% 2296 +22.6%
Nodejs v20.10.0 9.18 +1.2% 45248 +2317.1%

实验代码

Java

java 复制代码
public class Fibonacci {
    public static int fib(int n) {
        if (n <= 2) {
            return 1;
        }
        return fib(n - 2) + fib(n - 1);
    }

    public static void main(String[] args) {
        if (args.length < 1) {
            System.out.println("Usage: java Fibonacci NUMBER");
            return;
        }
        
        try {
            int num = Integer.parseInt(args[0]);
            System.out.println(String.format("The %dth Fibonacci number is %d", num, fib(num)));
        } catch (NumberFormatException e) {
            System.out.println("Invalid number provided.");
        }
    }
}

Python

python 复制代码
import sys

def fib(n: int) -> int:
    if n <= 2:
        return 1
    return fib(n-2) + fib(n-1)

def main():
    if len(sys.argv) < 2:
        print("Usage: python fib-python.py NUMBER")
        return
    print(f"The {sys.argv[1]}th Fibonacci number is {fib(int(sys.argv[1]))}")

if __name__ == "__main__":
    main()

Go

go 复制代码
package main

import (
    "fmt"
    "os"
    "strconv"
)

func fib(n uint) uint {
    switch n {
        case 0:
        	return 0
        case 1:
        	return 1
        case 2:
        	return 1
        default:
        	return (fib(n-2) + fib(n-1))
    }
}

func main() {
    args := os.Args
    for i := 1; i < len(args); i++ {
        v1, err := strconv.Atoi(args[i])
        if err != nil {
            panic(err)
        }
        var arg uint = uint(v1)
        fmt.Printf("The %dth Fibonacci number is %d\n", arg, fib(arg))
    }
}

Rust

rust 复制代码
use std::str::FromStr;
use std::env;


fn fib(n: u64) -> u64 {
    match n {
        0 => 0,
        1 => 1,
        2 => 1,
        _ => fib(n - 1) + fib(n - 2),
    }
}

fn main() {
    let mut args = Vec::new();
    for arg in env::args().skip(1) {
        args.push(u64::from_str(&arg).expect("error parsing argument"));
    }

    if args.len() == 0 {
        eprintln!("Usage: fib <number>");
        std::process::exit(1);
    }

    println!("The {}th Fibonacci number is {}", args[0], fib(args[0]));
}

JavaScript

javascript 复制代码
function fib(n) {
 if (n <= 2) {
   return 1;
 }
 return fib(n - 2) + fib(n - 1);
}

function main(n) {
 console.log(`The ${n}th Fibonacci number is ${fib(n)}`);
}

main(45);

C

c 复制代码
#include <stdio.h>
#include <stdlib.h>

long long fibonacci(int n);

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("使用方法: ./fib-c NUMBER\n");
        return 1;
    }
    
    int n = atoi(argv[1]);
    printf("The %dth Fibonacci number is %d\n", n, fibonacci(n));

    return 0;
}

long long fibonacci(int n) {
    if (n <= 1) return n;
    else return fibonacci(n - 1) + fibonacci(n - 2);
}

Lua

lua 复制代码
function fib(n)
    if n <= 2 then
        return 1
    else
        return fib(n - 2) + fib(n - 1)
    end
end

function main()
    if #arg == 0 then
        print("Usage: lua fib-lua.lua NUMBER")
        return
    end
    
    local num = tonumber(arg[1])
    if num then
        print(string.format("The %dth Fibonacci number is %d", num, fib(num)))
    else
        print("Invalid number provided.")
    end
end

main()
相关推荐
爱勇宝16 小时前
深扒 Anthropic 1680 位工程师简历:应届生几乎没机会,AI 公司最缺的不是博士
前端·后端·程序员
AskHarries16 小时前
工具失败时怎么办:重试、回滚、人工确认和风险提示
后端·程序员
苏三说技术18 小时前
Claude Code从失控到起飞,只用了这些技巧
后端
长栎19 小时前
写 for 循环写了十年,你却从没用过迭代器模式最狠的那一面
后端
LiaCode19 小时前
Redis 在生产项目的使用
前端·后端
用户5598224812219 小时前
Docker Compose Down 导致容器数据误删——ext4 日志恢复全记录
后端
LiaCode19 小时前
一天学完 redis 的爽翻版核心知识总结
前端·后端
大刚测试开发实战19 小时前
如何内网穿透访问本地私有化部署的TestHub
前端·后端·github
xiaodaoluanzha19 小时前
迄今為止,最簡單的編程語言 Nolang
前端·后端
Csvn19 小时前
Docker 容器管理入门 — 从镜像到容器编排
后端