我們拿一個算法的代碼實現(xiàn)來舉例子,首先我們寫一個求階乘的子函數(shù),這里我偷懶讓 ChatGPT 幫忙生成了一個:
// 階乘函數(shù)
intfactorial_iterative(int n) {
int result = 1;
// 從1乘到n
for (int i = 1; i <= n; ++i) {
result *= i;
}
return result;
}
// 示例
int main() {
int result_iterative = factorial_iterative(5);
printf("5的階乘是: %dn", result_iterative);
return 0;
}
這種簡單的迭代算法的優(yōu)點是比較容易理解,一眼就可以看出程序員想干什么。
但這樣寫出來的程序缺點也很大,就是運行效率非常低,我們在算法編寫中最怕的就是for 循環(huán),因為這里面會存在大量的比較和跳轉(zhuǎn),同時最容易產(chǎn)生一些代碼被無效的循環(huán)執(zhí)行。
這些缺點有的會被編譯器的優(yōu)化措施給規(guī)避掉,比如編譯器可以把一些需要內(nèi)存訪問的變量先放到寄存器中,等計算完結(jié)果后,再把結(jié)果從寄存器中轉(zhuǎn)移到內(nèi)存中,因為 CPU 讀取寄存器比讀取內(nèi)存可快多了。
但是編譯器也不是萬能的,有些優(yōu)化他就做不到。比如,我們改成下面展開的樣子,超標量的流水線就開始起作用了。
// 階乘函數(shù)
intfactorial_iterative(int n) {
int result0 = 1, result1 = 1, result2 = 1,result3 = 1;
// 從1乘到n
for (int i = 1; i < n; i += 4) {
result0 *= i;
result1 *= i + 1;
result2 *= i + 2;
result3 *= i + 3;
}
return (result0 * result1 * result2 * result3);
}
首先,我們假設(shè)開啟了編譯器優(yōu)化,編譯器已經(jīng)把所有內(nèi)存訪問的變量在函數(shù)開始都歸置到了寄存器中,那么這時候我們可以看到,4 個 result 的乘法語句是相互獨立的,他們的計算過程不依賴于其他 3 個語句的計算結(jié)果。
這就好比安排了四個人,給他們算 4 個單獨的式子,假設(shè)他們計算能力相同,于是他們會在同一段時間后跑到黑板上來互相乘一下算個總的結(jié)果。
而如果我們只是簡單的做循環(huán)展開,不增加新的寄存器變量,也就是不加人的情況下是怎么樣的呢?
// 階乘函數(shù)
intfactorial_iterative(int n) {
int result = 1;
// 從1乘到n
for (int i = 1; i < n; i += 4) {
result *= i;
result *= i + 1;
result *= i + 2;
result *= i + 3;
}
return (result * result * result * result);
}
這里只放了一個聰明的孩子做算式,不過你看他要做的 4 個算式,其中后一個算式總要用到前一個算式的結(jié)果,他即便再聰明也得一個一個的算。
這就是超標量流水線的用處,當然展開多少還需要我們自己衡量,本質(zhì)上也是用空間換時間,另外寄存器可是稀缺資源。
-
處理器
+關(guān)注
關(guān)注
68文章
19384瀏覽量
230494 -
mcu
+關(guān)注
關(guān)注
146文章
17227瀏覽量
351942 -
代碼
+關(guān)注
關(guān)注
30文章
4808瀏覽量
68816 -
編譯器
+關(guān)注
關(guān)注
1文章
1639瀏覽量
49198
發(fā)布評論請先 登錄
相關(guān)推薦
評論