摘要: 技术传播的价值,不单单体如今经过商业化产品和开源项目来缩短咱们构建应用的路径,加速业务的上线速率,也会体如今优秀程序员在工做效率提高、产品性能优化和用户体验改善等小技巧方面的分享,以提升咱们的工做能力。前端
技术传播的价值,不单单体如今经过商业化产品和开源项目来缩短咱们构建应用的路径,加速业务的上线速率,也会体如今优秀程序员在工做效率提高、产品性能优化和用户体验改善等小技巧方面的分享,以提升咱们的工做能力。git
从本期开始,咱们将邀请来自阿里巴巴各个技术团队的程序员,涵盖中间件、前端、移动开发、大数据和人工智能等多个技术领域,分享他们在工做中的小技巧, 内容力求简短、实用和可操做。程序员
第一期的分享嘉宾,是来自阿里巴巴中间件技术团队的程序员 - 断岭,他是阿里微服务开源项目 Dubbo 的项目组成员,也是Java线上诊断开源项目 Arthas 的负责人。github
第一期:理解CPU分支预测,提升代码效率数组
在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来验证,把判断提早后是否就能够提升代码执行效率。dom
率。
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) @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) @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); }}
验证说明:
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倍的代码效率,这种技巧能够放在性能要求严格的地方。