性能优化 java 24 次阅读 · 读完需要 15 分钟 0

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

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

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

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

原文地址:https://www.cnblogs.com/lalalagq/p/9986475.html

时间: 2024-11-09 00:03:57

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

《淘宝首页性能优化实践》文章阅读

想必很多人都已经看到了新版的淘宝首页,它与以往不太一样,这一版页面中四处弥散着个性化的味道,由于独特的个性化需求,前端也面临各方面的技术挑战: 数据来源多 串行请求渲染一个模块 运营数据和个性化数据匹配和管理 数据兜底容灾 本次淘宝首页改版,虽已不再支持 IE6 和 IE7 等低版本的古董浏览器,但依然存在多个影响首页性能的因素: 依赖系统过多,数据的请求分为三块,其一是静态资源(如 js/css/image/iconfont 等):其二是推到 CDN 的静态数据(如运营填写的数据.前端配置信息

HBase性能优化 Java Api

1. 使用“连接池” 如果每次和Hbase交互时都去新建连接的话,显然是低效率的,HBase也提供类连接池相关的API. 1.1. HTablePool 早期的API中使用它,但很不幸,现在它已经过时了.在次不再描述. 1.2. HConnection 取代HTablePool的就是现在的HConnection,可以通过它拿到几乎所有关于HBase的相关操作对象. private static HConnection connection = null; private static Confi

【杂谈】BI系统的前端性能优化

近一年,由于笔者团队的一些变化,笔者开始承担一个BI系统的前端应用的维护和迭代,一年中,围绕着这个BI系统,发生了不少令人啼笑皆非和醍醐灌顶的故事.最近,终于有时间把它们一点点的沉淀写来,以文字的形式呈现出来. 首先,简单介绍下笔者维护的这个BI系统,和常规的BI(Business Intelligence)系统一样,笔者的BI系统同样可以分为三层: 数据层:这一层其实主要是ETL的过程,即将业务数据库的数据通过抽取(Extract).转换(transform).加载(Load)到新的数据库中,

Java 代码性能优化总结

35 个 Java 代码性能优化总结 前言 代码优化,一个很重要的课题.可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于代码的运行效率有什么影响呢?这个问题我是这么考虑的,就像大海里面的鲸鱼一样,它吃一条小虾米有用吗?没用,但是,吃的小虾米一多之后,鲸鱼就被喂饱了.代码优化也是一样,如果项目着眼于尽快无BUG上线,那么此时可以抓大放小,代码的细节可以不精打细磨:但是如果有足够的时间开发.维护代码,这时候就必须考虑每个可以优化的细节了,一个一个细小的优化点累积起来,对于代码的运行效率

Java GC 专家系列5:Java应用性能优化的原则

本文是GC专家系列中的第五篇.在第一篇理解Java垃圾回收中我们学习了几种不同的GC算法的处理过程,GC的工作方式,新生代与老年代的区别.所以,你应该已经了解了JDK 7中的5种GC类型,以及每种GC对性能的影响. 在第二篇Java垃圾回收的监控中介绍了在真实场景中JVM是如何运行GC,如何监控GC数据以及有哪些工具可用来方便进行GC监控. 在第三篇GC 调优中基于真实案例介绍了可用于GC调优的最佳选项.同时也描述了如何通过降低移动到老年代中对象的数量来缩短Full GC耗时,以及如何设置GC类

Java性能优化技巧及实战

Java性能优化技巧及实战 关于Java代码的性能优化,是每个javaer都渴望掌握的本领,进而晋升为大牛的必经之路,但是对java的调优需要了解整个java的运行机制及底层调用细节,需要多看多读多写多试,并非一朝一夕之功.本文是近期笔者给公司员工内部做的一个培训,主要讲述在系统压测过程中出现的性能问题,以及如何在编码过程中提升代码的运行效率,需要掌握哪些实战技巧.片子里干货较多,也很具有实操性,因此发文出来,共享给大家(部分数据做了去除公司特征信息,见谅).(PS:由于原文是ppt,因此做了导

Java性能优化指南系列(二):Java 性能分析工具

进行JAVA程序性能分析的时候,我们一般都会使用各种不同的工具.它们大部分都是可视化的,使得我们可以直观地看到应用程序的内部和运行环境到底执行了什么操作,所以性能分析(性能调优)是依赖于工具的.在第2章,我强调了基于数据驱动的性能测试是非常重要的,我们必须测试应用的性能并理解每个指标的含义.性能分析和数据驱动非常类似,为了提升应用程序的性能,我们必须获取应用运行的相关数据.如何获取这些数据并理解它们是本章的主题.[本章重点介绍JDK中提供的性能分析工具] 操作系统工具及其分析 程序分析的起点并不

JVM性能优化,提高Java的伸缩性

很多程序员在解决JVM性能问题的时候,花开了很多时间去调优应用程序级别的性能瓶颈,当你读完这本系列文章之后你会发现我可能更加系统地看待这类的问题.我说过JVM的自身技术限制了Java企业级应用的伸缩性.首先我们先列举一些主导因素. 主流的硬件服务器提供了大量的内存 分布式系统有大量内存的需求,而且该需求在持续增长 一个普通Java应用程序所持有的对空间大概在1GB~4GB,这远远低于一个硬件服务器的内存管理能力以及一个分布式应用程序的内存需求量.这被称之为Java内存墙,如下图所示(图中表述Ja

Java编程性能优化一些事儿【转】

原文出处: 陶邦仁 在JAVA程序中,性能问题的大部分原因并不在于JAVA语言,而是程序本身.养成良好的编码习惯非常重要,能够显著地提升程序性能. 1. 尽量在合适的场合使用单例 使用单例可以减轻加载的负担,缩短加载的时间,提高加载的效率,但并不是所有地方都适用于单例,简单来说,单例主要适用于以下三个方面: 第一,控制资源的使用,通过线程同步来控制资源的并发访问: 第二,控制实例的产生,以达到节约资源的目的: 第三,控制数据共享,在不建立直接关联的条件下,让多个不相关的进程或线程之间实现通信.