网易首页 > 网易号 > 正文 申请入驻

为什么新一代的Rust、Go等编程语言都如此讨厌if-else、Switch结构

0
分享至

作者 | 马超

出品 | CSDN(ID:CSDNnews)

今天我们还是继续来聊高并发的话题,我们知道Swich及if-else分支是一个非常有用的语法,这是一个可以回溯到上世纪的Pascal、C等经典语言的分支结构,主要的作用就是判断变量的取值并将程序代码送入不同的分支,这种设计在当时的环境下非常的精妙,但是在当前最新的CPU环境下,却会带来很多意想不到的坑。

Swich的坑,环境一变效率就差远了

由于Rust并没有Switch了,因此以下代码就暂用Go语言来演示了。我们先来看以下这段代码:


package mainimport ("fmt""math/rand"//"sync""time"func main() {now := time.Now().UnixNano()count := []int{0, 0, 0, 0, 0, 0}for i := 0; i < 100000; i++ {random := rand.Intn(100)switch random {case 1:count[1]++case 2:count[2]++case 3:count[3]++case 4:count[4]++case 5:count[5]++default:count[0]++fmt.Println(time.Now().UnixNano() - now)fmt.Println(count)

它的运行结果如下:


[root@ecs-a4d3 hello_world]# time ./test1288084[99507 86 108 106 96 97]real 0m0.004suser 0m0.003ssys 0m0.001s

我们再稍微把Random的范围由100改为5,


package mainimport ("fmt""math/rand"//"sync"time"func main() {now := time.Now().UnixNano()count := []int{0, 0, 0, 0, 0, 0}for i := 0; i < 100000; i++ {random := rand.Intn(1000)switch random {case 1:count[1]++case 2count[2]++case 3:count[3]++case 4:count[4]++case 5:count[5]++default:count[0]++fmt.Println(time.Now().UnixNano() - now)fmt.Println(count)

上述的代码运行结果如下:


[root@ecs-a4d3 hello_world]# time ./test14365712[20184 20357 19922 19897 19640 0]real 0m0.006suser 0m0.004ssys 0m0.002s

可以看到这两段程序的运行时间相差了30%多,这个结果真的是细思极恐,因为也就是实际代码执行逻辑完全没有任何变化 ,只是变量取值范围有所调整,就会使程序的运行效率大为不同,也就是说当系统遭遇到某些极端情况时,同样的程序所需要的运行时间却有天壤之别,要解释这个问题要从指令流水线说起。

CPU流水线与指令预测

我们知道CPU的每个动作都需要用晶体震荡而触发,以加法ADD指令为例,想完成这个执行指令需要取指、译码、取操作数、执行以及取操作结果等若干步骤,而每个步骤都需要一次晶体震荡才能推进,因此在流水线技术出现之前执行一条指令至少需要5到6次晶体震荡周期才能完成。

为了缩短指令执行的晶体震荡周期,芯片设计人员参考了工厂流水线机制的提出了指令流水线的想法,由于取指、译码这些模块其实在芯片内部都是独立的,完成可以在同一时刻并发执行,那么只要将多条指令的不同步骤放在同一时刻执行,比如指令1取指,指令2译码,指令3取操作数等等,就可以大幅提高CPU执行效率:

以上图流水线为例 ,在T5时刻之前指令流水线以每周期一条的速度不断建立,在T5时代以后每个震荡周期,都可以有一条指令取结果,平均每条指令就只需要一个震荡周期就可以完成。这种流水线设计也就大幅提升了CPU的运算速度。

但是CPU流水线高度依赖指指令预测技术,假如在流水线上指令5本是不该执行的,但却在T6时刻已经拿到指令1的结果时才发现这个预测失败,那么指令5在流水线上将会化为无效的气泡,如果指令6到8全部和指令5有强关联而一并失效的话,那么整个流水线都需要重新建立。

会是编译器隐式likely优化的原因吗?因此为了适应指令流水线的机制,不少对于性能要求极高的程序中,都会将不太会执行到的分支加上unlikely修饰符,从而引导CPU不要预测这个分支上的代码以提升效率,不过这个机制在本例中不太适用,会objdump –S查看原始代码也并没有发现,default前面加上了likely优化。


default:count[0]++49a0f9: 48 8b 44 24 60 mov 0x60(%rsp),%rax49a0fe: 48 ff 00 incq (%rax)49a101: eb 84 jmp 49a087 0xa7>case 3:49a103: 48 83 f8 03 cmp $0x3,%rax49a107: 75 0e jne 49a117 0x137>count[3]++49a109: 48 8b 44 24 60 mov 0x60(%rsp),%rax49a10e: 48 ff 40 18 incq 0x18(%rax)49a112: e9 70 ff ff ff jmpq 49a087 0xa7>case 4:49a117: 48 83 f8 04 cmp $0x4,%rax49a11b: 75 0e jne 49a12b 0x14b>

所以可以看出这个效率差完全是CPU指令预测造成的。也就是说CPU自带的机制就是会对于执行概比较高的分支给出更多的预测倾斜。

Rust的ifelse也是一样的坑

当然我们说Switch不好也就不是说if else就避免了这个问题,根据指令流水线的原理,if else在处理分支时情况也一样,因此Rust也不太推荐if else的写法,以Rust为例如下:


use rand::Rng;fn main() {let mut rng = rand::thread_rng();let mut arr = [0, 0, 0, 0, 0,0];//println!("Integer: {}", arr[random]);for i in 0..1000000 {let mut random =rng.gen_range(0,5);if random==0{arr[0]=arr[0]+1;else if random==1{arr[1]=arr[1]+1;else if random==2{arr[2]=arr[2]+1;else if random==3{arr[3]=arr[3]+1;else if random==4{arr[4]=arr[4]+1;else{arr[5]=arr[5]+1;println!("{},{},{},{},{},{}", arr[0], arr[1], arr[2], arr[3], arr[4], arr[5]);

将随机数的取值范围调整一下


let mut random =rng.gen_range(0,100);

可以观察到这两段程序运行的时间也要相差近40%,这样的结果也深刻说明了一个问题,这个例子其实模拟的是一个极端状态,也就是某一个变量取值突然从0-100变成了0-5那么程序的运行效率可能就会有极大的改变,这个推论就是一旦系统运行在某一个极端状态,比如错误占比过高或者其它极端场景,那么正常情况下的压力测试结果也许就完全不能说明问题了。

哈希表会是个好的选择吗?

我们上文也介绍过哈希表也就是字典,可以快速将键值key转化为值value,从某种程度上讲可以替换switch的作用,按照第一段代码的逻辑,用哈希表重写的方案如下:


package mainimport ("fmt""math/rand""time"func main() {now := time.Now().UnixNano()//count := []int{0, 0, 0, 0, 0, 0}count := map[int]int{0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0}for i := 0; i < 100000; i++ {random := rand.Intn(5)count[random]++fmt.Println(time.Now().UnixNano() - now)fmt.Println(count)

不过这段代码的运行结果如下:


[root@ecs-a4d3 hello_world]# time ./test25864780map[0:20184 1:20357 2:19922 3:19897 4:19640 5:0]real 0m0.008suser 0m0.007ssys 0m0.001s

虽然这个版本性能比较稳定,但却比之前的Switch方案最慢的情况还慢60%,原因也很简单我们之前介绍过哈希表也叫散列表,它的各个元素在内存中的而已并不连续,因此高速缓存对这种数据结构的加速作用有限。

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
“拜登身体机能衰退”?美官方回应

“拜登身体机能衰退”?美官方回应

新京报
2024-06-19 08:40:36
上海这一夜,袒胸露乳的姚晨和金晨,败给了“全裹”出镜的俞飞鸿

上海这一夜,袒胸露乳的姚晨和金晨,败给了“全裹”出镜的俞飞鸿

秋姐居
2024-06-17 12:17:39
西方紧盯普京亚洲行

西方紧盯普京亚洲行

环球时报国际
2024-06-19 08:06:08
认怂了!余琦公开道歉,秘书长工作恐难保,还有五个问题没解决

认怂了!余琦公开道歉,秘书长工作恐难保,还有五个问题没解决

嘿哥哥科技
2024-06-19 11:33:37
看完甘肃天水这件事,才真的让我后怕!

看完甘肃天水这件事,才真的让我后怕!

顾礼先生
2024-06-18 16:11:00
国家社科基金项目成果:男人阴茎越短,智商越高

国家社科基金项目成果:男人阴茎越短,智商越高

必记本
2024-06-19 01:09:57
河南女学霸2次高考查分,从627分变成335分,到底怎么回事?

河南女学霸2次高考查分,从627分变成335分,到底怎么回事?

莉雅细细谈
2024-06-17 20:44:48
曝50岁阿姨呼吁“老年人不要再涨工资”:年轻人比老人还可怜,国家应重视他们!

曝50岁阿姨呼吁“老年人不要再涨工资”:年轻人比老人还可怜,国家应重视他们!

可达鸭面面观
2024-06-18 20:24:41
马伊琍女儿庆祝父亲节,与爸爸文章罕见同框,40岁文章已满头白发

马伊琍女儿庆祝父亲节,与爸爸文章罕见同框,40岁文章已满头白发

橘子大娱社
2024-06-18 19:20:02
妻子2次和光棍偷情,2017年光棍嫌她有丈夫,觉得被玩弄了杀死她

妻子2次和光棍偷情,2017年光棍嫌她有丈夫,觉得被玩弄了杀死她

汉史趣闻
2024-06-19 11:12:42
曹县翰林府不雅视频曝光后续:女销售身份被扒,长相身材很一般

曹县翰林府不雅视频曝光后续:女销售身份被扒,长相身材很一般

180°视角
2024-06-18 12:55:47
印度恒河崩溃报告:被印度彻底榨干 | 地球知识局

印度恒河崩溃报告:被印度彻底榨干 | 地球知识局

地球知识局
2024-06-19 11:05:30
两教授在世界顶刊发论文均获百万重奖,是重人才还是“唯论文”?当事人回应

两教授在世界顶刊发论文均获百万重奖,是重人才还是“唯论文”?当事人回应

极目新闻
2024-06-19 11:20:27
葡萄牙绝杀后C罗朝捷克门将挥拳庆祝,网友评论:没必要这么做

葡萄牙绝杀后C罗朝捷克门将挥拳庆祝,网友评论:没必要这么做

直播吧
2024-06-19 15:04:03
佛山出现反常现象,成千上万人分批离开,真是令人感到匪夷所思!

佛山出现反常现象,成千上万人分批离开,真是令人感到匪夷所思!

小怪吃美食
2024-06-19 07:30:30
一极端泛蒙主义歌手要在中国开演唱会,曾在屠宰场拍MV且歌词过激

一极端泛蒙主义歌手要在中国开演唱会,曾在屠宰场拍MV且歌词过激

不掉线电波
2024-06-19 13:24:24
华春莹推介!张家界这一幕爆火,外国人都被硬控住了

华春莹推介!张家界这一幕爆火,外国人都被硬控住了

潇湘晨报
2024-06-18 22:53:07
台湾新冠重症及死亡病例大幅增加

台湾新冠重症及死亡病例大幅增加

界面新闻
2024-06-19 10:40:22
女人“私处”的小秘密,别脸红(内含高清图)

女人“私处”的小秘密,别脸红(内含高清图)

喜马拉雅主播暮霭
2024-06-19 12:06:18
认怂!女司机承认自己是秘书长余琦并公开致歉,照稿念被批没诚意

认怂!女司机承认自己是秘书长余琦并公开致歉,照稿念被批没诚意

六毛朵朵
2024-06-19 10:02:47
2024-06-19 15:50:44
CSDN
CSDN
成就一亿技术人
24733文章数 241821关注度
往期回顾 全部

科技要闻

英伟达超越苹果、微软登顶全球新股王

头条要闻

司长张莹"空降"地方 履新江西省政府党组成员

头条要闻

司长张莹"空降"地方 履新江西省政府党组成员

体育要闻

欧洲杯最大的混子,非他莫属

娱乐要闻

黄一鸣“杀疯了” 直播间卖大葱养孩子

财经要闻

证监会:优化科创板公司股债融资制度

汽车要闻

双肾格栅变化大/内饰焕新 新一代宝马X3官图发布

态度原创

健康
艺术
旅游
教育
军事航空

晚餐不吃or吃七分饱,哪种更减肥?

艺术要闻

穿越时空的艺术:《马可·波罗》AI沉浸影片探索人类文明

旅游要闻

遭遇极端高温天气导致希腊多名游客死亡

教育要闻

家长用水果教育孩子事实与观点,告诉他别太在意别人的观点

军事要闻

美国务卿:除部分炸弹 其他对以武器援助均在正常运输

无障碍浏览 进入关怀版