摘要: 技術傳播的價值,不單單體如今經過商業化產品和開源項目來縮短咱們構建應用的路徑,加速業務的上線速率,也會體如今優秀程序員在工做效率提高、產品性能優化和用戶體驗改善等小技巧方面的分享,以提升咱們的工做能力。前端
技術傳播的價值,不單單體如今經過商業化產品和開源項目來縮短咱們構建應用的路徑,加速業務的上線速率,也會體如今優秀程序員在工做效率提高、產品性能優化和用戶體驗改善等小技巧方面的分享,以提升咱們的工做能力。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倍的代碼效率,這種技巧能夠放在性能要求嚴格的地方。