C++代码优化方法(1)

2016-02-19 18:06 0 1 收藏

只要你有一台电脑或者手机,都能关注图老师为大家精心推荐的C++代码优化方法(1),手机电脑控们准备好了吗?一起看过来吧!

【 tulaoshi.com - 编程语言 】

在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];
  
  }
  
  }
  
  
  注重: 源代码的转化是与编译器的代码发生器相结合的。从源代码层次很难控制产生的机器码。依靠编译器和非凡的源代码,有可能指针型代码编译成的机器码比同等条件下的数组型代码运行速度更快。明智的做法是在源代码转化后检查性能是否真正提高了,再选择使用指针型还是数组型。

来源:https://www.tulaoshi.com/n/20160219/1616798.html

延伸阅读
一直以来都想写一点为BCB初学者快速入门的东西,前不久写了几篇《闲谈BCB》想把自己学习BCB中如何来解决难点的方法说给大家,没想到被骂得不成样子。本想不写了,但觉得这些东西留下来能做什么呢?还是用另一种方法来重新演译我的思维吧,最近有些忙,那几篇没有写完的文章,我也会尽快写完的,至于《深入QR》,我想我一定会用另一种手法来...
第一篇-正确书写代码 1. 简介 2. 书写干净的代码 3. 使用异常及异常处理能力 4. 使用记录(logging)机制 5. 结合使用记录机制与类的异常处理机制 6. 处理您代码外产生的异常 7. 你的回合 8. 版权说明 简介 这篇文章,我将从...
一个由C/C++编译的程序占用的内存分为以下几个部分 1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数名,局部变量的名等。其操作方式类似于数据结构中的栈。 2、堆区(heap)— 由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。 3、全局区(静态区)(static...
C++
/*p129*/#include class CComplex{public: CCpomplex(double r=0,double i=0) { realPart=r; imagePart=i; } void print() { cout /*p129*/#include class CComplex{public: CCpomplex(double r=0,double i=0) { realPart=r; imagePart=i; } void print() { cout
我们从一开始就一直在利用C++的输入输出在做着各种练习,输入输出是由iostream库提供的,所以讨论此标准库是有必要的,它与C语言的stdio库不同,它从一开始就是用多重继续与虚拟继续实现的面向对象的层次结构,作为一个c++的标准库组件提供给程序员使用。 !-- frame contents -- !-- /frame contents -- iostream为内置类型类...

经验教程

68

收藏

58
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部