开发工具选对---性能提升万倍也有机会
最近有计划写一个程序对数据库做一个压测,这里涉及到大量的数据生成工作,于是我想着测下一下不同语言 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(number) FROM numbers_mt(10000000000) ;
+----------------------+
| sum(number) |
+----------------------+
| 13106511847580896768 |
+----------------------+
1 row in set (0.37 sec)
#10亿
mysql> SELECT sum(number) FROM numbers_mt(1000000000) ;
+--------------------+
| sum(number) |
+--------------------+
| 499999999500000000 |
+--------------------+
1 row in set (0.04 sec)
#1亿
mysql> SELECT sum(number) FROM numbers_mt(100000000) ;
+------------------+
| sum(number) |
+------------------+
| 4999999950000000 |
+------------------+
1 row in set (0.00 sec)
Datafuse 为什么这么快,经过了一个SQL的解析速度也没降,这是为什么?通过 Explain pipeline 结果对比:
mysql> explain pipeline SELECT sum(number) FROM 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 公开课了,也需要尝试一下了。