vlambda博客
学习文章列表

开发工具选对---性能提升万倍也有机会

最近有计划写一个程序对数据库做一个压测,这里涉及到大量的数据生成工作,于是我想着测下一下不同语言 for 的执行情况。

首先强调一下,这个不是对比语言的,就是一个个人的对比测试。

需求:对1 - 1000000000( 10 亿)求 sum

pyfor.py

if __name__ == '__main__':
    num = 1000000000
    sum = 0
    for i in range(0,num):
        sum += i
    print("sum =%d" % sum)

gfor.go 

go build gofor.go

package main
import "fmt"
func main() {
       var num uint64 = 1000000000
       var sum uint64 = 0
       var i uint64 = 0
        for i = 0;  i < num; i++ {
                sum += i
        }
        fmt.Println("sum = ", sum)
}

cfor.c 

编译使用 -O3 : gcc -O3 -ocfor cfor.c

#include <stdio.h>
int main(){
    unsigned  long sum = 0;
    unsigned  long num = 1000000000;
    for (unsigned  long i = 0 ; i< num; i++  ){
        sum += i;
    }
    printf("sum = %lu",sum);
}

rfor.rs 

编译使用release模式: cargo build --release

fn main() {
    let num :u64 = 1000000000;
    let mut sum :u64  = 0;
    for i in 1..num {
        sum += i;
    }
    println!("sum = {}", sum);
}


执行情况:

command 第一次 第二次 第三次 平均时间
time python ./pyfor.py 1m34.436s 1m32.193s 1m34.913s ~1m33.847s
time ./gfor 0m0.263s 0m0.259s 0m0.266s ~0.262s
time ./cfor 0m0.004s 0m0.004s 0m0.005s ~0.004s
time ./rfor 0m0.004s 0m0.005s 0m0.004s ~0.004s

从运行结果比较,如果以 rust 为基础:

rust : c : golang : python = 1 : 1 : 65 : 23738

看着非常夸张。在100000000( 1 亿)在这个规模计算,群友提供的数据:

rust : c : go : pypy : cpython = 1 : 1 : 6 : 36 :1000

从上面两个测试可以看出来,在小规数据下python,golang看着还不错。但到了大规模就差别比较大了。

在这个测试中发现一个怪异的问题,c, rust多次执行时间都是0.004s,估计是有缓存了,在测试意义不大了。

在这个测试中:从1亿到 10 亿运算sum时就遇到类型溢出~~~,100亿 python没跑不出来的无耐。 同时对于 C 程序, Rus t编译上不加优化和加优化的区别也比较大。 这几个测试走下来,对我这个不怎么写程序的人感觉对于理解编译优化, 数据溢出都挺有帮助的。

数据库中跑这个需求效果怎么样? 

从事数据库的习惯,想到这个需求用 SQL 也比较简单,适合在数据库里跑。于是使用我们的项目:Datafuse (https://github.com/datafuselabs/datafuse) 跑了一下:

#100亿
mysql>   SELECT sum(numberFROM numbers_mt(10000000000) ;
+----------------------+
| sum(number)          |
+----------------------+
| 13106511847580896768 |
+----------------------+
1 row in set (0.37 sec)

#10亿
mysql> SELECT sum(numberFROM numbers_mt(1000000000) ;
+--------------------+
| sum(number)        |
+--------------------+
| 499999999500000000 |
+--------------------+
1 row in set (0.04 sec)

#1亿
mysql> SELECT sum(numberFROM numbers_mt(100000000) ;
+------------------+
| sum(number)      |
+------------------+
| 4999999950000000 |
+------------------+
1 row in set (0.00 sec)

Datafuse 为什么这么快,经过了一个SQL的解析速度也没降,这是为什么?通过 Explain pipeline 结果对比:

mysql> explain pipeline SELECT sum(numberFROM numbers_mt(10000000000) ;
+--------------------------------------------------------------------------------------------+
explain                                                                                    |
+--------------------------------------------------------------------------------------------+
| ProjectionTransform × 1 processor                                                          |
|   AggregatorFinalTransform × 1 processor                                                   |
|     Merge (AggregatorPartialTransform × 16 processors) to (AggregatorFinalTransform × 1)   |
|       AggregatorPartialTransform × 16 processors                                           |
|         SourceTransform × 16 processors                                                    |
+--------------------------------------------------------------------------------------------+
5 rows in set (0.00 sec)


从上面的执行计划显示 Datafuse 把一个计算成按系统的 CPU core进行并行运算,最终走了一个合并执行。把原来的串行变成了并行。

以上成绩只是一个参考,如果有兴趣可以在 个人的环境里 | 数据库里 跑一下,再做对比。如果你有兴趣,也可以跑一下,把结果放在留言中。

BTW: 我觉写数据库的压测程序应该尝试用 Rust 实现一下。已经参加一个月的 Rust 公开课了,也需要尝试一下了。