理解CPU分支预测,提高代码效率

摘要: 技术传播的价值,不仅仅体现在通过商业化产品和开源项目来缩短我们构建应用的路径,加速业务的上线速率,也会体现在优秀程序员在工作效率提升、产品性能优化和用户体验改善等小技巧方面的分享,以提高我们的工作能力。

技术传播的价值,不仅仅体现在通过商业化产品和开源项目来缩短我们构建应用的路径,加速业务的上线速率,也会体现在优秀程序员在工作效率提升、产品性能优化和用户体验改善等小技巧方面的分享,以提高我们的工作能力。

从本期开始,我们将邀请来自阿里巴巴各个技术团队的程序员,涵盖中间件、前端、移动开发、大数据和人工智能等多个技术领域,分享他们在工作中的小技巧, 内容力求简短、实用和可操作。

第一期的分享嘉宾,是来自阿里巴巴中间件技术团队的程序员 - 断岭,他是阿里微服务开源项目 Dubbo 的项目组成员,也是Java线上诊断开源项目 Arthas 的负责人。

第一期:理解CPU分支预测,提高代码效率

一、基础概念:
Dubbo: 是一款高性能、轻量级的开源Java RPC框架,提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现;
ChannelEventRunnable: Dubbo 里所有网络事件的回调接口;
JMH:即Java Microbenchmark Harness,是专门用于代码微基准测试的工具套件。在性能优化的过程中,可以使用JMH对优化的结果进行量化的分析。
二、需求缘起:
在Stack Overflow上有一个非常著名的问题:为什么处理有序数组要比非有序数组快?从问题的结论来看,是分支预测对代码运行效率的提升起到了非常重要的作用。

现今的CPU是都支持分支预测(branch prediction)和指令流水线(instruction pipeline),这俩的结合可以极大的提高CPU的工作效率,从而提高代码执行效率。但这仅适用于简单的if跳转,但对于Switch跳转,CPU则没有太好的解决办法,因为Switch本质上是据索引,是从地址数组里取地址再跳转。

三、思考和方案假设:
要提高代码执行效率,一个重要的实现原则就是尽量避免CPU把流水线清空,从Stack Overflow上的讨论结果来看,通过提高分支预测的成功率,是可以降低CPU对流水线清空的概率。那么,除了在硬件层面,是否可以考虑代码层面帮CPU把判断提前,来提高代码执行效率呢?

四、方案验证:
在Dubbo的ChannelEventRunnable里有一个Switch来判断channel state。当一个channel建立起来之后,超过99.9%的情况,它的state都是ChannelState.RECEIVED,我们可以考虑,把这个判断提前。

以下通过JMH来验证,把判断提前后是否就可以提高代码执行效率。

率。

public class TestBenchMarks {
public enum ChannelState {
CONNECTED, DISCONNECTED, SENT, RECEIVED, CAUGHT }

@State(Scope.Benchmark)
public static class ExecutionPlan {
@Param({ "1000000" })
public int size;
public ChannelState[] states = null;

@Setup
public void setUp() {
    ChannelState[] values = ChannelState.values();
    states = new ChannelState[size];
    Random random = new Random(new Date().getTime());
    for (int i = 0; i < size; i++) {
        int nextInt = random.nextInt(1000000);
        if (nextInt > 100) {
            states[i] = ChannelState.RECEIVED;
        } else {
            states[i] = values[nextInt % values.length];
        }
    }
}

}

@Fork(value = 5)br/>@Benchmark
@BenchmarkMode(Mode.Throughput)
public void benchSiwtch(ExecutionPlan plan, Blackhole bh) {
int result = 0;
for (int i = 0; i < plan.size; ++i) {
switch (plan.states[i]) {
case CONNECTED:
result += ChannelState.CONNECTED.ordinal();
break;
case DISCONNECTED:
result += ChannelState.DISCONNECTED.ordinal();
break;
case SENT:
result += ChannelState.SENT.ordinal();
break;
case RECEIVED:
result += ChannelState.RECEIVED.ordinal();
break;
case CAUGHT:
result += ChannelState.CAUGHT.ordinal();
break;
}
}
bh.consume(result);
}

@Fork(value = 5)br/>@Benchmark
@BenchmarkMode(Mode.Throughput)
public void benchIfAndSwitch(ExecutionPlan plan, Blackhole bh) {
int result = 0;
for (int i = 0; i < plan.size; ++i) {
ChannelState state = plan.states[i];
if (state == ChannelState.RECEIVED) {
result += ChannelState.RECEIVED.ordinal();
} else {
switch (state) {
case CONNECTED:
result += ChannelState.CONNECTED.ordinal();
break;
case SENT:
result += ChannelState.SENT.ordinal();
break;
case DISCONNECTED:
result += ChannelState.DISCONNECTED.ordinal();
break;
case CAUGHT:
result += ChannelState.CAUGHT.ordinal();
break;
}
}
}
bh.consume(result);
}}
验证说明:

benchSiwtch里是纯Switch判断
benchIfAndSwitch 里用一个if提前判断state是否ChannelState.RECEIVED
Benchmark结果是:

Result "io.github.hengyunabc.jmh.TestBenchMarks.benchSiwtch":
576.745 ±(99.9%) 6.806 ops/s [Average]
(min, avg, max) = (490.348, 576.745, 618.360), stdev = 20.066
CI (99.9%): [569.939, 583.550](assumes normal distribution)
Run complete. Total time: 00:06:48

Benchmark (size) Mode Cnt Score Error Units
TestBenchMarks.benchIfAndSwitch 1000000 thrpt 100 1535.867 ± 61.212 ops/s
TestBenchMarks.benchSiwtch 1000000 thrpt 100 576.745 ± 6.806 ops/s
可以看到,提前if判断提高了近3倍的代码效率,这种技巧可以放在性能要求严格的地方。

五、总结:
Switch对于CPU来说难以做分支预测;
某些Switch条件如果概率比较高,可以在代码层设置提前if判断,充分利用CPU的分支预测机制;

理解CPU分支预测,提高代码效率

原文地址:http://blog.51cto.com/14031893/2318884

时间: 2024-10-05 04:09:37

理解CPU分支预测,提高代码效率的相关文章

CPU 分支预测

去年在安宁庄的时候, 有个同事阐述了一个观点:php中的if else  在执行时考虑到效率的原因,不会按我们的代码的顺序一条一条去试,而是随机找出一个分支,执行,如果不对,再随机找到一个分支 当时由于种种原因,也没过多去想这个问题,最近查了下资料,发现里面的学问还挺大的 php解释器是由c编写的,是个经编译生成的二进制文件, 我们编写的PHP代码相当于这个C程序的参数,只不过这个参数是个一个的文件, 这个C程序要解析这个php文件,产生相应的opcode,再去执行opcode对应的函数,每一部

优化技巧:提前if判断帮助CPU分支预测

摘要: 在stackoverflow上有一个非常有名的问题:为什么处理有序数组要比非有序数组快?,可见分支预测对代码运行效率有非常大的影响.要提高代码执行效率,一个重要的原则就是尽量避免CPU把流水线清空,那么提高分支预测的成功率就非常重要. 分支预测 在stackoverflow上有一个非常有名的问题:为什么处理有序数组要比非有序数组快?,可见分支预测对代码运行效率有非常大的影响. 现代CPU都支持分支预测(branch prediction)和指令流水线(instruction pipeli

前端程序员的蜕变——JS的 event 对象属性、使用实例、兼容性处理(极大提高代码效率、减少代码量)

下面讨论一下 js 中的 Event 对象,主要从以下三个方面详细的描述(点击标题可跳转到对应部分): 1.什么是event 2.怎么用event,用他该注意什么,几个简单实际应用 3.event在不同浏览器的存在的兼容问题,及如何去解决  1.  什么是event Event 对象代表事件的状态,比如事件在其中发生的元素.键盘按键的状态.鼠标的位置.鼠标按钮的状态等等.说的通俗一点就是,event是JS的一个系统内置对象.平时无法使用,当DOM元素发生按键.鼠标等等各种事件时,系统会自动根据D

使用 Django-debug-toolbar 优化Query 提高代码效率

一段程序执行效率慢,除了cpu计算耗时外,还有一个很重要的原因是SQL的Duplicated过多,使用Django-debug-toolbar能够快速找出哪些地方的SQL可以优化,提高程序执行效率 1. 安装 Django-debug-toolbar: pip install django-debug-toolbar 2. 在Django项目中创建APP用于toolbar测试: toolbar_test - __init__.py - templates - toolbar.html views

使用vue做项目如何提高代码效率

最近做了两个vue项目,算上之前做的两个项目,总共有四个vue项目的经验了,但是总体来说写的代码质量不是很高,体现在以下几点 1.代码没有高效的复用 自从使用vue做项目之后,以前使用面向过程变成的习惯随之被面向对象取代了,这是一个很好的转变,让代码看起来不是那么混乱了,但是不混乱并不代表质量高,比如实现一个检验输入是否有效的功能写的代码很长,而且重复代码很多例如: function checkInput(data) {let isTrue;if(!data.date) { this.$mess

浅谈分支预测、流水线与条件转移(转载)

一 一个问题 原文链接:http://www.cnblogs.com/yangecnu/p/4196026.html#undefined 在StackOverflow上有这么一个问题 Why is processing a sorted array faster than an unsorted array? .例子中,对一个数组进行条件求和,在排序前和排序后,性能有很大的差别.原始的例子是C++和Java的,这里将其换成了C# : static void Main(string[] args)

怎样提高开发效率

给你一个任务,限定5天内完成,如果你实际用了6天,可以说是开发效率不高,或者同样的一个任务,你花了6天,而你的同事却只用了4天,也可以说是你的开发效率不高,影响开发效率的因素有很多,下面就我个人的理解来谈谈怎样提高开发效率. 工具 俗话说,工欲善其事必先利其器,使用得心应手的工具必然会提高开发效率,做微软平台开发的肯定离不开VS,就VS本身来说,除了常用功能外一些常用的快捷键一定要能熟练运用,例如下面是我认为比较有用的几个快捷键: 注释: Ctrl + K + C 取消注释: Ctrl + K

【CPU微架构设计】利用Verilog设计基于饱和计数器和BTB的分支预测器

在基于流水线(pipeline)的微处理器中,分支预测单元(Branch Predictor Unit)是一个重要的功能部件,它负责收集和分析分支/跳转指令的参数和执行结果,当处理新的分支/跳转指令时,BPU将根据已有的统计结果和当前分支跳转指令的参数,预测其执行结果,为流水线取指提供决策依据,进而提高流水线效率. 下面讨论提出分支预测机制的主要原因和实际意义: 在流水线处理分支跳转指令时,目标地址往往需要推迟到指令的执行阶段才能运算得出,在此之前处理器无法及时得知下一条指令的取指地址,因此无法

提高代码编码的效率,习惯非常重要!

提高代码编码的效率,习惯非常重要.经验分享一下: 1.写代码前,先把需求弄清晰(这个非常关键):把业务转化成功能点,有多少个功能点? 2.设计 把业务对象弄清晰,从大到细设计,看看使用什么设计模式去把程序实现(设计模式里面都有一套思想,一定要把设计模式思想弄明确,再用.不能为了使用设计模式去强制使用) 弄清楚后,把代码功能框架写出来,注意,这个时候不能把具体实现的功能都写了,仅仅写代码的接口和整体的交互方法 检查一下,看是否有没有问题.没问题,往功能框架里面填内容,做实现 3.实现 代码实现从后