匯編程序優化
『壹』 正常編寫的c語言程序編譯器會自動進行針對特定指令集用匯編語言優化嗎
這個要看你使用什麼編譯器了。查看編譯器的幫助文檔,它會告訴你它支持那些指令集,並且做哪些可能的優化。
不同的編譯器,是不一樣的。
補充:GCC 不太清楚,你連VC++的版本都不說。汗,VC6是不支持SSE的,需要安裝VC6SP5。
VS2005 和 VS2008 都支持 SSE。對 SSE/MMX 指令集優化得最好的,還是 Intel 的 c++ 編譯器。
對並行和高性能計算,Fortran 的優勢比較大。特別是 Fortran2003 的新特徵,為並行計算做了很多專門的設定。Intel 也有 Fortran 的編譯器。
『貳』 dsp程序匯編優化
指令之間能否並行主要是依據內核資源和指令間是否存在相互依賴的關系來決定的。你把你的C源碼經過CCS的C編譯器後生成的匯編語言(ASM文件)來改是現實的,因為已經進過了編譯器的匯編優化,基本上就沒有可讀性,而且也不能作為源文件來輸入的。要想做匯編優化,第一步可以考慮從寫線性匯編指令開始,因為相對於匯編語言而言,對編碼者的要求低不少。積累到一定的基礎了,再可以考慮使用匯編語言。
『叄』 如何優化C++程序代碼編寫
第一招:以空間換時間
計算機程序中最大的矛盾是空間和時間的矛盾,那麼,從這個角度出發逆向思維來考慮程序的效率問題,我們就有了解決問題的第1招--以空間換時間。比如說字元串的賦值:
方法A:通常的辦法
#defineLEN32
charstring1[LEN];
memset(string1,0,LEN);
strcpy(string1,"Thisisaexample!!");
方法B:
constcharstring2[LEN]="Thisisaexample!";
char*cp;
cp=string2;
使用的時候可以直接用指針來操作。
從上面的例子可以看出,A和B的效率是不能比的。在同樣的存儲空間下,B直接使用指針就可以操作了,而A需要調用兩個字元函數才能完成。B的缺點在於靈活性沒有A好。在需要頻繁更改一個字元串內容的時候,A具有更好的靈活性;如果採用方法B,則需要預存許多字元串,雖然佔用了大量的內存,但是獲得了程序執行的高效率。
如果系統的實時性要求很高,內存還有一些,那我推薦你使用該招數。
第二招: 使用宏而不是函數。
這也是第一招的變招。函數和宏的區別就在於,宏佔用了大量的空間,而函數佔用了時間。大家要知道的是,函數調用是要使用系統的棧來保存數據的,如果編譯器里有棧檢查選 項,一般在函數的頭會嵌入一些匯編語句對當前棧進行檢查;同時,CPU也要在函數調用時保存和恢復當前的現場,進行壓棧和彈棧操作,所以,函數調用需要一 些CPU時間。 而宏不存在這個問題。宏僅僅作為預先寫好的代碼嵌入到當前程序,不會產生函數調用,所以僅僅是佔用了空間,在頻繁調用同一個宏的時候,該現象尤其突出。
舉例如下:
方法C:
#definebwMCDR2_ADDRESS4
#definebsMCDR2_ADDRESS17
intBIT_MASK(int__bf)
{
return((1U<<(bw##__bf))-1)<<(bs##__bf);
}
voidSET_BITS(int__dst,
int__bf,int__val)
{
__dst=((__dst)&~(BIT_MASK(__bf)))|
(((__val)<<(bs##__bf))
&(BIT_MASK(__bf))))
}
SET_BITS(MCDR2,MCDR2_ADDRESS,ReGISterNumber);
方法D:
#definebwMCDR2_ADDRESS4
#definebsMCDR2_ADDRESS17
#definebmMCDR2_ADDRESSBIT_MASK(MCDR2_ADDRESS)
#defineBIT_MASK(__bf)
(((1U<<(bw##__bf))-1)
<<(bs##__bf))
#defineSET_BITS(__dst,__bf,__val)
((__dst)=((__dst)&~(BIT_MASK(__bf)))
|
(((__val)<<(bs##__bf))
&(BIT_MASK(__bf))))
SET_BITS(MCDR2,MCDR2_ADDRESS,
RegisterNumber);
D方法是我看到的最好的置位操作函數,是arm公司源碼的一部分,在短短的三行內實現了很多功能,幾乎涵蓋了所有的位操作功能。C方法是其變體,其中滋味還需大家仔細體會。
第三招:數學方法解決問題
現在我們演繹高效C語言編寫的第二招--採用數學方法來解決問題。數學是計算機之母,沒有數學的依據和基礎,就沒有計算機的發展,所以在編寫程序的時候,採用一些數學方法會對程序的執行效率有數量級的提高。舉例如下,求 1~100的和。
方法E:
intI,j;
for(I=1;I<=100;I++)
{
j+=I;
}
方法F
intI;
I=(100*(1+100))/2
這個例子是我印象最深的一個數學用例,是我的計算機啟蒙老師考我的。當時我只有小學三年級,可惜我當時不知道用公式 N×(N+1)/ 2 來解決這個問題。方法E循環了100次才解決問題,也就是說最少用了100個賦值,100個判斷,200個加法(I和j);而方法F僅僅用了1個加法,1 次乘法,1次除法。效果自然不言而喻。所以,現在我在編程序的時候,更多的是動腦筋找規律,最大限度地發揮數學的威力來提高程序運行的效率。
第四招:使用位操作
使用位操作。減少除法和取模的運算。在計算機程序中數據的位是可以操作的最小數據單位,理論上可以用"位運算"來完成所有的運算和操作。一般的位操作是用來控制硬體的,或者做數據變換使用,但是,靈活的位操作可以有效地提高程序運行的效率。舉例如下:
方法G
intI,J;
I=257/8;
J=456%32;
方法H
intI,J;
I=257>>3;
J=456-(456>>4<<4);
在字面上好像H比G麻煩了好多,但是,仔細查看產生的匯編代碼就會明白,方法G調用了基本的取模函數和除法函數,既有函數調用,還有很多匯編代碼和寄存器參與運算;而方法H則僅僅是幾句相關的匯編,代碼更簡潔,效率更高。當然,由於編譯器的不同,可能效率的差距不大,但是,以我目前遇到的MS C ,arm C 來看,效率的差距還是不小。
對於以2的指數次方為"*"、"/"或"%"因子的數學運算,轉化為移位運算"<< >>"通常可以提高演算法效率。因為乘除運算指令周期通常比移位運算大。
C語言位運算除了可以提高運算效率外,在嵌入式系統的編程中,它的另一個最典型的應用,而且十分廣泛地正在被使用著的是位間的與(&)、或(|)、非(~)操作,這跟嵌入式系統的編程特點有很大關系。我們通常要對硬體寄存器進行位設置,譬如,我們通過將AM186ER型80186處理器的中斷屏蔽控制寄存器的第低6位設置為0(開中斷2),最通用的做法是:
#define INT_I2_MASK 0x0040
wTemp = inword(INT_MASK);
outword(INT_MASK, wTemp &~INT_I2_MASK);
而將該位設置為1的做法是:
#define INT_I2_MASK 0x0040
wTemp = inword(INT_MASK);
outword(INT_MASK, wTemp | INT_I2_MASK);
判斷該位是否為1的做法是:
#define INT_I2_MASK 0x0040
wTemp = inword(INT_MASK);
if(wTemp & INT_I2_MASK)
{
… /* 該位為1 */
}
運用這招需要注意的是,因為CPU的不同而產生的問題。比如說,在PC上用這招編寫的程序,並在PC上調試通過,在移植到一個16位機平台上的時候,可能會產生代碼隱患。所以只有在一定技術進階的基礎下才可以使用這招。
第五招:匯編嵌入
在熟悉匯編語言的人眼裡,C語言編寫的程序都是垃圾"。這種說法雖然偏激了一些,但是卻有它的道理。匯編語言是效率最高的計算機語言,但是,不可能靠著它來寫一個操作系統吧?所以,為了獲得程序的高效率,我們只好採用變通的方法--嵌入匯編,混合編程。嵌入式C程序中主要使用在線匯編,即在C程序中直接插入_asm{ }內嵌匯編語句。
舉例如下,將數組一賦值給數組二,要求每一位元組都相符。
char string1[1024],string2[1024];
方法I
int I;
for (I =0 ;I<1024;I++)
*(string2 + I) = *(string1 + I)
方法J
#ifdef _PC_
int I;
for (I =0 ;I<1024;I++)
*(string2 + I) = *(string1 + I);
#else
#ifdef _arm_
__asm
{
MOV R0,string1
MOV R1,string2
MOV R2,#0
loop:
LDMIA R0!, [R3-R11]
STMIA R1!, [R3-R11]
ADD R2,R2,#8
CMP R2, #400
BNE loop
}
#endif
再舉個例子:
/* 把兩個輸入參數的值相加,結果存放到另外一個全局變數中 */
int result;
void Add(long a, long *b)
{
_asm
{
MOV AX, a
MOV BX, b
ADD AX, [BX]
MOV result, AX
}
}
方法I是最常見的方法,使用了1024次循環;方法J則根據平台不同做了區分,在arm平台下,用嵌入匯編僅用128次循環就完成了同樣的操作。這里有朋友會說,為什麼不用標準的內存拷貝函數呢?這是因為在源數據里可能含有數據為0的位元組,這樣的話,標准庫函數會提前結束而不會完成我們要求的操作。這個常式典型應用於LCD數據的拷貝過程。根據不同的CPU,熟練使用相應的嵌入匯編,可以大大提高程序執行的效率。
雖然是必殺技,但是如果輕易使用會付出慘重的代價。這是因為,使用了嵌入匯編,便限制了程序的可移植性,使程序在不同平台移植的過程中,卧虎藏龍,險象環生!同時該招數也與現代軟體工程的思想相違背,只有在迫不得已的情況下才可以採用。
第六招, 使用寄存器變數
當對一個變數頻繁被讀寫時,需要反復訪問內存,從而花費大量的存取時間。為此,C語言提供了一種變數,即寄存器變數。這種變數存放在CPU的寄存器中,使用時,不需要訪問內存,而直接從寄存器中讀寫,從而提高效率。寄存器變數的說明符是register。對於循環次數較多的循環控制變數及循環體內反復使用的變數均可定義為寄存器變數,而循環計數是應用寄存器變數的最好候選者。
(1) 只有局部自動變數和形參才可以定義為寄存器變數。因為寄存器變數屬於動態存儲方式,凡需要採用靜態存儲方式的量都不能定義為寄存器變數,包括:模塊間全局變數、模塊內全局變數、局部static變數;
(2) register是一個"建議"型關鍵字,意指程序建議該變數放在寄存器中,但最終該變數可能因為條件不滿足並未成為寄存器變數,而是被放在了存儲器中,但編譯器中並不報錯(在C++語言中有另一個"建議"型關鍵字:inline)。
下面是一個採用寄存器變數的例子:
/* 求1+2+3+….+n的值 */
WORD Addition(BYTE n)
{
register i,s=0;
for(i=1;i<=n;i++)
{
s=s+i;
}
return s;
}
本程序循環n次,i和s都被頻繁使用,因此可定義為寄存器變數。
第七招: 利用硬體特性
首先要明白CPU對各種存儲器的訪問速度,基本上是:
CPU內部RAM>外部同步RAM>外部非同步RAM>FLASH/ROM
對於程序代碼,已經被燒錄在FLASH或ROM中,我們可以讓CPU直接從其中讀取代碼執行,但通常這不是一個好辦法,我們最好在系統啟動後將FLASH或ROM中的目標代碼拷貝入RAM中後再執行以提高取指令速度;
對於UART等設備,其內部有一定容量的接收BUFFER,我們應盡量在BUFFER被占滿後再向CPU提出中斷。例如計算機終端在向目標機通過RS-232傳遞數據時,不宜設置UART只接收到一個BYTE就向CPU提中斷,從而無謂浪費中斷處理時間;
如果對某設備能採取DMA方式讀取,就採用DMA讀取,DMA讀取方式在讀取目標中包含的存儲信息較大時效率較高,其數據傳輸的基本單位是塊,而所傳輸的數據是從設備直接送入內存的(或者相反)。DMA方式較之中斷驅動方式,減少了CPU 對外設的干預,進一步提高了CPU與外設的並行操作程度。
以上就是我總結的如何優化C代碼的方法了。
『肆』 一個關於C語言語句用匯編優化的問題:
生成的匯編已經夠精簡了,看起來你是在一個雙重循環中使用了這句
內聯滑升余匯編應該解決不了這里的效率問題,可以嘗試修改你這段代碼,比如,內層的循環,那個指針沒必信滾要重復計算和載入
double temp1,temp2;
for(int j=0; j < MMM; j++)
{
double *dbp = hpdf->b[j]->var->vec;
for(int i=0;i <NNN;i++)
temp2+=temp1*temp1*dbp[i];
}
又比如,temp1的平方如果是在外層就可笑睜以計算的,也沒必要在內層循環計算
這只是個修改的思路,你再看編譯出來的代碼情況