1. 程式人生 > >高頻率呼叫的函式一定要保證最優化,慎用除法和餘數(轉)

高頻率呼叫的函式一定要保證最優化,慎用除法和餘數(轉)

 在近期優化系統的過程中,發現有個函式在高壓力下,竟然佔用13.3%的時間,這個函式內部很簡單,基本就是這樣:

id_to_type(id)

{

      int iIndex = id%150000;

      return g_data[iIndex].cType;

}

核心基本上就在這個%取餘上,這往往使我們分組提高效率的常用方法,但是對於高頻率呼叫上,則可以避免了。

如何解決呢?下面的附錄說明了一些替代方法,但是顯然並不是符合我們現在要解決的這個問題,當前我採用了修改設計的方法,將type合入到id的一部分鐘,必須 id 0x10000000 表示type=0的非等最大值,0x20000000表示type=1的非等最大值。

=============附錄============

(說明:文章中的很多資料可能在新的CPU或不同的CPU或不同的系統環境下有不同的結果,可能不能面面俱到)
x86系列的CPU對於位運算、加、減等基本指令都能在1個CPU週期內完成(現在的CPU還能亂序執行,從而使指令的平均CPU週期更小);現在的CPU,做乘法也是很快的(1個CPU週期左右,或者是需要兩/三個週期,但每個週期能啟動一個新的乘指令),但作為基本指令的除法卻超出很多人的預料,它是一條很慢的操作,整數和浮點的除法都慢;我測試的英特爾P5賽揚CPU浮點數的除法差不多是37個CPU週期,整數的除法是80個CPU週期,AMD2200+浮點數的除法差不多是21個CPU週期,整數的除法是40個CPU週期。(改變FPU運算精度對於除法無效)(SSE指令集的低路單精度數除法指令DIVPS 18個CPU週期,四路單精度數除法指令DIVSS 36個CPU週期)  (x86求餘運算和除法運算是用同一條CPU指令實現的;據說,很多CPU的整數除法都是用數學協處理器的浮點除法器完成的;有一個推論就是,浮點除法和整數除法不能並行執行)
  本文將給出一些除法的優化方法或替代演算法  (警告:某些替代演算法並不能保證完全等價!)
  1.儘量少用除法
   比如: if (x/y>z) ...
   改成: if ( ((y>0)&&(x>y*z))||((y<0)&&(x<y*z)) ) ...
   (少用求餘)
   比如: ++index; if (index>=count) index=index % count;  //assert(index<count*2);
   改成: ++index; if (index>=count) index=index - count;
  2.用減法代替除法
   如果知道被除數是除數的很小的倍數,那麼可以用減法來代替除法
   比如:  uint32 x=200;
         uint32 y=70;
           uint32 z=x/y;
   改成:  uint z=0;
           while (x>=y)
           {
              x-=y;  ++z;
           }
   一個用減法和移位完成的除法 (如果你沒有除法指令可用:)
     uint32 div(uint64 s,uint32 z) //return u/z   
     {
         uint32 x=(uint32)(s>>32);
         uint32 y=(uint32)s;
         //y儲存商 x儲存餘數
         for (int i=0;i<32;++i)
         {
             uint32 t=((int32)x) >> 31; 
             x=(x<<1)|(y>>31);
             y=y<<1;
             if ((x|t)>=z)
             {
                 x-=z;
                 ++y;
             }
         }
         return y;
     }
     (該函式經過了測試;z==0需要自己處理;對於有符號除法,可以用取絕對值的方法(當然也不是輕鬆就能
寫出完全等價的有符號除法的:); 如果不需s的64bit長度,僅需要32bit,那麼可以化簡這個函式,但改進不多)
   
  3.用移位代替除法  (很多編譯器能自動做好這個優化)
   要求除數是2的次方的常量; (同理:對於某些應用,可以優先選取這樣的數來做除數)
   比如:  uint32 x=213432575;
         uint32 y=x/8;
   改成:  y=x>>3;
   對於有符號的整數;
   比如:  int32 x=213432575;
         int32 y=x/8;
   改成:  if (x>=0)  y=x>>3;  
           else  y=(x+(1<<3-1))>>3; 
  4.合併除法   (替代方法不等價,很多編譯器都不會(而且不應該)幫你做這種優化)
   適用於不能用其它方法避免除法的時候;
   比如:  double x=a/b/c;
   改成:  double x=a/(b*c);
   比如:  double x=a/b+c/b;
   改成:  double x=(a+c)/b;
   比如:  double x=a/b;
    double y=c/d;
    double z=e/f;
   改成:  double tmp=1.0/(b*d*f);
           double x=a*tmp*d*f;
           double y=c*tmp*b*f;
           double z=e*tmp*b*d;    
   
  5.把除法佔用的時間充分利用起來 
   CPU在做除法的時候,可以不用等待該結果(也就是後面插入的指令不使用該除法結果),而插入多條簡單整數
指令(不包含整數除法,而且結果不能是一個全域性或外部變數等),把除法佔用的時間節約出來; 
   (當除法不可避免的時候,這個方法很有用)
  6.用查表的方法代替除法
    (適用於除數和被除數的可能的取值範圍較小的情況,否則空間消耗太大)
    比如   uint8  x;  uint8 y; 
         uint8  z=x/y;
    改成   uint8  z=table[x][y];    //其中table是預先計算好的表,table[j]=i/j;  
           //對於除零的情況需要根據你的應用來處理
    或者:uint8  z=table[x<<8+y];  //其中table=(i>>8)/(i&(1<<8-1)); 
    比如   uint8  x; 
         uint8  z=x/17;
    改成   uint8  z=table[x];    //其中table=i/17;  
  7.用乘法代替除法
     (替代方法不等價,很多編譯器都不會(而且不應該)幫你做這種優化)
     比如:  double x=y/21.3432575;
     改成:  double x=y*(1/21.3432575); //如果編譯器不能優化掉(1/21.3432575),請預先計算出該結果
   對於整數,可以使用與定點數相似的方法來處理倒數
   (該替代方法不等價)
   比如:  uint32 x,y;  x=y/213;
   改成:  x=y*((1<<16)/213)  >> 16;
   某些應用中y*((1<<16)/213)可能會超出值域,這時候可以考慮使用int64來擴大值域
           uint32 x=((uint64)y)*((1<<31)/213)  >> 31;
      也可以使用浮點數來擴大值域
           uint32 x=(uint32)(y*(1.0/213)); (警告: 浮點數強制型別轉換到整數在很多高階語言裡都是
一條很慢的操作,在下幾篇文章中將給出其優化的方法)
   對於這種方法,某些除法是有與之完全等價的優化方法的:
     無符號數除以3:  uint32 x,y;   x=y/3; 
     推理:                       y               y          y               (1<<33)+1   y      
             x=y/3  =>  x=[-]  =>  x=[- + ---------]  => x=[--------- * -------] // []表示取整 
                                   3               3  3*(1<<33)             3       (1<<33) 
                                        y        1              
              (可證明: 0 <= --------- < - )
                              3*(1<<33)   3                                           
             即: x=(uint64(y)*M)>>33;  其中魔法數M=((1<<33)+1)/3=2863311531=0xAAAAAAAB;
     無符號數除以5:  uint32 x,y;   x=y/5;  (y<(1<<31))
     推理:                       y               y      3*y               (1<<33)+3    y      
             x=y/5  =>  x=[-]  =>  x=[- + ---------]  => x=[--------- * -------] 
                                   5               5   5*(1<<33)             5       (1<<33) 
                                      3*y      1              
              (可證明: 0 <= --------- < - )
                               5*(1<<33)   5                                                           
             即: x=(uint64(y)*M)>>33;  其中魔法數M=((1<<33)+3)/5=1717986919=0x66666667;
     無符號數除以7:  uint32 x,y;   x=y/7;  
     推理 :略                                                         
          即:x=((uint64(y)*M)>>33+y)>>3; 其中魔法數M=((1<<35)+3)/7-(1<<32)=613566757=0x24924925;
     對於這種完全等價的替代,還有其他替代公式不再討論,對於有符號除法的替代演算法沒有討論,很多數都有完全等價的替代演算法,那些魔法數也是有規律可循的;有“進取心”的編譯器應該幫助使用者處理掉這個優化(vc6中就已經見到!  偷懶的辦法是直接看vc6生成的彙編碼:);
  8.對於已知被除數是除數的整數倍數的除法,能夠得到替代演算法;或改進的演算法;
    這裡只討論除數是常數的情況;
    比如對於(32位除法):x=y/a; //已知y是a的倍數,並假設a是奇數
    (如果a是偶數,先轉化為a=a0*(1<<k); y/a==(y/a0)>>k;a0為奇數)
    求得a',使 (a'*a)  % (1<<32) =  1;//利用a為奇數,y是a的倍數可以推匯出a'的存在性
    那麼: x=y/a  => x=(y/a)*((a*a')  %(1<<32))  =>  x=(y*a') % (1<<32);  //這裡並不需要實際做一個求餘指令   
    (該演算法可以同時支援有符號和無符號除法)
    比如   uint32  y; 
         uint32  x=y/7;   //已知y是7的倍數
    改成   uint32  x=(uint32)(y*M);   //其中M=(5*(1<<32)+1)/7

  9.近似計算除法 (該替代方法不等價)
     優化除數255的運算(257也可以,或者1023,1025等等)(1026也可以,推匯出的公式略有不同)
     比如顏色處理中 :  uint8 color=colorsum/255;
     改成:             uint8 color=colorsum/256;  也就是 color=colorsum>>8;
     這個誤差在顏色處理中很多時候都是可以接受的
     如果要減小誤差可以改成:     uint8 color=(colorsum+(colorsum>>8))>>8; 
       推導: x/255=(x+x/255)/(255+1)=(x+A)>>8; A=x/255;
       把A改成A=x>>8 (引入小的誤差);帶入公式就得到了: x/255約等於(x+(x>>8))>>8的公式
       同理可以有x/255約等於(x+(x>>8)+(x>>16))>>8等其它更精確的公式(請推匯出誤差項已確定是否精度足夠)
     
  10. 牛頓迭代法實現除法
     (很多CPU的內部除法指令就是用該方法或類似的方法實現的)
     假設有一個函式y=f(x);  求0=f(x)時,x的值;(這裡不討論有多個解的情況或逃逸或陷入死迴圈或陷入混沌狀態的問題)
     
                                 (參考圖片)
     求函式的導函式為 y=f'(x);   //可以這樣來理解這個函式的意思:x點處函式y=f(x)的斜率;
     a.取一個合適的x初始值x0; 並得到y0=f(x0);
     b.過(x0,y0)作一條斜率為f'(x0)的直線,與x軸交於x1;
     c.然後用得到的x1作為初始值,進行迭代;
     當進行足夠多次的迭代以後,認為x1將會非常接近於方程0=f(x)的解,這就是牛頓迭代;
     把上面的過程化簡,得到牛頓迭代公式: x(n+1)=x(n)-y(x(n))/y'(x(n))
     
     這裡給出利用牛頓迭代公式求倒數的方法; (用倒數得到除法: y = x/a = x* (1/a) )
        求1/a,   
        令a=1/x;  有方程 y=a-1/x;
        求導得y'=1/x^2;
        代入牛頓迭代方程 x(n+1)=x(n)-y(x(n))/y'(x(n));
        有迭代式 x_next=(2-a*x)*x; //可證明:該公式為2階收斂公式;   也就是說計算出的解的有效精度成倍增長
       
     證明收斂性:令x=(1/a)+dx;  //dx為一個很小的量
     則有x_next-(1/a)=(2-a*(1/a+dx))*(1/a+dx)-1/a
                     =(-a)*dx^2  //^表示指數運算子
      證畢.
    
    程式可以用該方法來實現除法,並按照自己的精度要求來決定迭代次數;
    (初始值的選取不再討論)
    附錄: 用牛頓迭代法來實現開方運算   
       //開方運算可以表示為 y=x^0.5=1/(1/x^0.5); 先求1/x^0.5
       求1/a^0.5,   
       令a=1/x^2;  有方程y=a-1/x^2;
       求導得y'=2/x^3;
       代入牛頓方程 x(n+1)=x(n)-y(x(n))/y'(x(n));
       有迭代式 x_next=(3-a*x*x)*x*0.5; //可證明:該公式為2階收斂公式  //方法同上 證明過程略

  11. 用快速乘法器實現快速除法
     這是存在的一種快速除法演算法,當前最好用硬體實現,請查閱相關資料


如何優化C語言程式碼

1、選擇合適的演算法和資料結構 
  應該熟悉演算法語言,知道各種演算法的優缺點,具體資料請參見相應的參考資料,有很多計算機書籍上都有介紹。將比較慢的順序查詢法用較快的二分查詢或亂序查詢法代替,插入排序或氣泡排序法用快速排序、合併排序或根排序代替,都可以大大提高程式執行的效率。.選擇一種合適的資料結構也很重要,比如你在一堆隨機存放的數中使用了大量的插入和刪除指令,那使用連結串列要快得多。陣列與指標語句具有十分密碼的關係,一般來說,指標比較靈活簡潔,而陣列則比較直觀,容易理解。對於大部分的編譯器,使用指標比使用陣列生成的程式碼更短,執行效率更高。但是在Keil中則相反,使用陣列比使用的指標生成的程式碼更短。。

  2。。。

  3、使用盡量小的資料型別 
能夠使用字元型(char)定義的變數,就不要使用整型(int)變數來定義;能夠使用整型變數定義的變數就不要用長整型(long int),能不使用浮點型(float)變數就不要使用浮點型變數。當然,在定義變數後不要超過變數的作用範圍,如果超過變數的範圍賦值,C編譯器並不報錯,但程式執行結果卻錯了,而且這樣的錯誤很難發現。 
在ICCAVR中,可以在Options中設定使用printf引數,儘量使用基本型引數(%c、 
%d、%x、%X、%u和%s格式說明符),少用長整型引數(%ld、%lu、%lx和%lX格式說明 符),至於浮點型的引數(%f)則儘量不要使用,其它C編譯器也一樣。在其它條件不 變的情況下,使用%f引數,會使生成的程式碼的數量增加很多,執行速度降低。

  4、使用自加、自減指令 
  通常使用自加、自減指令和複合賦值表示式(如a-=1及a+=1等)都能夠生成高質量的
程式程式碼,編譯器通常都能夠生成inc和dec之類的指令,而使用a=a+1或a=a-1之類
的指令,有很多C編譯器都會生成二到三個位元組的指令。在AVR單片適用的ICCAVR、 GCCAVR、IAR等C編譯器以上幾種書寫方式生成的程式碼是一樣的,也能夠生成高質量 的inc和dec之類的的程式碼。

  5、減少運算的強度 
  可以使用運算量小但功能相同的表示式替換原來複雜的的表示式。如下:

(1)、求餘運算。 
a=a%8; 
可以改為: 
a=a&7; 
說明:位操作只需一個指令週期即可完成,而大部分的C編譯器的“%”運算均是調 用子程式來完成,程式碼長、執行速度慢。通常,只要求是求2n方的餘數,均可使用 位操作的方法來代替。

(2)、平方運算 
a=pow(a,2.0); 
可以改為: 
a=a*a; 
說明:在有內建硬體乘法器的微控制器中(如51系列),乘法運算比求平方運算快得多,因為浮點數的求平方是通過呼叫子程式來實現的,在自帶硬體乘法器的AVR微控制器中,如ATMega163中,乘法運算只需2個時鐘週期就可以完成。既使是在沒有內建 硬體乘法器的AVR微控制器中,乘法運算的子程式比平方運算的子程式程式碼短,執行速度快。 
如果是求3次方,如: 
a=pow(a,3.0); 
更改為: 
a=a*a*a; 
則效率的改善更明顯。

(3)、用移位實現乘除法運算 
a=a*4; 
b=b/4; 
可以改為: 
a=a<<2; 
b=b>>2; 
說明:通常如果需要乘以或除以2n,都可以用移位的方法代替。在ICCAVR中,如果 乘以2n,都可以生成左移的程式碼,而乘以其它的整數或除以任何數,均呼叫乘除法 子程式。用移位的方法得到程式碼比呼叫乘除法子程式生成的程式碼效率高。實際上, 只要是乘以或除以一個整數,均可以用移位的方法得到結果,如: 
a=a*9 
可以改為: 
a=(a<<3)+a

  6、迴圈

(1)、迴圈語 
對於一些不需要迴圈變數參加運算的任務可以把它們放到迴圈外面,這裡的任務包括表示式、函式的呼叫、指標運算、陣列訪問等,應該將沒有必要執行多次的操作全部集合在一起,放到一個init的初始化程式中進行。

(2)、延時函式: 
通常使用的延時函式均採用自加的形式: 
void delay (void) 

unsigned int i; 
for (i=0;i<1000;i++) 


將其改為自減延時函式: 
void delay (void) 

unsigned int i; 
for (i=1000;i>0;i--) 


  兩個函式的延時效果相似,但幾乎所有的C編譯對後一種函式生成的程式碼均比前一種程式碼少1~3個位元組,因為幾乎所有的MCU均有為0轉移的指令,採用後一種方式能夠生成這類指令。在使用while迴圈時也一樣,使用自減指令控制迴圈會比使用自加指令控制迴圈生成的程式碼更少1~3個字母。但是在迴圈中有通過迴圈變數“i”讀寫陣列的指令時,使用預減迴圈時有可能使陣列超界,要引起注意。

(3)while迴圈和do…while迴圈 
用while迴圈時有以下兩種迴圈形式: 
unsigned int i; 
i=0; 
while (i<1000) 

i++; 
//使用者程式 

或: 
unsigned int i; 
i=1000; 
do 
i--; 
//使用者程式 
while (i>0); 
在這兩種迴圈中,使用do…while迴圈編譯後生成的程式碼的長度短於while迴圈。

  7、查表 
  在程式中一般不進行非常複雜的運算,如浮點數的乘除及開方等,以及一些複雜的數學模型的插補運算,對這些即消耗時間又消費資源的運算,應儘量使用查表的方式,並且將資料表置於程式儲存區。如果直接生成所需的表比較困難,也儘量在啟了,減少了程式執行過程中重複計算的工作量。

  8、其它 
  比如使用線上彙編及將字串和一些常量儲存在程式儲存器中,均有利於優化 

編寫高效簡潔的C語言程式碼,是許多軟體工程師追求的目標。本文就工作中的一些體會和經驗做相關的闡述,不對的地方請各位指教。

  第1招:以空間換時間

  計算機程式中最大的矛盾是空間和時間的矛盾,那麼,從這個角度出發逆向思維來考慮程式的效率問題,我們就有了解決問題的第1招——以空間換時間。 
例如:字串的賦值。 
方法A,通常的辦法:

#define LEN 32
char string1 [LEN];
memset (string1,0,LEN);
strcpy (string1,“This is a example!!”);
方法B:
const char string2[LEN] =“This is a example!”;
char * cp;
cp = string2 ;

  (使用的時候可以直接用指標來操作。)

  從上面的例子可以看出,A和B的效率是不能比的。在同樣的儲存空間下,B直接使用指標就可以操作了,而A需要呼叫兩個字元函式才能完成。B的缺點在於靈活性沒有A好。在需要頻繁更改一個字串內容的時候,A具有更好的靈活性;如果採用方法B,則需要預存許多字串,雖然佔用了大量的記憶體,但是獲得了程式執行的高效率。 
如果系統的實時性要求很高,記憶體還有一些,那我推薦你使用該招數。

  該招數的變招——使用巨集函式而不是函式。舉例如下: 
方法C:

#define bwMCDR2_ADDRESS 4
#define bsMCDR2_ADDRESS 17
int BIT_MASK(int __bf)
{
return ((1U << (bw ## __bf)) - 1) << (bs ## __bf);
}
void SET_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:
#define bwMCDR2_ADDRESS 4
#define bsMCDR2_ADDRESS 17
#define bmMCDR2_ADDRESS BIT_MASK(MCDR2_ADDRESS)
#define BIT_MASK(__bf) (((1U<<(bw ## __bf))-1)<< (bs ## __bf))
#define SET_BITS(__dst, __bf, __val) /
((__dst) = ((__dst) & ~(BIT_MASK(__bf))) | /
(((__val) << (bs ## __bf)) & (BIT_MASK(__bf))))

SET_BITS(MCDR2, MCDR2_ADDRESS, RegisterNumber);

  函式和巨集函式的區別就在於,巨集函式佔用了大量的空間,而函式佔用了時間。大家要知道的是,函式呼叫是要使用系統的棧來儲存資料的,如果編譯器裡有棧檢查選項,一般在函式的頭會嵌入一些彙編語句對當前棧進行檢查;同時,CPU也要在函式呼叫時儲存和恢復當前的現場,進行壓棧和彈棧操作,所以,函式呼叫需要一些CPU時間。而巨集函式不存在這個問題。巨集函式僅僅作為預先寫好的程式碼嵌入到當前程式,不會產生函式呼叫,所以僅僅是佔用了空間,在頻繁呼叫同一個巨集函式的時候,該現象尤其突出。

  D方法是我看到的最好的置位操作函式,是ARM公司原始碼的一部分,在短短的三行內實現了很多功能,幾乎涵蓋了所有的位操作功能。C方法是其變體,其中滋味還需大家仔細體會。

  第2招:數學方法解決問題

  現在我們演繹高效C語言編寫的第二招——採用數學方法來解決問題。

  數學是計算機之母,沒有數學的依據和基礎,就沒有計算機的發展,所以在編寫程式的時候,採用一些數學方法會對程式的執行效率有數量級的提高。 
舉例如下,求 1~100的和。 
方法E 
int I , j; 
for (I = 1 ;I<=100; I ++){ 
j += I; 

方法F 
int I; 
I = (100 * (1+100)) / 2

  這個例子是我印象最深的一個數學用例,是我的計算機啟蒙老師考我的。當時我只有小學三年級,可惜我當時不知道用公式 N×(N+1)/ 2 來解決這個問題。方法E迴圈了100次才解決問題,也就是說最少用了100個賦值,100個判斷,200個加法(I和j);而方法F僅僅用了1個加法,1 次乘法,1次除法。效果自然不言而喻。所以,現在我在程式設計序的時候,更多的是動腦筋找規律,最大限度地發揮數學的威力來提高程式執行的效率。

  第3招:使用位操作

  實現高效的C語言編寫的第三招——使用位操作,減少除法和取模的運算。

  在計算機程式中,資料的位是可以操作的最小資料單位,理論上可以用“位運算”來完成所有的運算和操作。一般的位操作是用來控制硬體的,或者做資料變換使用,但是,靈活的位操作可以有效地提高程式執行的效率。舉例如下:

 方法G int I,J; I = 257 /8; J = 456 % 32; 方法H int I,J; I = 257 >>3; J = 456 - (456 >> 4 << 4);     在字面上好像H比G麻煩了好多,但是,仔細檢視產生的彙編程式碼就會明白,方法G呼叫了基本的取模函式和除法函式,既有函式呼叫,還有很多彙編程式碼和暫存器參與運算;而方法H則僅僅是幾句相關的彙編,程式碼更簡潔,效率更高。當然,由於編譯器的不同,可能效率的差距不大,但是,以我目前遇到的MS C ,ARM C 來看,效率的差距還是不小。相關彙編程式碼就不在這裡列舉了。 運用這招需要注意的是,因為CPU的不同而產生的問題。比如說,在PC上用這招編寫的程式,並在PC上除錯通過,在移植到一個16位機平臺上的時候,可能會產生程式碼隱患。所以只有在一定技術進階的基礎下才可以使用這招。        第4招:彙編嵌入   高效C語言程式設計的必殺技,第四招——嵌入彙編。   “在熟悉組合語言的人眼裡,C語言編寫的程式都是垃圾”。這種說法雖然偏激了一些,但是卻有它的道理。組合語言是效率最高的計算機語言,但是,不可能*著它來寫一個作業系統吧?所以,為了獲得程式的高效率,我們只好採用變通的方法 ——嵌入彙編,混合程式設計。   舉例如下,將陣列一賦值給陣列二,要求每一位元組都相符。char string1[1024],string2[1024];方法Iint 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,string1MOV R1,string2MOV R2,#0loop:LDMIA R0!, [R3-R11]STMIA R1!, [R3-R11]ADD R2,R2,#8CMP R2, #400BNE loop}#endif    方法I是最常見的方法,使用了1024次迴圈;方法J則根據平臺不同做了區分,在ARM平臺下,用嵌入彙編僅用128次迴圈就完成了同樣的操作。這裡有朋友會說,為什麼不用標準的記憶體拷貝函式呢?這是因為在源資料裡可能含有資料為0的位元組,這樣的話,標準庫函式會提前結束而不會完成我們要求的操作。這個例程典型應用於LCD資料的拷貝過程。根據不同的CPU,熟練使用相應的嵌入彙編,可以大大提高程式執行的效率。  雖然是必殺技,但是如果輕易可能使用會付出慘重的代價。這是因為,使用了嵌入彙編,便限制了程式的可移植性,使程式在不同平臺移植的過程中,臥虎藏龍。