阿里程序员工作小技巧 | 理解CPU分支预测,提高代码效率
技術傳播的價值,不僅僅體現在通過商業化產品和開源項目來縮短我們構建應用的路徑,加速業務的上線速率,也會體現在優秀程序員在工作效率提升、產品性能優化和用戶體驗改善等小技巧方面的分享,以提高我們的工作能力。
從本期開始,我們將邀請來自阿里巴巴各個技術團隊的程序員,涵蓋中間件、前端、移動開發、大數據和人工智能等多個技術領域,分享他們在工作中的小技巧, 內容力求簡短、實用和可操作。
第一期的分享嘉賓,是來自阿里巴巴中間件技術團隊的程序員 - 斷嶺,他是阿里微服務開源項目 Dubbo 的項目組成員,也是Java線上診斷開源項目 Arthas 的負責人。
第一期:理解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來驗證,把判斷提前后是否就可以提高代碼執行效率。
率。
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;@Setuppublic 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](assumes normal distribution) Run complete. Total time: 00:06:48Benchmark (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分支预测,提高代码效率的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 手机秒变IoT设备?——巧妙利用阿里云物
- 下一篇: 阿里云李刚:下一代低延时的直播CDN