The efficiency of different C++ constructs

简介: The efficiency of different C++ constructs

循环


循环的效率取决于微处理器对循环控制分支的预测能力。一个具有一个较小并且固定的重复计数,没有分支的循环,可以完美地被预测。



循环展开


展开前

int i;
for (i = 0; i < 20; i++)
{
  if (i % 2 == 0); 
    FuncA(i);
  else
    FuncB(i);
  FuncC(i);
}

展开后

int i;
for (i = 0; i < 20; i+=2)
{
    FuncA(i);
    FuncC(i);
    FuncB(i+1);
  FuncC(i+1);
}

这样做的好处:


  • 循环次数变成了10次而不是20次,CPU可以更完美的进行预测


  • if分支被消除,有利于编译器自动进行向量化等优化


循环展开的坏处:


  • 展开循环后在代码缓存中占用更多空间


  • 非常小的循环展开不如不展开


  • 如果重复计数为奇数,并将其展开为2, 则必须在循环之外执行额外的迭代。

只有在能够取得特定好处的情况下,才应该使用循环展开。如果一个循环包含浮点运算,且循环计数器是整数,那么通常可 以假设整个计算时间是由浮点代码决定的,而不是由循环控制分支决定的。在这种情况下,展开循环是没有任何好处的 。



循环控制条件


如果循环控制分支依赖于循环内部的计算,则效率较低。


确定最坏情况下的最大重复计数并始终使用此迭代次数的效率会更高。


循环计数器最好是整数。



复制或清除数组


对于诸如复制数组或将数组中的元素全部设置为零这样的琐碎任务,使用循环可能不是最佳选择。使用memset和memcpy函数通常会更快



函数


函数调用会让程序慢下来,因为


  • 代码地址跳转,可能需要4个时钟周期



  • 如果代码分散在内存中会降低代码缓存效率


  • 如果函数参数不够放在寄存器中,需要入到栈中,效率不高


  • 需要额外时间设置stack frame, 保存和恢复寄存器


  • 每个函数调用语句需要在分支目标缓冲区(BTB)中占用空间 , BTB争用可能会导致分支预测失败



如何避免函数调用降低效率呢?



避免不必要函数


不要过度封装


使用内联函数


如果函数很小,或者只在程序中的一个位置调用它,那么内联函数是有好处的。小函数通常由编译器自动内联


避免在最内层循环嵌套函数调用


如果程序关键的最内层循环包含对帧函数的调用,那么代码有可能通过内联帧函数或使帧函数调用的所有函数内联(把帧函数变为叶函数)来提升效率


使用宏代替函数


但是不要滥用宏,宏的问题是:名称不能重载或限制作用区域。宏将干扰具有相同名称的任何函数或变量,而与作用域或命名空间无关


使函数局部化


应该使同一个模块中使用的函数(即当前*.cpp* 文件)是局部的。 这使得编译器更容易将函数内联,并对函数调用进行优 化。


如何使函数局部化呢?


  • 对于非类成员函数,直接使用static


  • 对于类成员函数,将函数或类放置于匿名命名空间中


使用全程序优化


一些编译器具有对整个程序进行优化的选项,也可以选择将多个 .cpp 文件组合成一个对象文件。这使得编译器能够在组成程 序的所有 .cpp 模块之间优化寄存器分配和参数传递。


使用64位模式


现在服务器端开发都是64位模式了吧


相关文章
|
存储 缓存 并行计算
The efficiency of different C++ constructs5
The efficiency of different C++ constructs5
115 0
|
存储 缓存 编译器
The efficiency of different C++ constructs4
The efficiency of different C++ constructs4
98 0
|
缓存 安全 编译器
The efficiency of different C++ constructs3
The efficiency of different C++ constructs3
125 0
|
存储 缓存 Unix
The efficiency of different C++ constructs2
The efficiency of different C++ constructs2
113 0
|
29天前
|
C++ 容器
C++中自定义结构体或类作为关联容器的键
C++中自定义结构体或类作为关联容器的键
30 0
|
29天前
|
存储 算法 搜索推荐
【C++】类的默认成员函数
【C++】类的默认成员函数
|
8天前
|
存储 编译器 C++
C ++初阶:类和对象(中)
C ++初阶:类和对象(中)
|
8天前
|
C++
C++(十六)类之间转化
在C++中,类之间的转换可以通过转换构造函数和操作符函数实现。转换构造函数是一种单参数构造函数,用于将其他类型转换为本类类型。为了防止不必要的隐式转换,可以使用`explicit`关键字来禁止这种自动转换。此外,还可以通过定义`operator`函数来进行类型转换,该函数无参数且无返回值。下面展示了如何使用这两种方式实现自定义类型的相互转换,并通过示例代码说明了`explicit`关键字的作用。
|
8天前
|
存储 设计模式 编译器
C++(十三) 类的扩展
本文详细介绍了C++中类的各种扩展特性,包括类成员存储、`sizeof`操作符的应用、类成员函数的存储方式及其背后的`this`指针机制。此外,还探讨了`const`修饰符在成员变量和函数中的作用,以及如何通过`static`关键字实现类中的资源共享。文章还介绍了单例模式的设计思路,并讨论了指向类成员(数据成员和函数成员)的指针的使用方法。最后,还讲解了指向静态成员的指针的相关概念和应用示例。通过这些内容,帮助读者更好地理解和掌握C++面向对象编程的核心概念和技术细节。
|
29天前
|
存储 安全 编译器
【C++】类和对象(下)
【C++】类和对象(下)
【C++】类和对象(下)