《C++性能优化指南》——粗学学习笔记

1.代码优化策略总结

  • 用好得编译器并用好编译器(使用支持c++11的编译器、编译时打开优化选项)
  • 使用更好的算法(查找或排序的最优算法)
  • 使用更好的库
  • 减少内存分配和赋值
  • 移除计算(循环中单条语句的循环执行)
  • 使用更好的数据结构(不同的数据结构在使用内存管理器上有所不同)
  • 提高并发性
  • 优化内存管理

2.影响优化的计算机性能

  • 计算机的主内存很慢(通往主内存的接口是限制执行速度的瓶颈(被称冯诺尼曼瓶颈),(摩尔定律)每年处理器的核心数量都会增加,但性能未必提高,这些核心只是增加了访问内存的机会(内存墙)
  • 访问内存的开销比其他操作要大, 非对齐的内存访问的时间是这些字节在同一个字中时的两倍,因为需要读取两个字。
  • 计算机内存的访问方式并非以字节为单位,某些内存访问会比其他的更慢 内存容量有限,
  • 访问频繁使用的内存地址的速度比访问非频繁使用的内存地址的速度快。 访问相邻地址的内存的速度比访问互相远离的地址的内存快。
  • 访问线程间共享的数据比访问非共享的数据要慢得多。
  • 每次赋值、函数参数的初始化和函数返回值都会嗲用一次构造函数,这个函数就可能隐藏了大量未知的代码。

3.优化动态分配内存的变量

  • 静态的创建类成员变量,并在有必要时采用“两段初始化”,可以节省为这些成员变量分配内存的开销。

两段初始化:将类1声明为其他类的成员,并在创建其他类时部分初始化这个类1。然后,在类1中定义一个用于在资 源准备就绪时完全地初始化变量的成员函数。最后,在原来使用new表达式动态地创建实例的地方,插入一段调用这个初始化成员函数的代码就可以了。

  • 使用静态数据结构
    (1). 用std::array 代替std::vector(条件:编译时知道数组最大大小)
    (2). 在栈上创建大块存储区 (例如 预先为string预留内存空间)
    (3). 静态初始化的方式构建具有链式数据结构的数据 或 为结构中的每一个元素都初始化一个变量
  • 减少动态变量的重新分配
    (1). 如 std:string ,std::vector ,预先使用reserve函数设置大小
    (2). 在循环外创建动态变量
  • 减少无谓的复制,复制可能发生的地方:
    初始化(调用构造函数)
    赋值(调用赋值运算符
    函数参数 (每个参数表达式都会被移动构造函数或复制构造函数复制到形参中)
    函数返回(调用移动构造函数或复制构造函数,甚至可能会调用两次)
    插入一个元素到标准库容器里(调用移动构造函数或复制构造函数复制元素)
  • 扁平数据结构更好(数据结构中的元素被存储在连续的存储空间)

4.热点语句的优化

4.1. 循环

  • strlen 开销大
  • 使用do…while(条件) 比使用for 通常可以提高循环处理的速度
  • 递减代替递增
  • 从循环中移除不变性代码
  • 从循环中移除无谓的函数调用
  • 从循环中移除隐含的函数调用:
    • 声明一个类实例(调用构造函数)
    • 初始化一个类实例(调用构造函数)
    • 赋值一个类实例(调用赋值运算符)
    • 涉及类实例的 计算表达式(调用运算符成员函数)
    • 退出作用域(调用在作用域中声明的类实例的析构函数)
    • 函数参数(每个参数表达式都会被复制构造到形参中)
    • 函数返回一个类的实例(调用复制构造函数,可能两次)
    • 向标准库容器中插入元素(元素会被移动构造或复制构造)
    • 向矢量中插入元素

4.2. 频繁被调用的函数

  • 移除函数调用开销有效方式:内联函数

内联函数:
使用场景:代码量小,调用频繁的小函数,是以空间换时间的做法,省去调用函数、建立栈帧的额外开销
注意事项:关键字inline 必须与函数定义体放在一起才能使函数成为内联,仅将inline放在函数声明前面不起任何作用。一般在一个.h里实现内联函数的声明和定义。

  • 模板编程
  • 使用静态成员函数代替成员函数
  • ==double 计算比float计算快 ==

4.3. 优化控制流程惯用法

  • 用switch 代替 if…else

5.优化字符串的使用

5.1. 字符串特性:

  1. 字符串是动态分配的

原因:字符串内部的字符缓冲区的大小是固定的,当有使字符串变长的操作时,可能会使字符串的长度超出它内部的缓冲区的大小,从而发生从内存管理器中malloc/new 一块新的缓冲区,并将字符串 copy 到新的缓冲区中,并 free/delete 原来的空间。

  1. 字符串是值,而非引用
  2. 字符串会进行大量复制

原因:字符串是以值的方式来处理的,当创建字符串、赋值、或将其作为参数传递给函数时,都会进行一次copy,并且赋值和参数传递的开销都会变的很大

5.2. 优化方法:

  • 字符串连接运算符(+)的开销很大,他会调用内存管理器去构建一个新的临时字符串对象来保存连接后的字符串。(改进:复合赋值操作符+=代替+,或用append成员函数避免产生临时字符串)
  • 为字符串预留内存空间可以减少内存分配的开销
  • 将指向字符串的常量引用(const & string)给函数与传递值的结果一样,但是更加高效。(使用值传递时可能会导致复制操作,取决于字符串的实现方式;但使用常量引用时注意字符串的**“解引指针”** )
  • 将函数的字符串结果通过输出参数作为引用返回给调用方会复用实参的存储空间,这比分配新的存储空间更高效
  • 使用字符数组(char *)代替字符串

6.优化数据结构

  1. 优化std::map 的查找
    • 关联容器
    • 只想知道一个元素是否存在于map中,而不是找他的值,可用 binary_serach() 代替find()函数。
    • lower_bound()
  2. std::vector
    • 序列容器
    • 高效的填充一个vector方法排序(最优在前):赋值>使用迭代器和insert从另一个容器插入元素>push_back和insert在末尾插入元素
    • 编译vector高效方法排序(最优在前):使用下标[]>使用at()>使用迭代器
    • 高效使用vector秘诀之一:使用reserve(size_t size)函数预先分配足够内存
    • 高效使用vector秘诀之二:即使其中的元素被移除了,也不会自动将内存返回给内存管理器。clear()函数会设置容器的大小为0,但不一定会重新分配内存空间来减小vector的容量。释放容量可使用如下方法:
    std::vector<Foo>x;
    ...
    vector<Foo>().swap(x);//会创造一个临时空的矢量,内容与x交换,接着删除这个矢量,内存管理器会回收之前属于x的内存。
    
  3. deque各个操作都不如vector
  4. list :适用于在中间插入元素
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《C性能优化指南》是一本涵盖了C语言程序性能优化的重要知识和技巧的PDF文档。本指南旨在帮助开发人员提高他们编写和优化C语言程序的能力,以达到更高的性能和效率。 首先,在《C性能优化指南》中,作者详细介绍了C语言程序性能优化的重要性和意义。他们解释了性能优化对程序速度、内存占用和功耗的影响,以及对用户体验和系统资源利用率的重要性。 其次,本指南列举了一系列常见的性能优化技巧和最佳实践,包括优化代码结构、减少内存占用、减少函数调用次数、使用高效的数据结构和算法等方面。这些技巧都是基于C语言的特性和机制,针对C语言程序的性能瓶颈进行了详细的介绍和讲解。 另外,本指南还提供了大量实用的示例代码和性能优化案例分析,帮助读者更好地理解和应用性能优化技巧。这些案例涉及到不同类型的C语言程序,涵盖了计算密集型、内存密集型和I/O密集型等不同场景和需求。 最后,在《C性能优化指南》中,还包括了一些常见的工具和技术,比如性能分析工具、调试工具和代码优化器等,帮助开发人员更方便地进行性能优化和测试。 总之,这本PDF文档为开发人员提供了丰富的C语言程序性能优化知识和实用技巧,是一本深入浅出、实用性强的性能优化指南。阅读本指南可以帮助开发人员更好地提升C语言程序的性能和效率,提高程序的质量和用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值