今天在羣裏看到一個有意思的問題——爲何處理排序數組比處理沒有排序的數組要快,這個問題來源於 StackoverFlow,雖然我看到代碼略微知道緣由,可是模模糊糊不夠清晰,搜了不少博客也講的不夠明白,因此就本身來總結了。java
首先來看一下問題,下面是很簡單的一段代碼,隨機生成一些數字,對其中大於 128 的元素求和,記錄並打印求和所用時間。git
import java.util.Arrays;
import java.util.Random;
public class Main {
public static void main(String[] args) {
// Generate data
int arraySize = 32768;
int data[] = new int[arraySize];
Random rnd = new Random(0);
for (int c = 0; c < arraySize; ++c)
data[c] = rnd.nextInt() % 256;
// !!! With this, the next loop runs faster
Arrays.sort(data);
// Test
long start = System.nanoTime();
long sum = 0;
for (int i = 0; i < 100000; ++i)
{
// Primary loop
for (int c = 0; c < arraySize; ++c)
{
if (data[c] >= 128)
sum += data[c];
}
}
System.out.println((System.nanoTime() - start) / 1000000000.0);
System.out.println("sum = " + sum);
}
}
複製代碼
個人運行結果:分別在對數組排序和不排序的前提下測試,在不排序時所用的時間比先排好序所用時間平均要多 10 ms。這不是巧合,而是必然的結果。github
問題就出在那個if
判斷上面,在舊文順序、條件、循環語句的底層解釋中其實已經提到了形成這種結果的緣由,只是舊文中沒有拿出具體的例子來講明。數組
爲了把這個問題搞明白,須要先對流水線
有必定的瞭解。計算機是指令流驅動的,執行的是一個一個的指令,而執行一條指令,又要通過取指、譯碼、執行、訪存、寫回、更新
六個階段(不一樣的劃分方式所包含的階段不同)。微信
六個階段使用的硬件基本是不同的,若是一條指令執行完再去執行另外一條指令,那麼在這段時間裏會有不少硬件處於空閒狀態,要使計算機的速度變快,那麼就不能讓硬件停下來,因此有了流水線技術。dom
流水線技術經過將指令重疊來實現幾條指令並行處理,下圖表示的是三階段指令時序,即把一個指令分爲三個階段。在第一條指令的 B 階段,A 階段相關的硬件是空閒的,因而能夠將第二條指令的 A 階段提早操做。oop
很明顯,這種設計大幅提升了指令運行的效率,聰明的你可能發現問題了,要是不知道下一條指令是什麼怎麼辦,那提早的階段也就白乾了,那樣流水線不就失效了?沒錯,這就是致使開篇問題的緣由。post
讓流水線出問題的狀況有三種:一、數據相關
,後一條指令須要用到前一條指令的運算結果;二、控制相關
,好比無條件跳轉,跳轉的地址須要在譯碼階段才能知道,因此跳轉以後已經被取出的指令流水就須要清空;三、結構相關
,因爲一些指令須要的時鐘週期長(好比浮點運算等),長時間佔用硬件,致使以後的指令沒法進入譯碼等階段,即它們在爭用同一套硬件。測試
代碼中的if (data[c] >= 128)
翻譯成機器語言就是跳轉指令,處理器事先並不知道要跳轉到哪一個分支,那難道就等知道了纔開始下一條指令的取指工做嗎?處理器選擇了僞裝知道會跳轉到哪一個分支(不是謙虛,是真的僞裝知道),若是猜中了是運氣好,而沒有猜中那就浪費一點時間從新來幹。this
沒有排序的數組,元素是隨機排列的,每次data[c] >= 128
的結果也是隨機的,前面的經驗就不可參考,因此下一次執行到這裏理論上仍是會有 50% 的可能會猜錯,猜錯了確定就須要花時間來修改犯下的錯誤,天然就會浪費更多的時間。
對於排好序的數組,開始幾回也須要靠猜,可是猜着猜着發現有規律啊,每次都是往同一個分支跳轉,因此之後基本上每次都能猜中,當遍歷到與 128 分界的地方,纔會出現猜不中的狀況,可是猜幾回以後,發現這又有規律啊,每次都是朝着另一個相同分支走的。
雖然都會猜錯,可是在排好序的狀況下猜錯的概率遠遠小於未排序時的概率,最終呈現的結果就是處理排序數組比未排序數組快,其緣由就是流水線發生了大量的控制相關現象,下面通俗一點,加深一下理解。
遠在他方心儀多年的姑娘忽然告訴你,其實她也喜歡你,激動的你三天三夜睡不着覺,決定開車前往她的城市,要和她待在一塊兒,可是要去的路上有不少不少岔路,你只能使用的某某地圖導航,做爲老司機而且懷着立馬要見到愛人心情的你,開車超快,什麼樣罰單都不在意了。
地圖定位已經跟不上你的速度了,爲了儘快到達,遇到岔路你都是隨機選一條路前進,遺憾的是,本身的選擇不必定對(咱們假設高速能夠回退),走錯路了就要從新回到分岔點,這就對應着未排序的狀況。
如今岔路是有規律的,告訴你開始一直朝着一邊走,到某個地點後會一直朝着另外一邊走,你只須要花點時間去探索一下開始朝左邊仍是右邊,到了中間哪一個地點會改變方向就能夠了,相比之下就能節省很多時間了,儘快見到本身的愛人,這對應着排好序的狀況。
最後的故事改編自兩我的的現實生活,一位是本身最好的朋友之一,談戀愛開心的睡不着覺;另外一位是微信上的一位好友,爲了對方從北京裸辭飛到了深圳。