邏輯程式碼計算最佳化
在不考慮面向物件的情況下,先讓我們先回憶下c語言中的3大結構。
1、順序結構
2、分支(條件)結構
3、迴圈結構
這三個結構任意組合構成了我們書寫的邏輯程式碼(演算法)。下面我們就根據這常用的三種結構進行調優。注意,下文中的使用的語言為JavaScript,但考慮大的是大部分語言的情況。
程式碼在計算機中實際都是一條條語句執行的,分支和迴圈結構在使用了jump語句實現了語句跳轉之後,實際上也變成了一個順序結構。如果程式碼以順序結構執行,則可以透過程式計數器自增讓程式碼順序執行。如果是jump跳轉語句則會發生定址,計算等額外步驟。
[ 彙編 ]
這裡c語言程式碼中寫了一個普通的迴圈。但是在計算機中實際執行時類似右邊goto版本的。每一個goto會加重計算機的計算,所以在考慮效能的情況下儘量使用順序結構。
常見的迴圈有
1、while
2、for
3、do…while
注意的是迴圈中while與for是先判斷在開始迴圈,do…while是先執行一次後判斷在開始迴圈
迴圈中兩種方案調優
1、減少每次迴圈處理的事務
2、減少迴圈的次數
在迴圈判斷條件中,通常我們將某個物件的屬性進行判斷比如
實際上在計算機的語句為
初始化(一次)
一次i初始化(var i)
一次i賦值(i = 0)
判斷(n次)
i值查詢(i)
array值查詢(array)
透過array找到length(array.length)
i 與 length 比較(i < array.length)
後處理,自增(n - 1次)
i取值(i)
計算i + 1(i + 1)
將結果賦值給(i = i + 1)
這次判斷實際上有四個語句組成,在看看下面的程式碼
在這個判斷中實際上在計算機的語句為:
一次i值查詢。
一次len值查詢
透過array找到length
i 與 length 比較
這裡在單次迴圈中語句減少了一條。在多次迴圈下,將減少了n條計算機語句。
如果是簡單的數值比較我們還可以使用倒序迴圈方式
這次實際上在計算機的語句為
i初始化(var i)
i賦值(i = array.length)
計算i - 1(i - 1)
將結果賦值給(i = i - 1)
後處理,自增(n-1次)
實際上這樣的語句確實少了挺多,但是必須是在順序無關和數值比較的前提下。因為部分語言會提供數值與布林值的自動轉換(像java這麼寫就會報錯)
減少使用遞迴
談到迴圈,自然也會談到遞迴。遞迴最簡單說就是某個函式繼續呼叫某個函式,一般是自己呼叫自己。
一個程序。有資料區,堆疊,PCB等內容。堆疊的大小在程序開啟的時候基本確定,棧能儲存執行時的區域性變數,和函式呼叫資訊。如果發生遞迴,則會在棧中持續新增內容。若執行函式需要用到區域性變數,或者呼叫函式不是最後一句時。將會發生中斷。中斷後,本次執行的上下文環境也會被儲存進棧內,形成一個呼叫棧。
[ 遞迴 ]
發生一次中斷,會觸發CPU的中斷處理,儲存中斷資訊現場,指令跳轉。恢復時又會跳轉指令,恢復中斷現場資訊。如果非必要,請不要使用遞迴。
棧的大小是固定的,如果超過棧的大小,就會有爆棧的可能(著名的stack overflow)。部分語言有尾呼叫最佳化,可以讓遞迴不儲存中斷資訊。
最佳化選擇
常見的選擇有:
if…else 更適合範圍匹配
switch…case 更適合單值匹配
不管使用的是if…else還是switch…case,判斷都是按書寫的順序開始執行的。所以我們應該把最可能出現的情況放在第一位。
由於選擇結構也會觸發指令跳轉,我們應該使用巢狀的if…else,使用樹的形式,讓到達每一個結果的情況儘可能的少。
邏輯程式碼計算最佳化
在不考慮面向物件的情況下,先讓我們先回憶下c語言中的3大結構。
1、順序結構
2、分支(條件)結構
3、迴圈結構
這三個結構任意組合構成了我們書寫的邏輯程式碼(演算法)。下面我們就根據這常用的三種結構進行調優。注意,下文中的使用的語言為JavaScript,但考慮大的是大部分語言的情況。
程式碼在計算機中實際都是一條條語句執行的,分支和迴圈結構在使用了jump語句實現了語句跳轉之後,實際上也變成了一個順序結構。如果程式碼以順序結構執行,則可以透過程式計數器自增讓程式碼順序執行。如果是jump跳轉語句則會發生定址,計算等額外步驟。
[ 彙編 ]
這裡c語言程式碼中寫了一個普通的迴圈。但是在計算機中實際執行時類似右邊goto版本的。每一個goto會加重計算機的計算,所以在考慮效能的情況下儘量使用順序結構。
最佳化迴圈常見的迴圈有
1、while
2、for
3、do…while
注意的是迴圈中while與for是先判斷在開始迴圈,do…while是先執行一次後判斷在開始迴圈
迴圈中兩種方案調優
1、減少每次迴圈處理的事務
2、減少迴圈的次數
在迴圈判斷條件中,通常我們將某個物件的屬性進行判斷比如
實際上在計算機的語句為
初始化(一次)
一次i初始化(var i)
一次i賦值(i = 0)
判斷(n次)
i值查詢(i)
array值查詢(array)
透過array找到length(array.length)
i 與 length 比較(i < array.length)
後處理,自增(n - 1次)
i取值(i)
計算i + 1(i + 1)
將結果賦值給(i = i + 1)
這次判斷實際上有四個語句組成,在看看下面的程式碼
在這個判斷中實際上在計算機的語句為:
一次i值查詢。
一次len值查詢
透過array找到length
i 與 length 比較
這裡在單次迴圈中語句減少了一條。在多次迴圈下,將減少了n條計算機語句。
如果是簡單的數值比較我們還可以使用倒序迴圈方式
這次實際上在計算機的語句為
初始化(一次)
i初始化(var i)
array值查詢(array)
透過array找到length(array.length)
i賦值(i = array.length)
判斷(n次)
i取值(i)
計算i - 1(i - 1)
將結果賦值給(i = i - 1)
後處理,自增(n-1次)
實際上這樣的語句確實少了挺多,但是必須是在順序無關和數值比較的前提下。因為部分語言會提供數值與布林值的自動轉換(像java這麼寫就會報錯)
減少使用遞迴
談到迴圈,自然也會談到遞迴。遞迴最簡單說就是某個函式繼續呼叫某個函式,一般是自己呼叫自己。
一個程序。有資料區,堆疊,PCB等內容。堆疊的大小在程序開啟的時候基本確定,棧能儲存執行時的區域性變數,和函式呼叫資訊。如果發生遞迴,則會在棧中持續新增內容。若執行函式需要用到區域性變數,或者呼叫函式不是最後一句時。將會發生中斷。中斷後,本次執行的上下文環境也會被儲存進棧內,形成一個呼叫棧。
[ 遞迴 ]
發生一次中斷,會觸發CPU的中斷處理,儲存中斷資訊現場,指令跳轉。恢復時又會跳轉指令,恢復中斷現場資訊。如果非必要,請不要使用遞迴。
棧的大小是固定的,如果超過棧的大小,就會有爆棧的可能(著名的stack overflow)。部分語言有尾呼叫最佳化,可以讓遞迴不儲存中斷資訊。
最佳化選擇
常見的選擇有:
if…else 更適合範圍匹配
switch…case 更適合單值匹配
不管使用的是if…else還是switch…case,判斷都是按書寫的順序開始執行的。所以我們應該把最可能出現的情況放在第一位。
由於選擇結構也會觸發指令跳轉,我們應該使用巢狀的if…else,使用樹的形式,讓到達每一個結果的情況儘可能的少。