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

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

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

第一期的分享嘉宾,是来自阿里巴巴中间件技术团队的程序员 - 断岭,他是阿里微服务开源项目 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)
@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);

}}
验证说明:

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
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的分支预测机制;

原文地址:https://segmentfault.com/a/1190000017063609

性能优化 java 24 次阅读 · 读完需要 15 分钟 0的更多相关文章

  1. 冒泡排序,冒泡性能优化--java实现

    冒泡排序说明: 一次比较两个元素,如果他们的顺序错误就把他们交换过来. 重复地进行直到没有再需要交换,也就是说已经排序完成. 越小的元素会经由交换慢慢“浮”到数列的顶端. 冒泡排序算法的运作如下: 比 ...

  2. JVM性能优化--Java的垃圾回收机制

    一.Java内存结构 1.Java堆(Java Heap) java堆是java虚拟机所管理的内存中最大的一块,是被所有线程共享的一块内存区域,在虚拟机启动时创建.此内存区域的唯一目的就是存放对象实例 ...

  3. HBase性能优化 Java Api

    1. 使用“连接池” 如果每次和Hbase交互时都去新建连接的话,显然是低效率的,HBase也提供类连接池相关的API. 1.1. HTablePool 早期的API中使用它,但很不幸,现在它已经过时 ...

  4. Android 性能优化(24)*性能工具之「Traceview,dmtracedump」Profiling with Traceview and dmtracedump :记录并查看函数调用栈*

    Profiling with Traceview and dmtracedump In this document Traceview Layout         Traceview工具界面介绍 T ...

  5. 【Java】Java-正则匹配-性能优化

    Java-正则匹配-性能优化 Java 正则 点_百度搜索 在Java类中如何用正则表达式表示小数点啊?_百度知道 使用Jakarta-ORO库的几个例子 - 小橡树 - ITeye博客 正则表达式以 ...

  6. C#性能优化实践 资料整理

    缓存(Cache)是性能优化中最常用的优化手段.适用的情况是频繁的获取一些数据,而每次获取这些数据需要的时间比较长.这时,第一次获取的时候会用正常的方法,并且在获取之后把数据缓存下来.之后就使用缓存的 ...

  7. C#性能优化实践【转】

    性能主要指两个方面:内存消耗和执行速度.性能优化简而言之,就是在不影响系统运行正确性的前提下,使之运行地更快,完成特定功能所需的时间更短. 本文以.NET平台下的控件产品MultiRow为例,描述C# ...

  8. RabbitMQ性能优化

    修改rabbitmq.config文件 rabbitmq.config文件时rabbitmq的配置文件,他遵守Erlang配置文件定义. rabbitmq.config文件位置: Unix $RABB ...

  9. C#性能优化实践(转载)

    原文地址http://www.infoq.com/cn/articles/C-sharp-performance-optimization?utm_source=infoq&utm_mediu ...

随机推荐

  1. git自动化部署+rsync文件同步

    1.进入线上git裸仓库 2.编辑post-receive #!/bin/sh unset GIT_DIR cd /var/www/ git pull http://web:xxxxxxx@120.3 ...

  2. LuoguP2846[USACO08NOV]光开关Light Switching【线段树维护区间异或】By cellur925

    题目传送门 题目大意,给你一串灯,按一下开关可以将灯的状态取反(开变成关,关变成开).维护这个序列的两种操作:询问区间内有多少灯是开着的,区间按灯. 开始想的是分别维护区间内0的数量,1的数量,两个懒 ...

  3. jQuery 第九章 工具方法

    $.type() $.isArray() $.isFunction() $.isWindow()... $.trim() $.proxy() $.noConflict() $.each() $.map ...

  4. [洛谷P3512 [POI2010]PIL-Pilots]

    题目链接: 传送门走这里 题目分析: 感觉不是很难啊--不像是蓝题(AC量也不像)恶意评分? 少打了一个+1调了半天,就这样居然还能过60pts?我思路和题解第一篇高度重合是什么鬼啊,太过分了吧本来还 ...

  5. 学习JavaScript数据结构与算法 (一)

    学习JavaScript数据结构与算法 的笔记, 包含一二三章 01基础 循环 斐波那契数列 var fibonaci = [1,1] for (var i = 2; i< 20;i++) { ...

  6. 第03课 在VMwave 14.0 上配置企业级CentOS 6.6操作系统

    第一部分:配置虚拟硬件 1.1 启动VMware,选择文件-->新建虚拟机(Ctrl + N),创建一个虚拟机. (VMware的安装过程较为简单,可自行百度.) 1.2 此时,出现新建虚拟机向 ...

  7. 浅析String

    浅析String String的设计结构: 首先我们看一下 String的源码 public final class String     implements java.io.Serializabl ...

  8. android 7.0 应用间文件共享FileProvider

    1.官方教程 Android 7.0 以后安全系数提高,应用间文件共享要使用FileProvider.原来的 file:/// Uri 替换为 content://Uri  https://devel ...

  9. 常用的DOCS命令

    1.Help 可以查看当前DOS常用命令,是帮助2.Help dir 查看Dir命令的帮助,使用帮助3.ipconfig 查看当前电脑的IP地址4.ping 127.0.0.1 测试与某一台电脑之间网 ...

  10. HDU 2227 Find the nondecreasing subsequences dp思想 + 树状数组

    http://acm.hdu.edu.cn/showproblem.php?pid=2227 用dp[i]表示以第i个数为结尾的nondecreasing串有多少个. 那么对于每个a[i] 要去找 & ...