程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++代碼優化方法(1)

C++代碼優化方法(1)

編輯:C++入門知識
在C++層次進行優化,比在匯編層次優化具有更好的移植性,應該是優化中的首選做法。
  
  確定浮點型變量和表達式是 float 型
  
  為了讓編譯器產生更好的代碼(比如說產生3DNow! 或SSE指令的代碼),必須確定浮點型變量和表達式是 float 型的。要非凡注重的是,以 ";F"; 或 ";f"; 為後綴(比如:3.14f)的浮點常量才是 float 型,否則默認是 double 型。為了避免 float 型參數自動轉化為 double,請在函數聲明時使用 float。
  
  使用32位的數據類型
  
  編譯器有很多種,但它們都包含的典型的32位類型是:int,signed,signed int,unsigned,unsigned int,long,signed long,long int,signed long int,unsigned long,unsigned long int。盡量使用32位的數據類型,因為它們比16位的數據甚至8位的數據更有效率。
  
  明智使用有符號整型變量
  
  在很多情況下,你需要考慮整型變量是有符號還是無符號類型的。比如,保存一個人的體重數據時不可能出現負數,所以不需要使用有符號類型。但是,假如是要保存溫度數據,就必須使用到有符號的變量。
  
  在許多地方,考慮是否使用有符號的變量是必要的。在一些情況下,有符號的運算比較快;但在一些情況下卻相反。比如:整型到浮點轉化時,使用大於16位的有符號整型比較快。因為x86構架中提供了從有符號整型轉化到浮點型的指令,但沒有提供從無符號整型轉化到浮點的指令。看看編譯器產生的匯編代碼,不好的代碼:
  
  編譯前 編譯後
  
   double x; mov [foo + 4], 0
  
  unsigned int i; mov eax, i
  
  x = i; mov [foo], eax
  
  flid qWord ptr [foo]
  
  fstp qword ptr [x]
  
  上面的代碼比較慢。不僅因為指令數目比較多,而且由於指令不能配對造成的FLID指令被延遲執行。最好用以下代碼代替,推薦的代碼:
  
  編譯前 編譯後
  
   double x; fild dword ptr [i]
  
  int i; fstp qword ptr [x]
  
  x = i;
  
  在整數運算中計算商和余數時,使用無符號類型比較快。以下這段典型的代碼是編譯器產生的32位整型數除以4的代碼,不好的代碼 推薦的代碼
  
  編譯前 編譯後
  
   int i; mov eax, i
  
  i = i / 4; cdq
  
  and edx, 3
  
  add eax, edx
  
  sar eax, 2
  
  mov i, eax
  
  編譯前 編譯後
  
   unsigned int i; shr i, 2
  
  i = i / 4;
  
  總結:
  
  無符號類型用於:
  
  除法和余數
  
  循環計數
  
  數組下標
  
  有符號類型用於:
  
  整型到浮點的轉化
  
  while VS. for
  
  在編程中,我們經常需要用到無限循環,常用的兩種方法是while (1) 和 for (;;)。這兩種方法效果完全一樣,但那一種更好呢?然我們看看它們編譯後的代碼:
  
  編譯前 編譯後
  
   while (1); mov eax,1
  
  test eax,eax
  
  je foo+23h
  
  jmp foo+18h
  
  編譯前 編譯後
  
   for (;;); jmp foo+23h
  
  for (;;)指令少,不占用寄存器,而且沒有判定跳轉,比while (1)好。 使用數組型代替指針型 使用指針會使編譯器很難優化它。因為缺乏有效的指針代碼優化的方法,編譯器總是假設指針可以訪問內存的任意地方,包括分配給其他變量的儲存空間。所以為了編譯器產生優化得更好的代碼,要避免在不必要的地方使用指針。一個典型的例子是訪問存放在數組中的數據。C++ 答應使用操作符 [] 或指針來訪問數組,使用數組型代碼會讓優化器減少產生不安全代碼的可能性。比如,x[0] 和x[2] 不可能是同一個內存地址,但 *p 和 *q 可能。強烈建議使用數組型,因為這樣可能會有意料之外的性能提升。
  
  不好的代碼 推薦的代碼 typedef strUCt
  
  {
  
  float x,y,z,w;
  
  } VERTEX;
  
  typedef struct
  
  {
  
  float m[4][4];
  
  } MATRIX;
  
  void XForm(float* res, const float* v, const float* m, int nNumVerts)
  
  {
  
  float dp;
  
  int i;
  
   const VERTEX* vv = (VERTEX *)v;
  
   for (i = 0; i <; nNumVerts; i++)
  
  {
  
  dp = vv->;x * *m ++;
  
  dp += vv->;y * *m ++;
  
  dp += vv->;z * *m ++;
  
  dp += vv->;w * *m ++;
  
  *res ++ = dp;// 寫入轉換了的 x
  
  dp = vv->;x * *m ++;
  
  dp += vv->;y * *m ++;
  
  dp += vv->;z * *m ++;
  
  dp += vv->;w * *m ++;
  
  *res ++ = dp; // 寫入轉換了的 y
  
  dp = vv->;x * *m ++;
  
  dp += vv->;y * *m ++;
  
  dp += vv->;z * *m ++;
  
  dp += vv->;w * *m ++;
  
  *res ++ = dp;// 寫入轉換了的 z
  
  dp = vv->;x * *m ++;
  
  dp += vv->;y * *m ++;
  
  dp += vv->;z * *m ++;
  
  dp += vv->;w * *m ++;
  
  *res ++ = dp;// 寫入轉換了的 w
  
  vv ++;  // 下一個矢量
  
  m -= 16;
  
  }
  
  }
  
  typedef struct
  
  {
  
  float x,y,z,w;
  
  } VERTEX;
  
  typedef struct
  
  {
  
  float m[4][4];
  
  } MATRIX;
  
  void XForm (float* res, const float* v, const float* m, int nNumVerts)
  
  {
  
  int i;
  
  const VERTEX* vv = (VERTEX*)v;
  
  const MATRIX* mm = (MATRIX*)m;
  
  VERTEX* rr = (VERTEX*)res;
  
  for (i = 0; i <; nNumVerts; i++)
  
  {
  
  rr->;x = vv->;x * mm->;m[0][0] + vv->;y * mm->;m[0][1]
  
  + vv->;z * mm->;m[0][2] + vv->;w * mm->;m[0][3];
  
  rr->;y = vv->;x * mm->;m[1][0] + vv->;y * mm->;m[1][1]
  
  + vv->;z * mm->;m[1][2] + vv->;w * mm->;m[1][3];
  
  rr->;z = vv->;x * mm->;m[2][0] + vv->;y * mm->;m[2][1]
  
  + vv->;z * mm->;m[2][2] + vv->;w * mm->;m[2][3];
  
  rr->;w = vv->;x * mm->;m[3][0] + vv->;y * mm->;m[3][1]
  
  + vv->;z * mm->;m[3][2] + vv->;w * mm->;m[3][3];
  
  }
  
  }
  
  
  注重: 源代碼的轉化是與編譯器的代碼發生器相結合的。從源代碼層次很難控制產生的機器碼。依靠編譯器和非凡的源代碼,有可能指針型代碼編譯成的機器碼比同等條件下的數組型代碼運行速度更快。明智的做法是在源代碼轉化後檢查性能是否真正提高了,再選擇使用指針型還是數組型。 在C++層次進行優化,比在匯編層次優化具有更好的移植性,應該是優化中的首選做法。
  
  確定浮點型變量和表達式是 float 型
  
  為了讓編譯器產生更好的代碼(比如說產生3DNow! 或SSE指令的代碼),必須確定浮點型變量和表達式是 float 型的。要非凡注重的是,以 ";F"; 或 ";f"; 為後綴(比如:3.14f)的浮點常量才是 float 型,否則默認是 double 型。為了避免 float 型參數自動轉化為 double,請在函數聲明時使用 float。
  
  使用32位的數據類型
  
  編譯器有很多種,但它們都包含的典型的32位類型是:int,signed,signed int,unsigned,unsigned int,long,signed long,long int,signed long int,unsigned long,unsigned long int。盡量使用32位的數據類型,因為它們比16位的數據甚至8位的數據更有效率。
  
  明智使用有符號整型變量
  
  在很多情況下,你需要考慮整型變量是有符號還是無符號類型的。比如,保存一個人的體重數據時不可能出現負數,所以不需要使用有符號類型。但是,假如是要保存溫度數據,就必須使用到有符號的變量。
  
  在許多地方,考慮是否使用有符號的變量是必要的。在一些情況下,有符號的運算比較快;但在一些情況下卻相反。比如:整型到浮點轉化時,使用大於16位的有符號整型比較快。因為x86構架中提供了從有符號整型轉化到浮點型的指令,但沒有提供從無符號整型轉化到浮點的指令。看看編譯器產生的匯編代碼,不好的代碼:
  
  編譯前 編譯後
  
   double x; mov [foo + 4], 0
  
  unsigned int i; mov eax, i
  
  x = i; mov [foo], eax
  
  flid qword ptr [foo]
  
  fstp qword ptr [x]
  
  上面的代碼比較慢。不僅因為指令數目比較多,而且由於指令不能配對造成的FLID指令被延遲執行。最好用以下代碼代替,推薦的代碼:
  
  編譯前 編譯後
  
   double x; fild dword ptr [i]
  
  int i; fstp qword ptr [x]
  
  x = i;
  
  在整數運算中計算商和余數時,使用無符號類型比較快。以下這段典型的代碼是編譯器產生的32位整型數除以4的代碼,不好的代碼 推薦的代碼
  
  編譯前 編譯後
  
   int i; mov eax, i
  
  i = i / 4; cdq
  
  and edx, 3
  
  add eax, edx
  
  sar eax, 2
  
  mov i, eax
  
  編譯前 編譯後
  
   unsigned int i; shr i, 2
  
  i = i / 4;
  
  總結:
  
  無符號類型用於:
  
  除法和余數
  
  循環計數
  
  數組下標
  
  有符號類型用於:
  
  整型到浮點的轉化
  
  while VS. for
  
  在編程中,我們經常需要用到無限循環,常用的兩種方法是while (1) 和 for (;;)。這兩種方法效果完全一樣,但那一種更好呢?然我們看看它們編譯後的代碼:
  
  編譯前 編譯後
  
   while (1); mov eax,1
  
  test eax,eax
  
  je foo+23h
  
  jmp foo+18h
  
  編譯前 編譯後
  
   for (;;); jmp foo+23h
  
  for (;;)指令少,不占用寄存器,而且沒有判定跳轉,比while (1)好。 使用數組型代替指針型 使用指針會使編譯器很難優化它。因為缺乏有效的指針代碼優化的方法,編譯器總是假設指針可以訪問內存的任意地方,包括分配給其他變量的儲存空間。所以為了編譯器產生優化得更好的代碼,要避免在不必要的地方使用指針。一個典型的例子是訪問存放在數組中的數據。C++ 答應使用操作符 [] 或指針來訪問數組,使用數組型代碼會讓優化器減少產生不安全代碼的可能性。比如,x[0] 和x[2] 不可能是同一個內存地址,但 *p 和 *q 可能。強烈建議使用數組型,因為這樣可能會有意料之外的性能提升。
  
  不好的代碼 推薦的代碼 typedef struct
  
  {
  
  float x,y,z,w;
  
  } VERTEX;
  
  typedef struct
  
  {
  
  float m[4][4];
  
  } MATRIX;
  
  void XForm(float* res, const float* v, const float* m, int nNumVerts)
  
  {
  
  float dp;
  
  int i;
  
   const VERTEX* vv = (VERTEX *)v;
  
   for (i = 0; i <; nNumVerts; i++)
  
  {
  
  dp = vv->;x * *m ++;
  
  dp += vv->;y * *m ++;
  
  dp += vv->;z * *m ++;
  
  dp += vv->;w * *m ++;
  
  *res ++ = dp;// 寫入轉換了的 x
  
  dp = vv->;x * *m ++;
  
  dp += vv->;y * *m ++;
  
  dp += vv->;z * *m ++;
  
  dp += vv->;w * *m ++;
  
  *res ++ = dp; // 寫入轉換了的 y
  
  dp = vv->;x * *m ++;
  
  dp += vv->;y * *m ++;
  
  dp += vv->;z * *m ++;
  
  dp += vv->;w * *m ++;
  
  *res ++ = dp;// 寫入轉換了的 z
  
  dp = vv->;x * *m ++;
  
  dp += vv->;y * *m ++;
  
  dp += vv->;z * *m ++;
  
  dp += vv->;w * *m ++;
  
  *res ++ = dp;// 寫入轉換了的 w
  
  vv ++;  // 下一個矢量
  
  m -= 16;
  
  }
  
  }
  
  typedef struct
  
  {
  
  float x,y,z,w;
  
  } VERTEX;
  
  typedef struct
  
  {
  
  float m[4][4];
  
  } MATRIX;
  
  void XForm (float* res, const float* v, const float* m, int nNumVerts)
  
  {
  
  int i;
  
  const VERTEX* vv = (VERTEX*)v;
  
  const MATRIX* mm = (MATRIX*)m;
  
  VERTEX* rr = (VERTEX*)res;
  
  for (i = 0; i <; nNumVerts; i++)
  
  {
  
  rr->;x = vv->;x * mm->;m[0][0] + vv->;y * mm->;m[0][1]
  
  + vv->;z * mm->;m[0][2] + vv->;w * mm->;m[0][3];
  
  rr->;y = vv->;x * mm->;m[1][0] + vv->;y * mm->;m[1][1]
  
  + vv->;z * mm->;m[1][2] + vv->;w * mm->;m[1][3];
  
  rr->;z = vv->;x * mm->;m[2][0] + vv->;y * mm->;m[2][1]
  
  + vv->;z * mm->;m[2][2] + vv->;w * mm->;m[2][3];
  
  rr->;w = vv->;x * mm->;m[3][0] + vv->;y * mm->;m[3][1]
  
  + vv->;z * mm->;m[3][2] + vv->;w * mm->;m[3][3];
  
  }
  
  }
  
  
  注重: 源代碼的轉化是與編譯器的代碼發生器相結合的。從源代碼層次很難控制產生的機器碼。依靠編譯器和非凡的源代碼,有可能指針型代碼編譯成的機器碼比同等條件下的數組型代碼運行速度更快。明智的做法是在源代碼轉化後檢查性能是否真正提高了,再選擇使用指針型還是數組型。 在C++層次進行優化,比在匯編層次優化具有更好的移植性,應該是優化中的首選做法。
  
  確定浮點型變量和表達式是 float 型
  
  為了讓編譯器產生更好的代碼(比如說產生3DNow! 或SSE指令的代碼),必須確定浮點型變量和表達式是 float 型的。要非凡注重的是,以 ";F"; 或 ";f"; 為後綴(比如:3.14f)的浮點常量才是 float 型,否則默認是 double 型。為了避免 float 型參數自動轉化為 double,請在函數聲明時使用 float。
  
  使用32位的數據類型
  
  編譯器有很多種,但它們都包含的典型的32位類型是:int,signed,signed int,unsigned,unsigned int,long,signed long,long int,signed long int,unsigned long,unsigned long int。盡量使用32位的數據類型,因為它們比16位的數據甚至8位的數據更有效率。
  
  明智使用有符號整型變量
  
  在很多情況下,你需要考慮整型變量是有符號還是無符號類型的。比如,保存一個人的體重數據時不可能出現負數,所以不需要使用有符號類型。但是,假如是要保存溫度數據,就必須使用到有符號的變量。
  
  在許多地方,考慮是否使用有符號的變量是必要的。在一些情況下,有符號的運算比較快;但在一些情況下卻相反。比如:整型到浮點轉化時,使用大於16位的有符號整型比較快。因為x86構架中提供了從有符號整型轉化到浮點型的指令,但沒有提供從無符號整型轉化到浮點的指令。看看編譯器產生的匯編代碼,不好的代碼:
  
  編譯前 編譯後
  
   double x; mov [foo + 4], 0
  
  unsigned int i; mov eax, i
  
  x = i; mov [foo], eax
  
  flid qword ptr [foo]
  
  fstp qword ptr [x]
  
  上面的代碼比較慢。不僅因為指令數目比較多,而且由於指令不能配對造成的FLID指令被延遲執行。最好用以下代碼代替,推薦的代碼:
  
  編譯前 編譯後
  
   double x; fild dword ptr [i]
  
  int i; fstp qword ptr [x]
  
  x = i;
  
  在整數運算中計算商和余數時,使用無符號類型比較快。以下這段典型的代碼是編譯器產生的32位整型數除以4的代碼,不好的代碼 推薦的代碼
  
  編譯前 編譯後
  
   int i; mov eax, i
  
  i = i / 4; cdq
  
  and edx, 3
  
  add eax, edx
  
  sar eax, 2
  
  mov i, eax
  
  編譯前 編譯後
  
   unsigned int i; shr i, 2
  
  i = i / 4;
  
  總結:
  
  無符號類型用於:
  
  除法和余數
  
  循環計數
  
  數組下標
  
  有符號類型用於:
  
  整型到浮點的轉化
  
  while VS. for
  
  在編程中,我們經常需要用到無限循環,常用的兩種方法是while (1) 和 for (;;)。這兩種方法效果完全一樣,但那一種更好呢?然我們看看它們編譯後的代碼:
  
  編譯前 編譯後
  
   while (1); mov eax,1
  
  test eax,eax
  
  je foo+23h
  
  jmp foo+18h
  
  編譯前 編譯後
  
   for (;;); jmp foo+23h
  
  for (;;)指令少,不占用寄存器,而且沒有判定跳轉,比while (1)好。 使用數組型代替指針型 使用指針會使編譯器很難優化它。因為缺乏有效的指針代碼優化的方法,編譯器總是假設指針可以訪問內存的任意地方,包括分配給其他變量的儲存空間。所以為了編譯器產生優化得更好的代碼,要避免在不必要的地方使用指針。一個典型的例子是訪問存放在數組中的數據。C++ 答應使用操作符 [] 或指針來訪問數組,使用數組型代碼會讓優化器減少產生不安全代碼的可能性。比如,x[0] 和x[2] 不可能是同一個內存地址,但 *p 和 *q 可能。強烈建議使用數組型,因為這樣可能會有意料之外的性能提升。
  
  不好的代碼 推薦的代碼 typedef struct
  
  {
  
  float x,y,z,w;
  
  } VERTEX;
  
  typedef struct
  
  {
  
  float m[4][4];
  
  } MATRIX;
  
  void XForm(float* res, const float* v, const float* m, int nNumVerts)
  
  {
  
  float dp;
  
  int i;
  
   const VERTEX* vv = (VERTEX *)v;
  
   for (i = 0; i <; nNumVerts; i++)
  
  {
  
  dp = vv->;x * *m ++;
  
  dp += vv->;y * *m ++;
  
  dp += vv->;z * *m ++;
  
  dp += vv->;w * *m ++;
  
  *res ++ = dp;// 寫入轉換了的 x
  
  dp = vv->;x * *m ++;
  
  dp += vv->;y * *m ++;
  
  dp += vv->;z * *m ++;
  
  dp += vv->;w * *m ++;
  
  *res ++ = dp; // 寫入轉換了的 y
  
  dp = vv->;x * *m ++;
  
  dp += vv->;y * *m ++;
  
  dp += vv->;z * *m ++;
  
  dp += vv->;w * *m ++;
  
  *res ++ = dp;// 寫入轉換了的 z
  
  dp = vv->;x * *m ++;
  
  dp += vv->;y * *m ++;
  
  dp += vv->;z * *m ++;
  
  dp += vv->;w * *m ++;
  
  *res ++ = dp;// 寫入轉換了的 w
  
  vv ++;  // 下一個矢量
  
  m -= 16;
  
  }
  
  }
  
  typedef struct
  
  {
  
  float x,y,z,w;
  
  } VERTEX;
  
  typedef struct
  
  {
  
  float m[4][4];
  
  } MATRIX;
  
  void XForm (float* res, const float* v, const float* m, int nNumVerts)
  
  {
  
  int i;
  
  const VERTEX* vv = (VERTEX*)v;
  
  const MATRIX* mm = (MATRIX*)m;
  
  VERTEX* rr = (VERTEX*)res;
  
  for (i = 0; i <; nNumVerts; i++)
  
  {
  
  rr->;x = vv->;x * mm->;m[0][0] + vv->;y * mm->;m[0][1]
  
  + vv->;z * mm->;m[0][2] + vv->;w * mm->;m[0][3];
  
  rr->;y = vv->;x * mm->;m[1][0] + vv->;y * mm->;m[1][1]
  
  + vv->;z * mm->;m[1][2] + vv->;w * mm->;m[1][3];
  
  rr->;z = vv->;x * mm->;m[2][0] + vv->;y * mm->;m[2][1]
  
  + vv->;z * mm->;m[2][2] + vv->;w * mm->;m[2][3];
  
  rr->;w = vv->;x * mm->;m[3][0] + vv->;y * mm->;m[3][1]
  
  + vv->;z * mm->;m[3][2] + vv->;w * mm->;m[3][3];
  
  }
  
  }
  
  
  注重: 源代碼的轉化是與編譯器的代碼發生器相結合的。從源代碼層次很難控制產生的機器碼。依靠編譯器和非凡的源代碼,有可能指針型代碼編譯成的機器碼比同等條件下的數組型代碼運行速度更快。明智的做法是在源代碼轉化後檢查性能是否真正提高了,再選擇使用指針型還是數組型。
 
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved