计算机 · 2021年12月19日 0

如何写高效的C++代码

C++ Performance Optimization

C++ Optimization Strategies and Techniques

Design considerations

  • 使用stl
  • 尽量使用引用而不是指针
    1. 引用不需要像指针一样检查是否非空
    2. 引用不涉及*操作符,代码更简洁
    3. 指针让编译器更难确定不同的指针是否指向的是同一片内存,而引用可以,使用引用可以让编译器更好地做相关的优化
  • 考虑two-phase construction
    就是有些类的构造函数可能包含大量的拷贝等初始化操作,如果代码中很多时候可能用到的仅仅是”空的”这类对象,那么这些构造函数里做的初始化操作就是无用功,可以考虑把构造函数给移到一个专门的函数里面,只在需要的时候调用它。
  • 小心、尽量少用异常
    异常会让代码变大、变慢(因为会插入额外的异常处理相关的代码)
  • 少用RTTI(Runtime Type Identification)
    RTTI主要和dynamic_cast和typeid有关。使用RTTI和使用goto的原则差不多:尽量少用。
  • stdio比iostream更快

C++ Optimization tips

  • 传参数时尽量使用引用
  • 只在必要的时候才声明变量
  • 尽量使用变量的初始化而不是赋值(减少拷贝操作)
  • 尽量使用构造函数的初始化列表而不是在构造函数里面初始化成员变量
    原因是在构造函数里初始化成员变量时,会先为成员变量调用其默认(default)的构造函数,而使用初始化列表的时候则是调用复制构造函数。
  • 使用Operator=操作符而不是单独的使用Operator,因为这种方式会生成临时对象
  • 使用前缀操作符而不是后缀操作符,因为后缀操作符会生成临时对象
  • 使用explicit关键字发现潜在的构造函数的调用

Final Optmizations On C++

  • 使用内联函数
    仅对小型函数作内联;考虑编译器的自动内联选项;内联函数的效果到底好不好以profiling的结果为准
  • 尽量避免”hidden temporary objects”的产生
    • 使用引用
    • 使用前缀操作符
    • 返回值的优化(return constructor arguments instead of named objects)
      函数的返回值不要使用named objects,而是在return语句里直接构造一个对象返回,这样编译器可以直接在接收返回值的对象的内存空间里生成该对象,而不再需要再开辟一份空间来存储临时对象。示意代码:
template <class T> T Original(const T& tValue)
    {
    T tResult; // named object; probably can't be optimized away
    tResult = tValue;
    return (tResult);
    }

template <class T> T Optimized(const T& tValue)
    {
    return (T(tValue)); // unnamed object; optimization potential high
    }
**不过需要我确认一下在C++11里是不是编译器对named objects的返回也会做优化了**。  
  • 虚函数带来的额外开销
    由于virtual function table带来的对象内存占用上的额外开销和初始化、析构上需要执行额外的操作带来的开销
  • Empty Member Optimization
    为了支持寻址操作,即使一个类没有任何一个成员变量,该类的对象也会被分配一个字节的存储空间。但是a base class subobject of an empty class type may have zero size。也就是说继承自空类(没有任何成员变量)的类的对象里面所包含的这个空父类对象可以是空的。具体例子:
template <class T, class Alloc = Allocator<T> >
class ListWithAllocMember // (sizeof(ListWithAlocMember) == 8)
    {
    private :
        Alloc m_heap;
        Node* m_head;
    };

template <class T, class Alloc = Allocator<T> >
class ListWithAllocBase : private Alloc // (sizeof(ListWithAllocBase) == 4)
    {
    private :
        Node* m_head;
    };
  • 自定义allocator
  • Copy-On-Write
    通过RefCount和SmartPtr两个类来实现Copy-On-Write的策略。

Tips for Optimizing C/C++ Code

全面、简洁。

Optimizing C And C++ Code

同上。

C++11 optimization

[深入应用C++11代码优化与工程级应用 祁宇]

  1. C++11的标准要求所有容器的size()操作在O(1)时间内完成,而C++03只是说容器的size()操作”应该”是O(1)时间复杂度。
  2. 使用move constructor,减少拷贝
  3. emplace_back减少内存拷贝和移动
  4. 无序容器,video server中有很多map、set,都是用的红黑树的实现(需要排序),可以考虑换为C++11新增的无序容器,用的hash的实现,不需要排序操作; 需要确认a.删除操作会不会带来什么问题;b.小心hash实现的数据结构扩容时带来的性能问题,可能需要提前设置一个比较大的容量;c.确定使用set、map的地方真的是不在乎顺序的;
  5. 右值引用