減少指令數
1.降低資料精度
小數點後面位數越多,精度越大。100.11比100.1更加精確。越是精確的資料,所用的位數越多。運算時間越長。浮點數有雙精度和單精度之分,單精度浮點數佔32bit,雙精度浮點數佔64bit,處理雙精度資料自然要比單精度資料慢。
在c語言中,fabsf()是計算單精度浮點數絕對值的函式,而fabs()是計算雙精度浮點數絕對值的函式。如果資料是單精度浮點數,使用fabsf()要比fabs()快。如果乙個資料能夠使用單精度表示,就不需要使用雙精度表示。
2.減少函式的呼叫
函式呼叫會帶來額外的開銷,除了引起跳轉外,還會產生額外的指令。函式呼叫是這樣的,要進行引數壓棧出棧、暫存器的儲存、指令跳轉等多個步驟,如果程式的效能要求較高,就可以把較小的函式直接轉換為**。
1)將函式直接寫出語句
eg: int min(int a, int b)
c = min(a,b); //編譯器將優化為 c = a3.空間換時間
fibonacci序列實現的例子中:
eg1:
int f(int n)
int main()
}eg2:
int arr[40];
int f(int i)
arr[n] = result;
return result;
}int main()
}
eg1在電腦上耗時21秒。而eg2耗時不到1秒。eg2使用了陣列將f的值快取起來,這樣計算f(n) = f(n-1)+f(n-2)的時候不在執行遞迴,直接從陣列中取值即可。典型的空間換時間策略。
減少處理器不擅長的操作
單週期指令是處理器最喜歡的,不僅執行時間短,而且有利於流水線。加、減、邏輯運算都是單週期指令,乘、除、分支指令、浮點指令、記憶體訪問指令等,都需要較多的時鐘週期。程式設計的時候,盡可能的少用執行周期長的指令。
1.少用乘法
定點乘法在dsp中需要兩個cycle,而移位操作只需要乙個cycle。如果乙個數是乘以2的n次方,就可以用移位代替乘法。
len = len *4;
可以改寫為:
len = len<< 2;
2.少用除法和取餘
除法和取餘操作,將消耗大量時間,很多處理器沒有相應的指令,是通過軟體實現的。所以應該盡量少用。如果是除以乙個常識,eg:
f = f /5.0
可以將它改寫為乘法操作:
#define cof 1.0/5
f = f * cof
假如a資料範圍為[128,511], b資料範圍為18位有效位,當計算b/a的結果時,我們可以把a的資料範圍對映到乙個整數範圍(範圍可大可小?),然後與b相乘,然後再移位,使得最後的資料位與其原本b/a的資料位相等。比如我們可以把a通過tmp = 65535/(a-128),對映到[257,511]。即最後的結果把除法運算轉換為乘法運算,b/a = (b*tmp) >> 16,右移16bit確保最後的資料位與原來的b/a的結果資料位相同。
3.在精度允許的情況下,可以將浮點數定點化
浮點指令要比定點指令慢很多,功耗也大。在精度不那麼高的情況下,就可以將浮點數定點化。用定點指令代替浮點指令。
eg:alpha混合例項
利用兩張半透明的影象混合實現alpha混合效果。混合後的影象中每個畫素顏色值為:
pixel_c =(int)(pixel_a* alpha + pixel_b*(1-alpha));
alpha為透明度,介於0到1之間的小數。這條語句是浮點運算,如果每個畫素都經過這樣的運算,是相當耗時的。其實可以將alpha定點為0到32之間的乙個整數。改寫後為:
pixel_c =(int)(pixel_a* alpha + pixel_b*(32-alpha)+16)>>5;
在影象處理中,rgb轉換yuv中常用到這種技術。
//bt 601
y = ((((306* r +601*g + 117*b)>>10)-y_shift)*y_contrast)>>6;
y = clip(y, 0, 1023);
u= (-173* r -339*g + 512*b+512)>>10 ;
u = clip(u, -512, 511);
v= (512 * r - 428*g -84*b+512)>>10;
v = clip(v, -512,511);
本來rgb轉yuv時,應該是rgb三通道的值分別乘以相應的小數。在以上的演算法中,把相應的小數乘以了1024,變成定點整數。執行時間要大幅度減少。由於arm是精簡指令集的處理器,沒有專門計算浮點的硬體支援,它是在浮點模擬器中進行,因此,速度較慢。
4.儘量減少分支
現在的處理器都是流水線結構,if 和switch語句會帶來跳轉,而跳轉將打亂流水線的正常執行,影響程式的執行效率。
下面的**把奇數賦乙個值,把偶數賦乙個值。
for(i=0;i < 100;i++)
改寫如下形式會更好些:
for(i=0;i< 100;i++)
5.將最可能進入的分支放到if中,而不是else中
intel處理器有**分支單元,第一次進入乙個分支的時候,由於沒有歷史資訊可供參考,是否跳轉取決於static predictor(靜態**器)的**策略。通常靜態**器的**策略是:向下跳轉**為不跳轉,向上跳轉**為跳轉。根據此特性,if語句也是需要按照這種方式去編碼。
int a = -5;
int b = 0;
if(a > 0)
b= 1;
else
b= -2;
優化記憶體訪問
cpu的執行速度越來越快,但是記憶體的訪問速度卻增長緩慢。所有資料和程式必須放入記憶體才可以工作。cpu計算的時候,需要從記憶體中讀取相應的資料和程式,但是記憶體的訪問速度太慢,嚴重影響了計算機的執行效率。為了彌補記憶體速度低下的問題,處理器內部會放置一些sram做cache(快取),來提高處理器訪問程式和資料的速度。cache作為核心和記憶體的橋梁如下圖所示:
cache有兩個重要的性質:
1)時間侷限性(temporal locality):如果某個資料被訪問,那麼不久的將來它很可能再次被訪問。最典型的例子就是迴圈。迴圈體**被處理器重複執行,知道迴圈結束。如果將迴圈體**放到cache中,那麼只需要第一次讀取改**需要耗時,以後這些**每次都能夠被核心快速訪問,節約了時間。
2)空間侷限性(spatial locality):如果某項資料被訪問,那麼與它相鄰的資料很可能很快就被訪問。典型的例子就是陣列。我們一次將陣列中的多個資料從記憶體複製到cache中,雖然訪問第乙個元素需要花費點時間,但是後續的元素訪問就很快了。這是cache在空間侷限性上的應用。
1.少用陣列,少用指標
由於大塊資料將放到儲存器中,簡單區域性資料將放到暫存器中,因此,盡可能的少用陣列和指標,多用簡單區域性變數。
下面這段**,需要4次記憶體訪問
c = a [i]*b[i];
d = a[i]+b[i];
如果改寫如下形式,就只需要兩次記憶體訪問
x = a[i];
y = b[i];
c = x*y;
d = x+y;
2.少用全域性變數
全域性變數由於需要被多個模組使用,不會放到暫存器中,區域性變數才能被放到暫存器中。應該盡可能的少使用全域性變數。
int x;
int fun_a()
最好改寫為:
int x
int fun_a()
3.資料對齊訪問
對於32bit處理器,乙個int型變數i在記憶體中佔據2、3、4、5這四個byte位置
記憶體訪問這個資料的時候,會從0開始的4個byte讀入到暫存器a中,再將從4開始的4個byte讀入到暫存器b中,然後再將有效資料拼成乙個int資料,放到暫存器c中。這種方式訪問效率低下。如果變數i儲存的位置在從0開始的4個byte處,那麼i就可以一次讀入到暫存器中。這就是位元組對齊與不對齊的差別。對於2byte的變數,它的位址應該為2的整數倍,同理,對於4byte和8位元組的變數,它們的起始位址應該分別為4的整數倍和8的整數倍,這樣訪問速率才會高。
4.程式和資料訪問符合cache的時間和空間區域性特性
為了使cache訪問效率最高,程式和資料的組織,也應該符合兩個特性。典型的例子就是二位陣列的訪問。
如果a[i][j]在cache中,那麼a[i][j+1]就很可能在cache中,而a[i+1][j]則不一定。如果**這樣寫,效率並不高。
for( j = 0;j <500;j++)
}
改寫**如下後,效率將提高很多。
for( i= 0; i <500; i ++)
}
資料:《大話處理器》---處理器基礎知識讀本 萬木楊著
企業整站優化的一些技巧
優化的目的是用有效的方法對 進行優化,使 對搜尋引擎更加具有友好性,更加符合排名的規則,乙個 的排名百分之八十是來靠優化來得到的 優化的基礎是網頁的優化,網頁的優化是內容的優化 內容為王 現在時一些小技巧分享 2.meta標籤包括keyword description generator robot...
SQL的一些技巧和優化
一 使用萬用字元技巧 1 不要過度使用萬用字元,如果其他操作符能達到相同的目的,應該使用其他其他操作符。2 在確實需要使用萬用字元時,除非絕對必要,否則不要把他們用在搜尋模式的開始處,把萬用字元置於搜尋模式的開始處,搜尋起來最慢。3 注意萬用字元的位置。如果放錯位置,可能不會返回想要的資料 二 使用...
Pycharm中的一些技巧
作為乙個工具控,不來總結點pycharm的使用技巧怎麼行呢?1 遠端 編輯 除錯 這類使用情景就是,比如遠端機器是執行centos系統的伺服器,本地主機是安裝了pycharm的os x。要在本地寫 遠端機器上執行除錯。首先,新建乙個專案 目前還是乙個空專案 配置遠端python直譯器。並且設定ssh...