最近在看一些八股文,有涉及到Java JVM的,也有安卓的底层原理,例如app启动流程,很多在实际业务中没有接触过,也或许是自己没有意识到,所以更多是死记硬背,所以我其实并不推崇八股文。
在实际工作中,完成各种需求其实都有很多种写法,哪怕是最简单的判断也有if-else和switch(when)可以选择,从来也没想过也可能会带来深层次的影响。

单独的if处理
最近在写的项目中,突然发现自己的一种写法,起因是因为不希望改变原有代码结构,以及将最常见的情况单独列出、单独处理,例如
kotlin
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
mFeedbackViewModel.uiStateFlow.collect { state ->
if (state == FeedBackState.SubmitSuccess) {
return@collect
}
when (state) {
is FeedBackState.Init -> {
}
is FeedBackState.SubmitLoading -> {
}
is FeedBackState.SubmitError -> {
}
else -> {}
}
}
}
}
只是一种个人习惯,更多时候其实会把片段逻辑写入when(switch)中,这样也代表了代码可读性
同时,在我学习的时候就知道switch在编译成字节码时会进行优化,通过一次计算直接定位到目标分支,时间复杂度是O(1),而if-else需要逐个判断,时间复杂度是O(n)
但是今天看到一篇文章讲到一个很有意思的点,if-else在一部分情况下其实性能比switch要好很多。
CPU 分支预测与指令流水线的关键作用
现代CPU的两大性能支柱
-
分支预测:CPU 将指令执行拆分为 "取指→译码→执行→写回" 等步骤并行处理,大幅提升吞吐量;但分支指令(如 if/switch 的跳转)会打断流水线 ------CPU 需等待分支判断结果,才能确定下一条指令地址,导致流水线 "空转"。
-
指令流水线:CPU 通过 "历史执行记录" 猜测分支走向,提前预取指令填充流水线。若预测成功,流水线无缝执行;若失败,需清空已预取的错误指令,重新加载正确指令,造成10-20 个 CPU 时钟周期的延迟(高频场景下性能损耗会被放大)。
对于像简单的if跳转,CPU是可以比较好地做分支预测的 。但是对于switch跳转,CPU则没有太多的办法。switch本质上是据索引,从地址数组里取地址再跳转。
例如:
java
// 情况 A:if-else
if (x == 100) {
handle100();
} else if (x == 101) {
handle101();
} else {
handleOther();
}
// 情况 B:switch
switch(x) {
case 100: handle100(); break;
case 101: handle101(); break;
default: handleOther();
}
如果是if,汇编大概是这样的逻辑:
java
cmp x, 100
beq label_100 // 条件分支(预测器能学习到:几乎总是走)
cmp x, 101
beq label_101
jmp default
如果编译器把switch变成跳转表,汇编会变成大概这样的逻辑:
java
idx = x - 100
if (idx < 0 || idx > (max-min)) goto default_label
target = table[idx] // 从内存取地址
jmp target // 间接跳转 ------ 难预测
如果x == 100的概率是 99.9%,那么:
-
if (x==100) 这一个条件分支对预测器来说是强偏向(always true)→ 很少错,流水线几乎不清空;
-
switch 若是跳转表实现:每次都通过间接跳转到对应目标,预测器对"跳到哪个目标"不一定猜得准,导致大量间接跳转失败,流水线损耗放大,吞吐下降。
所以在"极端不均匀分布"下,通过把高频情况先写成 if,把低频情况交给 switch,能兼顾可读性和性能。
为什么处理有序数组要比非有序数组快
这是一个在stackoverflow上有一个非常有名的问题,本质上其实就是是同一个CPU分支预测原理的不同表现,只是场景不一样
假设我们写了这样的循环:
java
for (int x : arr) {
if (x > 100) {
count++;
}
}
情况 A:arr 有序(递增)
- 前一半元素小于 100,后一半大于 100。
- CPU 的分支预测器能很快学到模式:
- 在前半段几乎总是 false;
- 到后半段几乎总是 true;
- 预测命中率接近 100%,流水线不需要频繁清空 → 执行很快。
情况 B:arr 无序(随机分布)
- 每个元素大于/小于100的概率差不多,CPU无法找到规律。
- 预测器几乎是瞎猜,预测成功率大约50%。
- 一半时间会预测失败 → 每次失败要清空流水线,性能下降。
这就是为什么"有序数组"在某些判断场景下比"无序数组"要快。
-
if (x == HOT) 这种情况就像有序数组中长时间连续出现同一种情况,CPU很容易预测成功。
-
switch(x)(特别是跳转表实现)就像无序数组,每次跳转目标可能不同,CPU 很难预测,导致流水线清空更频繁。
无论是"有序数组更快"还是"提前 if 优于 switch",核心原因都是:CPU 的分支预测器喜欢"稳定可预测的模式",不喜欢"杂乱无序的跳转"。
在想
其实在最开始了解这方面的知识只是把这个事情当作一个小tips了解,看完才发现,在很多情况下性能差距可能会达到2倍。
很多的判断,例如加载状态下,其实大部分都是"加载成功"也就意味着完全可以使用if-else加上switch的方式来提升性能,而代码可读性和扩展性也可以采用其他方案权衡。
现在回过头来看,八股文其实并不是研发需要的的最终答案,作用在于提醒我们去思考为什么。如果光去背"分支少用 if-else,分支多用 switch"则毫无意义,而是真正理解了CPU的分支预测、流水线、跳转表优化,才会发现计算机的世界远远比死记硬背来的更精彩。
我不推崇八股文,因为我觉得光会背没有意义。但是我也相信八股,因为不论是代码优化、架构设计、性能调优都逃不开这些基本原理。离开了地基,再高深的设计模式、框架思维,都很容易成为空中楼阁。
参考资料:提前if判断帮助cpu分支预测
公众号:柿蒂