Jasper Ji

博观而约取,厚积而薄发。

一次语言间的性能对比

2021-04-02 05:53

最近突然间想对比下几个语言间的性能,日常使用的是Java,但平时也会用Python,JavaScript,C,Go之类的语言。Java写项目真的也没有什么问题,成熟的生态圈,遇到问题很容易解决。不过写点小东西的时候,还是觉得Python比较好用,不过就是速度慢了点,很早之前写过一个游戏的移植,脚本语言重构真的是个大问题。Go在上家公司的时候也使用过,不过也都是小的测试了,并没有拿来写过完整点的项目。不过语言的简洁性和C的这种传承,还是很有好感,不过在当时重写公司系统时,最后还是用了Java,主要是大部分的对接都有Java的实现。C一直是我研究的语言,虽然实际写东西不多,但是却看了很多相关的书籍,另外最近研究操作系统和学习汇编语言后,对C的认识又有了新的认识,C的厉害地方就是简洁的设计,性能强大,他的诞生就是为写Unix,缺点是实用性不强,另外内存管理,实际上成熟的项目在内存管理上都有自己的框架,所以对于C/C++之类的语言光学习了语言其实并不能做什么,离应用的距离还很远。

回到正题,我实际上想把Go作为业务研究的语言了,虽然脑海中一直印象他的性能不错,但具体表现怎样能,好像我也并不清楚。最后选择对比的语言是Java、Go、C、Rust四个语言,程序都是运行100000000循环,循环内执行冒泡排序。

Java 代码

public class Test {
	public static void main(String[] args) {
		// 记录开始时间
		long start = System.nanoTime();
		int num = 100000000;
		for (int i = 0; i < num; i++) {
			int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
			bubbleSort(arr);
		}
		// 打印耗时时间
		System.out.println(System.nanoTime() - start);
	}

	// 排序
	public static void bubbleSort(int[] arr) {
		int length = arr.length - 1;
		for (int i = 0; i < length; i++) {
			for (int j = 0; j < length - i; j++) {
				if (arr[j] < arr[j + 1]) {
					int temp = arr[j];
					arr[j] = arr[j + 1];
					arr[j + 1] = temp;
				}
			}
		}
	}
}

运行时间

3052757121

Go 代码

package main

import (
	"fmt"
	"time"
)

func main() {
	start := time.Now().UnixNano()
	const NUM int = 100000000
	for i := 0; i < NUM; i++ {
		arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
		bubbleSort(arr)
	}
	// 打印消耗时间
	fmt.Println(time.Now().UnixNano() - start)
}

// 排序
func bubbleSort(arr []int) {
	for j := 0; j < len(arr)-1; j++ {
		for k := 0; k < len(arr)-1-j; k++ {
			if arr[k] < arr[k+1] {
				arr[k], arr[k+1] = arr[k+1], arr[k]
			}
		}
	}
}

运行时间

3804215000 # 纳秒

C 代码

#include <stdlib.h>
#include <stdio.h>
#include <time.h>

#include <sys/time.h>
#include <unistd.h>

void bubbleSort(int* array, int length) {
    for (int i = 0; i < length -1 ; i++) {
        for(int j=0; j < length -1 -i; j++) {
            if(array[j] < array[j+1]) {
                int temp = array[j];
                array[j] = array[j + 1];
                array[j + 1] = temp;
            }
        }
    }
}

int main() {
    struct timeval t_val;
    gettimeofday(&t_val, NULL);
    printf("start, now, sec=%ld m_sec=%d \n", t_val.tv_sec, t_val.tv_usec);

    for (int i = 0; i < 100000000; i++) {
        int array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
        int length = sizeof(array)/sizeof(int);
        bubbleSort(array, length);
    }
    
    struct timeval t_val_end;
    gettimeofday(&t_val_end, NULL);

    struct timeval t_result;
    timersub(&t_val_end, &t_val, &t_result);

    double consume = t_result.tv_sec + (1.0 * t_result.tv_usec)/1000000;
    printf("end.elapsed time= %fs \n", consume);

    return 0;
}

运行时间

start, now, sec=1617327646 m_sec=57219 
end.elapsed time= 1.746955s 

Rust代码

use std::time::Instant;

pub fn bubble_sort<T: Ord>(arr: &mut [T]) {
    for i in 0..arr.len() {
        for j in 0..arr.len() - 1 - i {
            if arr[j] < arr[j + 1] {
                arr.swap(j, j + 1);
            }
        }
    }
}

fn main() {
    let start = Instant::now();

    for _i in 0..100000000 {
        let mut numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
        bubble_sort(&mut numbers);
    }

    println!("time cost: {:?} ms", start.elapsed().as_millis()); // ms
    println!("time cost: {:?} us", start.elapsed().as_micros()); // us
    println!("time cost: {:?} ns", start.elapsed().as_nanos()); // us
}

运行时间

time cost: 1763 ms
time cost: 1763298 us
time cost: 1763300912 ns

Swift 代码

import Foundation

func bubbleSort<T: Comparable>(with array: inout [T]) -> [T] {
    for i in 1..<array.count {
        for j in 0..<array.count-i where array[j] < array[j+1] {
            array.swapAt(j, j+1)
        }
    }
    return array
}


let start = DispatchTime.now()
for _ in 0..<10000000 {
    var intArray = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    bubbleSort(with: &intArray)
}
let end = DispatchTime.now()
let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds
let timeInterval = Double(nanoTime) / 1_000_000_000
print("Time: \(timeInterval) seconds")

运行时间

Time: 2.391004642 seconds

总结

最早我对比的是Java和C,因为C是在Debug模式下跑的,反而比Java的慢,这个让我无法接受,不应该啊,后来让朋友跑了下,说应该用Release模式,这才反应过来,Release + O3优化后速度一下子就上来了,明显比Java快。Rust和C基本上都有Debug和Release的区分,现在看来Rust和C是一个级别,Java和Go是另一个档次上的同一级别。现在的Java运行默认是解释和编译的混合模式,所以速度上自然也不错,和Java对标的Go数据上来看略微差点,不过考虑到Java的运行时开销,Go其实还是很有诱惑的,后续有可能赶超Java。

企业开发的话用Java依旧最好的选择,个人创业项目的话用Go其实不错。对于Rust和C,不好选择C语法上比较简洁,很多语言特性跟底层更靠近,需要一定底层知识,写操作系统的话,C依旧是首选。Rust呢?个人觉得语法上比较复杂,混杂的语言特性过于多了,既想兼容底层有想高级别的抽象上靠近现代语言,不过这样的情况下依然保持高性能,还是很令人惊讶的。这个不好选,但Rust更充满诱惑,但复杂的语法让人畏惧。

Go语言虽然有C的传承,不过在类型定义上使用后缀的形式跟C语言正好相反,有时还是比较抵触的,另外因为GC的缘故,性能毕竟还是处于应用语言的级别。Rust太复杂,Swift没有用GC是引用计数,速度上比Rust差些,语法上其实跟Rust也差不多,不过作为一个曾经的iOS开发者,虽然Swift在一段时间大家都非常看好,不过最近几年过去了,其实Swift更多还是Oc的一种替代品,并没有突破苹果的范围,服务端也没有掀起什么风浪,Swift终究是Oc的替代品,也正因为此,他也跟苹果生态深度的绑定,成了某种无法脱颖而出的枷锁。

我心中语言其实就是C语言的改良版,内存管理上希望在语言层面上解决,比如用引用计数。语法上不要太加很多的特性,有Go的那样就可以了,但不是Go。不过目前来看是没有的,要不就是自己写一个,也不是没有可能。但实在要是也没有的Go和Rust总还是不错的选择。

Comments