为什么很多程序员不用switch,而是大量的if else?

说来也是巧最近在看 Dubbo 源码,然后发现了一处很奇怪的代码,刚好和这个 switch 和 if else 有关!

让我们来看一下这段代码,它属于 ChannelEventRunnable,这个 runnable 是 Dubbo IO 线程创建,将此任务扔到业务线程池中处理。

看到没,把 state == ChannelState.RECEIVED 拎出来独立一个 if,而其他的 state 还是放在 switch 里面判断。

我当时脑子里就来回扫描,想想这个到底有什么花头,奈何知识浅薄一脸懵逼。

于是就开始了一波探险之旅!

原来是 CPU 分支预测

遇到问题当然是问搜索引擎了,一般而言我会同时搜索各大引擎,咱这也不说谁比谁好,反正有些时候度娘还是不错的,比如这次搜索度娘给的结果比较靠前,google 较靠后。

一般搜索东西我都喜欢先在官网上搜,找不到了再放开搜,所以先这么搜 site:xxx.com key。

你看这就有了,完美啊!

我们先来看看官网的这篇博客怎么说的,然后再详细地分析一波。

Dubbo 官网的博客

现代 CPU 都支持分支预测 (branch prediction) 和指令流水线 (instruction pipeline),这两个结合可以极大提高 CPU 效率。对于像简单的 if 跳转,CPU 是可以比较好地做分支预测的。但是对于 switch 跳转,CPU 则没有太多的办法。 switch 本质上是根据索引,从地址数组里取地址再跳转。

也就是说 if 是跳转指令,如果是简单的跳转指令的话 CPU 可以利用分支预测来预执行指令,而 switch 是要先根据值去一个类似数组结构找到对应的地址,然后再进行跳转,这样的话 CPU 预测就帮不上忙了。

然后又因为一个 channel 建立了之后,超过99.9%情况它的 state 都是 ChannelState.RECEIVED,因此就把这个状态给挑出来,这样就能利用 CPU 分支预测机制来提高代码的执行效率。

并且还给出了 Benchmark 的代码,就是通过随机生成 100W 个 state,并且 99.99% 是 ChannelState.RECEIVED,然后按照以下两种方式来比一比(这 benchSwitch 官网的例子名字打错了,我一开始没发现后来校对文章才发现)。

虽然博客也给出了它的对比结果,但是我还是本地来跑一下看看结果如何,其实 JMH 不推荐在 ide 里面跑,但是我懒,直接 idea 里面跑了。

从结果来看确实通过 if 独立出来代码的执行效率更高(注意这里测的是吞吐),博客还提出了这种技巧可以放在性能要求严格的地方ÿ

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第1篇 理解程序设计 第1章 基础知识 1.1 什么是编程 1.1.1 计算机如何工作 1.1.2 内存中的程序是哪里来的 1.1.3 可执行文件的制作 1.1.4 C语言的演化 1.2 怎样用C语言编程 1.2.1 学习C语言编程都需要什么 1.2.2 最简单的C语言程序的基本结构 1.2.3 Dev C++ 1.3 printf()函数初步 1.3.1 简单的一般用法 1.3.2 特殊的字符 1.4 C语言的“字母”和“单词” 1.4.1 C语言的字母 1.4 12C语言的“词” 小结 概念与术语 风格与习惯 常见错误 牛角尖 练习与自测 第2章 数据类型 2.1 什么是数据类型 2.1.1 “三个世界”理论 2.1.2 问题世界:“万物皆数” 2.1.3 代码世界:书写规则及含义 2.1.4 机器世界里的“机器数” 2.1.5 输出问题 2.1.6 计算2的1到10次幂 2.1.7 代码质量的改进 2.2 让程序记住计算结果——变量 2.2.1 计算机的记忆功能 2.2.2 在代码中实现“记忆 2.3 int类型——总结与补充 2.3.1 计算机表示负整数的几种方法 2.3.2 计算机码制和C语言的关系 2.3.3 暂时不必关心的一些细节 2.3.4 int类型值的范围 2.3.5 int类型常量在代码中的其他写法 2.3.6 Dev C++中int类型的机器数 2.4 对数据类型的进一步讨论 2.4.1 int数据类型的运算 2.4.2 数学公式与数据类型 2.4.3 数据类型——代码与编译器的约定 2.5 莫名其妙的“整型 2.5.1 unsignedint类型 2.5.2 long、short关键字描述的整数类型 2.5.3 没有常量的char类型 2.5.4 其他 2.6 浮点类型 2.6.1 double类型常量的代码书写规则 2.6.2 浮点类型数据存储模型 2.6.3 浮点类型的一些特性 2.6.4 浮点类型的运算 2.6.5 浮点类型的输出及其他 2.7 数据类型与算法 2.7.1 错误的数据类型 217.2 所谓算法 2.7.3 一个技巧 2.7.4 更高效率的写法 2.8 算法的特性 小结 概念与术语 风格与习惯 常见错误 牛角尖 练习与自测 第3章 运算符、表达式及语句 3.1 C的“动词”及“动词”的“宾语” 3.2 表达式——C语言的“词组 3.2.1 初等表达式 3.2.2 被误解的“() 3.2.3 带运算符的表达式 3.2.4 不像表达式的表达式 3.2.5 表达式:专业与副业 3.2.6 赋值运算符左侧的标识符称为左值 3.2.7 函数调用是表达式不是语句 3.3 谁是谁的谁 3.3.1 流行的谬误:优先级决定运算次序 3.3.2 “左结合性”是运算对象先与左面的运算符相结合吗 3.3.3 运算符、表达式小结 3.4 右值的类型转换 3.4.1 明确写出的显式转换——cast运算 3.4.2 cast运算的规则 3.4.3 赋值中的转换 3.4.4 1+1.0=? 3.4.5 算术转换:早已废弃的规则和依然有效的规则 3.5 语句的概念 3.5.1 关于语句的闲话 3.5.2 空语句有两种 3.5.3 表达式语句 3.5.4 顺序结构 3.5.5 复合语句 3.6 例题 3.6.1 简单的类型转换 3.6.2 最基础的算法——交换变量的值 3.6.3 编程不是列公式 3.7 算法和数据结构初窥 3.8 在程序运行时提供数据 小结 概念与术语 风格与习惯 常见错误 牛角尖 练习与自测 第4章 选择语句 4.1 关系运算 4.1.1 “<”的数学含义及代码含义 4.1.2 4种关系运算符 4.1.3 常见误区及与常识不符的结果 4.2 if语句 4.2.1 语法格式及含义 4.2.2 例题 4.2.3 ()内的表达式 4.2.4 ()后面的语句 4.3 判等运算 4.4 表达复杂的条件 4.5 if else语句 4.6 鸡肋——Bool类型(C99) 4.7 判断三角形种类 4.8 显得很有学问的运算符 4.9 大师如是说goto 4.10 给程序更多选项——Switch语句 4.10.1 switch语句的一种应用形式 4.10.2 switch语句中的break语句 4.11 程序开发的过程 小结 概念与术语 风格与习惯 常见错误 牛角尖 练习与自测 第5章 从循环到穷举 5.1 造句:当就 5.1.1 语法要素 5.1.2 猴子吃桃问题更简洁的写法 …… 第2篇 结构化程序设计与简单的数据结构 第6章 最复杂的去处符——“()” 第7章 作为类型说明符和去处符的“[]” 第8章 结构体、共用体与位运算 第9章 指针 第10章 字符串、字符数组及指向字符的指针 第3篇 复杂的数据结构、算法及其他话题 第11章 复杂的数据类型与算法 第12章 程序的输入与输出 第13章 程序组织与编译预处理 第14章 标准库简介 附录 参考文献

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值