沒有看過或者沒有看到這裏的小夥伴們,看到這個標題必定以爲摸不着頭腦。那這裏就先來解釋一下背景。函數
double poly(double a[], double x, long degree) { long i; double result = a[0]; double xpwr = x; for (i = 1; i <= degree; i++) { result += a[i] * xpwr; xpwr = x * xpwr; } return result; }
double polyh(double a[], double x, long degree) { long i; double result = a[degree]; for (i = degree; i >= 0; i--) { result = a[i] + x * result; } return result; }
這是 CSAPP 的兩道題,每一題是一段代碼,這兩段代碼實現了同一個功能。這兩道題有一個共同的問題,比較這兩段代碼的性能。性能
這裏的答案是,poly 的性能比 polyh 的性能要高。poly 的 CPE 是 5,而 polyh 的 CPE 是 8。code
這就顯得很尷尬了,我原覺得兩個函數的 CPE 都是 8。class
polyh 的 CPE 是 8 我沒有疑問,由於這個循環裏的操做是沒法並行的,也就是下一次迭代會依賴上一次迭代產生的結果。因此,CPE = 5 + 3,5 是浮點數乘法的延遲下屆,3 是浮點數加法的延遲下界。效率
poly 的 CPE 我本來認爲也是 8,兩個乘法是能夠並行的,可是這個加法的是依賴於第一個乘法的值,沒法並行,因此 CPE = 5 + 3 = 8。循環
上面的是個人猜測,因此我認爲這裏的答案是它們的 CPE 是相同的,性能也是相同的。可是如前面所寫,答案並非這樣的。因而,我把以前看的東西都翻出來想了一下,真的不是這樣的。並行
現代 CPU 是有一個流水線的概念的。什麼是流水線呢,想象一下汽車車間,咱們造一輛汽車,是分紅了不少道工序的,好比裝配發動機、裝車門、輪子等等。現代 CPU 也是相似的,咱們看到的一條指令,在執行的時候,經歷了一長串的流水線,致使了指令真正的執行順序和咱們看到的多是不同的,可是因爲現代出來的這種機制,能夠確保最後的結果是和咱們看到的是同樣的。總結
poly 函數,在執行的時候,因爲有兩個浮點數乘法單元,因此 a[i] * xpwr
和 xpwr = x * xpwr
能夠並行執行。而 a[i] * xpwr
能夠經過流水線的數據轉移,讓這個加法 result + a[i] * xpwr
能夠在下一次迭代的時候執行,由於每次迭代的時候,兩個乘法都不會依賴 result
這個結果。這樣,加法和乘法能夠並行執行。浮點乘法的延遲下界是 5,浮點加法的延遲下界是 3,因此浮點乘法是關鍵路徑,CPE 也天然就是 5 了。數據
再來看看 polyh 函數。這個函數的循環裏只有一個浮點乘法運算和一個浮點加法運算。先來看看浮點乘法運算,x * result
,很顯然,每一次乘法都須要依賴上一次迭代的結果,致使了加法沒法和乘法並行執行。因而,CPE 就成了 5 + 3 = 8 了。計算機
這個例子,我以爲頗有趣,由於它涉及到了一個流水線的細節。同時,也說明了,並非操做少的代碼,效率就高。
本文爲做者本身讀書總結的文章,因爲做者的水平限制,不免會有錯誤,歡迎你們指正,感激涕零。
《深刻理解計算機系統(第 3 版)》第 四、5 章