我們常常聽說goto是一把雙面刃,它可以在程式碼中像任意門一樣穿梭自如,但是卻破壞了程式碼的結構與可讀性。
但是,goto的優點只有跳躍而已嗎?
本文將設計針對for、while、goto的效能做比較的實驗,探討goto是否能對程式碼的效能進行優化。
測試環境:
程式語言:C++
作業系統:Windows7
CPU:Intel i5-3470
使用IDE及編譯器:Visual Studio 2019
平台模式:64位元,debug模式(無最佳化)
以下是實驗程式碼:
unsigned int length = 1024 *
1024 * 1024 * 10;
int length2 = 1024 *
1024 * 1024;
int key = 0;
std::cout << "一層迴圈\n";
i = 0;
clock.restart();
for (i = 0;i <
length;i++)
{
key = i;
}
std::cout << "for的時間為" << clock.GetTime() << "毫秒\n";
i = 0;
clock.restart();
while (i < length)
{
key = i;
i++;
}
std::cout << "while的時間為" << clock.GetTime() << "毫秒\n";
i = 0;
clock.restart();
flag0:
key = i;
i++;
if (i < length) goto flag0;
std::cout << "goto的時間為" << clock.GetTime() << "毫秒\n";
std::cout << "二層迴圈\n";
i = 0;
j = 0;
clock.restart();
for (j = 0;j <
10;j++) {
for (i = 0;i <
length2;i++)
{
key
= i;
}
}
std::cout << "for的時間為" << clock.GetTime() << "毫秒\n";
j = 0;
i = 0;
clock.restart();
while (j < 10) {
i = 0;
while (i < length2)
{
key
= i;
i++;
}
j++;
}
std::cout << "while的時間為" << clock.GetTime() << "毫秒\n";
i = 0;
j = 0;
clock.restart();
flag:
i = 0;
flag2:
key = i;
i++;
if (i < length2) goto flag2;
j++;
if (j < 10) goto flag;
std::cout << "goto的時間為" << clock.GetTime() << "毫秒\n";
我們分別利用單層迴圈和雙層迴圈測試for、while、goto的效能。
首先,定義了一個 unsigned int的變數length,大小為1024 * 1024 * 1024 * 10。
然後分別設計for、while、goto的單層迴圈去運行它,次數為length。
然後,定義了一個 int的變數length2,大小為1024 * 1024 * 1024。
然後分別設計for、while、goto的雙層迴圈去運行它,次數為10*length2。
所以,單層迴圈和雙層迴圈運轉的次數是一樣的。
最後,我們在迴圈中加入了key=i,模擬實際迴圈中常見的狀況。
測試結果:
在一層迴圈中,for的效能最差,花費時間為4627毫秒,次之則為while,為4426毫秒,令人驚訝的是,goto的效能竟然最高,僅花費了4402毫秒。
然而,進入第二階段的雙層迴圈試驗,原本趨於劣勢的for迴圈竟逆轉局勢並驚險的勝出,花費了22026毫秒,而goto在這場比賽則敬陪末座,總花費時間為22119毫秒。
但是,真的只差了一點點,我們再重跑一次程式:
在第二次,在一層迴圈中,一樣是由goto勝出,但是呢,在雙層迴圈中,goto再次贏過for和while拿下勝利,總花費時間為22018毫秒,while則敬陪末座。
可見在第二層迴圈中,三者的速度是差不多的。
但是,如何讓goto在雙層迴圈中發揮它在單層迴圈的優勢呢?
我們再做個實驗
unsigned int length = 1024 *
1024 * 1024 * 10;
int length2 = 1024 *
1024 * 1024;
int key = 0;
i = 0;
j = 0;
clock.restart();
for (j = 0;j <
10;j++) {
for (i = 0;i <
length2;i++)
{
key
= i;
}
}
std::cout << "for+for的時間為" << clock.GetTime() << "毫秒\n";
i = 0;
j = 0;
clock.restart();
for (j = 0;j <
10;j++)
{
i = 0;
flag1:
i++;
key = i;
if (i < length2)goto flag1;
}
std::cout << "for+goto的時間為" << clock.GetTime() << "毫秒\n";
i = 0;
j = 0;
clock.restart();
flag3:
i = 0;
for (i = 0;i <
length2;i++)
{
key = i;
}
j++;
if (j < 10) goto flag3;
std::cout << "goto+for的時間為" << clock.GetTime() << "毫秒\n";
在這一次,我們分別使用雙層for迴圈、外圈為for內圈為goto、外圈為goto內圈為for,來做測試,跑的次數跟上一次的實驗一樣。
測試結果:
果不其然,讓goto在內層迴圈,效能上會有顯著優勢,而放在外層則不具有優勢。
為了防止運氣問題,我們再讓程式跑一次:
一樣的,for+goto再度以19074毫秒壓倒性的勝出,for+for最差,為22196毫秒,goto+for則為22123毫秒
結論:
在單層迴圈中,goto在效能上會比for和while來的有優勢,而在雙層迴圈中,goto在內層迴圈中依然可以為效能帶來貢獻。
這次的for、while、goto不專業效能評比就到這邊結束了,如果有其他點子想測試的,歡迎留言一起來做討論,我們下次見囉!
沒有留言:
張貼留言
有興趣或有疑問的歡迎提問與交流喔!!!