如何将AI能力与大数据技术结合,助力数据分析治理等工作的效率大幅提升,优化大数据引擎的性能及成本? 了解详情
写点什么

阿里程序员工作小技巧:理解 CPU 分支预测,提高代码效率

  • 2019-04-01
  • 本文字数:2343 字

    阅读完需:约 8 分钟

阿里程序员工作小技巧:理解CPU分支预测,提高代码效率

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


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


一 、基础概念

a. Dubbo: 是一款高性能、轻量级的开源 Java RPC 框架,提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现;


b. ChannelEventRunnable: Dubbo 里所有网络事件的回调接口;


c. 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 里用一个如果提前判断状态是否ChannelState.RECEIVED


基准测试结果是:


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 UnitsTestBenchMarks.benchIfAndSwitch 1000000 thrpt 100 1535.867 ± 61.212 ops/sTestBenchMarks.benchSiwtch 1000000 thrpt 100 576.745 ± 6.806 ops/s
复制代码


可以看到,提前 if 判断提高了近 3 倍的代码效率,这种技巧可以放在性能要求严格的地方。。


五、总结

  • 开关对于 CPU 来说难以做分支预测。

  • 某些 Switch 条件如果概率比较高,可以在代码层设置提前 if 判断,充分利用 CPU 的分支预测机制。


查看原文链接:阿里程序员工作小技巧| 理解CPU分支预测,提高代码效率


2019-04-01 08:003108

评论 5 条评论

发布
用户头像
那为什么不直接用if else if 来替换switch呢?
2019-12-09 17:58
回复
用户头像
错别字都不校验,不走心啊
2019-04-01 10:08
回复
用户头像
Switch 翻译成 “S 女士条件 ” 我也是服了。
2019-04-01 09:54
回复
用户头像
“以下通过江铃控股来验证……benchSiwtch 里是纯 S 女巫判断……某些 S 女士条件如果概率比较高”
——翻译程序有点蠢萌啊
2019-04-01 09:41
回复
非常抱歉,翻译问题带来的困扰,我们已经修改。
2019-04-01 10:23
回复
没有更多了
发现更多内容

【高并发】开篇:线程与多线程

冰河

Java 并发编程 多线程 高并发 异步编程

杂谈——程序人生我的大学

思想者杰克

linux lsquic 编译

webrtc developer

科大讯飞联袂伯俊科技进军3C零售,构建发展新格局

科技热闻

极客时间【架构实战营】第二期 模块七作业

Geek_91606e

架构实战营

TDengine在浙商银行微服务监控中的实践

TDengine

tdengine 后端 时序数据库

GitHub远程免密连接详解,还顺手解决了RPC失败HTTP413

老表

GitHub RPC HTTP 11月日更

架构实战营-总结

哈希

牛掰!“基础-中级-高级”Java程序员面试集结,看完献出我的膝盖

Java spring 程序员 JVM hashmap

RadonDB ClickHouse on K8s 2.1.0 发布!

RadonDB

数据库 Kubernetes Clickhouse RadonDB

Github上线仅六天,收获Star超55K+,这套笔记能拿下90%以上面试

Java redis spring 程序员 架构

百度智能云人脸采集SDK通过CFCA权威安全测评

百度开发者中心

安全 sdk

Bash 脚本简介

信码由缰

bash Shell

不敢想,做个博客竟如此简单!

程序员鱼皮

博客

百度Apollo参编首批汽车信息安全国家标准,跻身车联网信息安全第一梯队

百度开发者中心

Apollo

百度智能云与雅量商业智能携手,加速零售行业智能化升级

百度开发者中心

百度智能云 零售行业

Hexo个人博客快速部署到Gitee&Coding详细教程

老表

Hexo gitee CODING 博客配置 11月日更

科技热点周刊|ClickHouse 融资 2.5 亿美元、个人信息保护法正式实施、Facebook 改名 Meta

青云技术社区

云计算 facebook 云原生

前后端、多语言、跨云部署,全链路追踪到底有多难?

阿里巴巴中间件

阿里云 云原生 中间件 全链路追踪

杂谈—程序人生第一份工作

思想者杰克

程序人生 新手指南 程序

常用的Nmap脚本及使用实例

喀拉峻

网络安全 信息安全 渗透测试 脚本 nmap

2021年10月云主机性能评测报告

博睿数据

看完这篇SpringBoot让我在阿里成功涨薪40%,感谢

Java 编程 程序员 程序人生 springboot

行业白皮书发布!百度智慧城市助力城市“双碳”目标达成

百度开发者中心

人工智能 智慧城市

ReplacingMergeTree:实现Clickhouse数据更新

华为云开发者联盟

数据 事务 Clickhouse 数据更新 OLAP数据库

万字长文解密数据异构最佳实践(含完整代码实现)!!

冰河

MySQL 数据库 canal 数据同步 数据异构

阿里云视频云,用技术普惠打造平民化“虚拟人”

阿里云视频云

人工智能 阿里云 视频云 数字人 虚拟人

操作系统——计算机硬件简介

思想者杰克

接口文档工具yapi的安装

小鲍侃java

11月日更

端开发技术——5个高效的Flutter开发工具

思想者杰克

阿里程序员工作小技巧:理解CPU分支预测,提高代码效率_文化 & 方法_断岭_InfoQ精选文章