第 8 章:使用更好的库_《C++性能优化指南》_notes

使用更好的库

      • 第八章核心知识点解析
      • 编译与测试建议
      • 总结优化原则
      • 重点内容:
      • 第一部分:多选题(10题)
      • 第二部分:设计题
      • 答案与解析
        • 多选题答案:
        • 设计题答案示例(部分):
      • 测试用例设计原则:

第八章核心知识点解析

  1. 优化标准库的使用
    知识点:选择合适的数据结构、预分配内存、减少拷贝
#include <vector>
#include <chrono>
#include <iostream>// 测试vector的reserve对性能的影响
void test_vector_reserve() {const int N = 1000000;// 不预分配内存{std::vector<int> v;auto start = std::chrono::high_resolution_clock::now();for(int i=0; i<N; ++i) {v.push_back(i);}auto end = std::chrono::high_resolution_clock::now();std::cout << "Without reserve: " << std::chrono::duration_cast<std::chrono::microseconds>(end-start).count()<< "μs\n";}// 预分配内存{std::vector<int> v;v.reserve(N);auto start = std::chrono::high_resolution_clock::now();for(int i=0; i<N; ++i) {v.push_back(i);}auto end = std::chrono::high_resolution_clock::now();std::cout << "With reserve: " << std::chrono::duration_cast<std::chrono::microseconds>(end-start).count()<< "μs\n";}
}int main() {test_vector_reserve();return 0;
}

输出示例

Without reserve: 5432μs
With reserve: 1276μs

关键点

  • reserve()预分配内存避免多次重新分配
  • 减少内存分配次数可提升3-4倍性能
  • 适用于vector、string等动态容器
  1. 优化现有库
    知识点:添加批量处理接口
#include <vector>
#include <chrono>
#include <iostream>// 原始单元素处理接口
void process_element(std::vector<int>& vec, int value) {vec.push_back(value * 2);
}// 优化的批量处理接口
void process_batch(std::vector<int>& vec, const std::vector<int>& values) {vec.reserve(vec.size() + values.size());for(auto v : values) {vec.push_back(v * 2);}
}void test_batch_processing() {const int N = 10000;std::vector<int> input(N, 5);// 单次处理测试{std::vector<int> result;auto start = std::chrono::high_resolution_clock::now();for(auto v : input) {process_element(result, v);}auto end = std::chrono::high_resolution_clock::now();std::cout << "Single processing: " << std::chrono::duration_cast<std::chrono::microseconds>(end-start).count()<< "μs\n";}// 批量处理测试{std::vector<int> result;auto start = std::chrono::high_resolution_clock::now();process_batch(result, input);auto end = std::chrono::high_resolution_clock::now();std::cout << "Batch processing: " << std::chrono::duration_cast<std::chrono::microseconds>(end-start).count()<< "μs\n";}
}int main() {test_batch_processing();return 0;
}

输出示例

Single processing: 856μs
Batch processing: 213μs

关键点

  • 批量处理减少函数调用开销
  • 预分配内存进一步提升性能
  • 接口设计要考虑使用场景
  1. 设计高效库
    知识点:扁平化调用链
#include <chrono>
#include <iostream>// 深层次调用链
class DeepCallChain {
public:void level3(int x) { data = x * 2; }void level2(int x) { level3(x); }void level1(int x) { level2(x); }int get() const { return data; }
private:int data;
};// 扁平化调用链
class FlatCallChain {
public:void process(int x) { data = x * 2; }int get() const { return data; }
private: int data;
};void test_call_chain() {const int N = 1000000;// 深层次调用{DeepCallChain obj;auto start = std::chrono::high_resolution_clock::now();for(int i=0; i<N; ++i) {obj.level1(i);}auto end = std::chrono::high_resolution_clock::now();std::cout << "Deep call chain: " << std::chrono::duration_cast<std::chrono::microseconds>(end-start).count()<< "μs\n";}// 扁平调用{FlatCallChain obj;auto start = std::chrono::high_resolution_clock::now();for(int i=0; i<N; ++i) {obj.process(i);}auto end = std::chrono::high_resolution_clock::now();std::cout << "Flat call chain: " << std::chrono::duration_cast<std::chrono::microseconds>(end-start).count()<< "μs\n";}
}int main() {test_call_chain();return 0;
}

输出示例

Deep call chain: 2563μs
Flat call chain: 1245μs

关键点

  • 减少函数调用层级
  • 避免不必要的中间调用层
  • 扁平调用提升约50%性能
  1. 避免动态查找
    知识点:用直接访问替代查找
#include <vector>
#include <algorithm>
#include <chrono>
#include <iostream>void test_access_method() {const int N = 100000;std::vector<int> data(N);std::iota(data.begin(), data.end(), 0);// 查找方式访问{int sum = 0;auto start = std::chrono::high_resolution_clock::now();for(int i=0; i<N; ++i) {auto it = std::find(data.begin(), data.end(), i);if(it != data.end()) sum += *it;}auto end = std::chrono::high_resolution_clock::now();std::cout << "Find access: " << std::chrono::duration_cast<std::chrono::microseconds>(end-start).count()<< "μs\n";}// 直接索引访问{int sum = 0;auto start = std::chrono::high_resolution_clock::now();for(int i=0; i<N; ++i) {sum += data[i];}auto end = std::chrono::high_resolution_clock::now();std::cout << "Direct access: " << std::chrono::duration_cast<std::chrono::microseconds>(end-start).count()<< "μs\n";}
}int main() {test_access_method();return 0;
}

输出示例

Find access: 12563μs
Direct access: 256μs

关键点

  • 查找操作时间复杂度O(n)
  • 直接索引访问复杂度O(1)
  • 性能差异可达50倍以上

编译与测试建议

  1. 使用C++11及以上标准编译:
g++ -std=c++11 -O2 example.cpp -o example
  1. 运行测试:
./example
  1. 典型优化效果对比:
  • 内存预分配可提升3-5倍性能
  • 批量接口比单次处理快2-4倍
  • 扁平调用链比深层次快1.5-2倍
  • 直接访问比查找快10-50倍

总结优化原则

  1. 预分配原则:对已知大小的容器使用reserve()
  2. 批量处理:设计支持批量操作的接口
  3. 扁平设计:减少不必要的调用层次
  4. 直接访问:用索引替代查找操作
  5. 内存重用:避免重复分配/释放内存

重点内容:

  1. 标准库性能特征与实现差异
  2. 自定义内存分配器的设计与应用
  3. 避免抽象惩罚的编程技巧
  4. 高效算法和数据结构的选择
  5. 字符串处理优化策略
  6. 模板元编程的性能影响
  7. 异常处理的开销控制
  8. 线程安全与性能平衡
  9. 缓存友好型数据结构设计
  10. 编译器优化选项的合理使用

第一部分:多选题(10题)

  1. 关于标准库容器的性能优化,以下哪些说法正确?
    A) vector的push_back时间复杂度是O(n)
    B) unordered_map的查找复杂度总是O(1)
    C) deque在中间插入元素的时间复杂度是O(n)
    D) list的迭代器失效规则与vector相同

  2. 下列哪些方法可以有效减少动态内存分配?
    A) 使用对象池模式
    B) 优先使用emplace_back代替push_back
    C) 为string预先调用reserve()
    D) 使用std::make_shared创建智能指针

  3. 关于字符串优化,哪些做法正确?
    A) 小字符串优化(SSO)可以避免堆分配
    B) 使用+=拼接比多次operator+更高效
    C) 移动语义可以完全消除字符串拷贝
    D) c_str()调用会触发深拷贝

  4. 以下哪些算法选择可能提升性能?
    A) 用std::sort替代冒泡排序
    B) 用std::lower_bound替代线性查找
    C) 用std::list替代std::vector存储频繁修改的序列
    D) 用std::array替代原始数组

  5. 关于内存分配器,正确的有:
    A) 自定义分配器可以减少锁竞争
    B) std::allocator是线程安全的
    C) 内存池适合固定大小对象的分配
    D) 对齐分配对SIMD指令很重要

  6. 哪些模板使用可能影响性能?
    A) 深度嵌套的模板实例化
    B) 递归模板元编程
    C) 使用类型擦除的any类型
    D) 模板参数推导失败

  7. 关于异常处理,正确的有:
    A) 异常规范影响代码优化
    B) try块会增加函数调用开销
    C) noexcept声明帮助编译器优化
    D) 异常捕获应尽量精确

  8. 缓存友好的设计包括:
    A) 使用紧凑数据结构
    B) 预取相邻内存数据
    C) 随机访问链表节点
    D) 对齐内存访问边界

  9. 编译器优化相关:
    A) -O3可能增加代码体积
    B) LTO优化链接时代码
    C) PGO需要训练数据
    D) restrict关键字帮助别名分析

  10. 线程安全优化策略:
    A) 读写锁减少竞争
    B) thread_local变量避免锁
    C) 无锁数据结构消除等待
    D) 原子操作总是比锁高效


第二部分:设计题

设计题1:高效字符串拼接

// 要求:实现零拷贝的字符串拼接,支持链式调用
class StringBuilder {
public:StringBuilder& append(const std::string& s);std::string build();
private:// 设计存储结构
};

设计题2:内存池分配器

// 实现固定块大小的内存池,支持STL容器
template <typename T>
class PoolAllocator {
public:using value_type = T;// 必要接口实现
};

设计题3:类型擦除优化

// 设计替代虚函数调用的高效多态方案
template <typename T>
class FunctionWrapper {
public:template <typename F>FunctionWrapper(F&& f);void operator()() const;
private:// 存储策略
};

设计题4:SIMD优化矩阵乘法

// 使用AVX指令优化4x4矩阵乘法
void matrix_multiply_avx(const float* a, const float* b, float* result);

设计题5:无锁队列

// 实现多生产者单消费者的无锁队列
template <typename T>
class LockFreeQueue {
public:void push(const T& value);bool pop(T& value);
private:// 设计节点结构和原子操作
};

答案与解析


多选题答案:
  1. BC
    C正确,deque中间插入O(n);B哈希表平均O(1)
    A错,摊销O(1);D错,vector插入会使迭代器失效

  2. ABCD
    全部正确,B/D减少临时对象,A/C预分配

  3. AB
    C错,移动可能保留容量;D错,c_str()不触发拷贝

  4. AB
    C错,vector更适合随机访问;D类型安全但性能相同

  5. ACD
    B错,标准分配器线程安全但可能有锁

  6. ABC
    D是编译错误,不影响运行性能

  7. ACD
    B错,try块本身无运行时开销

  8. ABD
    C链表导致缓存不命中

  9. ABCD
    全部正确

  10. ABC
    D错,原子操作可能更慢


设计题答案示例(部分):

设计题1实现:

class StringBuilder {std::vector<std::reference_wrapper<const std::string>> parts;
public:StringBuilder& append(const std::string& s) {parts.emplace_back(s);return *this;}std::string build() const {size_t total = 0;for (const auto& s : parts) total += s.get().size();std::string result;result.reserve(total);for (const auto& s : parts) result += s.get();return result;}
};// 测试用例
int main() {StringBuilder sb;sb.append("Hello").append(" ").append("World");assert(sb.build() == "Hello World");
}

设计题2实现:

template <typename T>
class PoolAllocator {struct Block { Block* next; };Block* freeList = nullptr;public:T* allocate(size_t n) {if (n != 1 || !freeList) {return static_cast<T*>(::operator new(n * sizeof(T)));}auto head = freeList;freeList = freeList->next;return reinterpret_cast<T*>(head);}void deallocate(T* p, size_t n) {if (n != 1) {::operator delete(p);return;}auto block = reinterpret_cast<Block*>(p);block->next = freeList;freeList = block;}
};

剩下的设计题目, 后续补充


测试用例设计原则:

  1. 边界测试(空容器、最大容量)
  2. 并发测试(多线程竞争)
  3. 性能对比(与标准实现比较)
  4. 内存泄漏检测(Valgrind验证)
  5. 平台兼容性(不同编译器/架构)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/75664.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RabbitMQ 学习整理1 - 基础使用

项目代码&#xff1a;RabbitMQDemo: 学习RabbitMQ的一些整理 基本概念 RabbitMQ是一种基于AMQP协议的消息队列实现框架RabbitMQ可以用于在系统与系统之间或者微服务节点之间&#xff0c;进行消息缓存&#xff0c;消息广播&#xff0c;消息分配以及限流消峰处理RabbitMQ-Serve…

React组件简介

组件 在 React 中&#xff0c;组件&#xff08;Component&#xff09; 是 UI 的基本构建块。可以把它理解为一个独立的、可复用的 UI 单元&#xff0c;类似于函数&#xff0c;它接受输入&#xff08;props&#xff09;&#xff0c;然后返回 React 元素来描述 UI。 组件的简单…

Kafka消息序列化深度革命:构建高性能、高安全的自定义编码体系

一、突破默认序列化的桎梏 1.1 原生序列化器的致命缺陷 Kafka默认提供的StringSerializer/ByteArraySerializer在复杂场景下暴露三大痛点&#xff1a; 类型安全黑洞&#xff1a;字节流缺乏元数据描述&#xff0c;消费端解析如履薄冰版本兼容困境&#xff1a;数据结构变更导致…

向量数据库与传统数据库的差异

向量数据库是一种专门设计用于高效存储、管理和检索**向量数据&#xff08;高维数值数组&#xff09;**的数据库系统。它针对非结构化数据&#xff08;如图像、文本、音频&#xff09;的特征进行优化&#xff0c;通过将数据转化为向量嵌入&#xff08;embeddings&#xff09;&a…

自动化框架的设计与实现

一、自动化测试框架 在大部分测试人员眼中只要沾上“框架”&#xff0c;就感觉非常神秘&#xff0c;非常遥远。大家之所以觉得复杂&#xff0c;是因为落地运用起来很复杂&#xff1b;每个公司&#xff0c;每个业务及产品线的业务流程都不一样&#xff0c;所以就导致了“自动化…

SpringBoot 3+ Lombok日志框架从logback改为Log4j2

r要将Spring Boot 3项目中的日志框架从Logback切换到Log4j2&#xff0c;并配置按日期滚动文件和控制台输出&#xff0c;请按照以下步骤操作&#xff1a; 步骤 1&#xff1a;排除Logback并添加Log4j2依赖 在pom.xml中修改依赖&#xff1a; <dependencies><!-- 排除默…

①、环境准备-主流技术(IPS/FW/主备-主主快速切换)

主流技术&(IPS/FW/主备-主主快速切换&#xff09; 一、RBM主备方案 RBM-FW-P 主配置内容介绍-注释 remote-backup group 含义&#xff1a;定义了一个远程备份组。这表明设备支持某种形式的远程备份功能&#xff0c;用于在设备之间同步配置或数据。data-channel interface …

量化交通拥堵

指数&#xff1a; 基于严重拥堵里程比的指数和基于出行时间比的指数。 评价指标是饱和度&#xff08;VC比&#xff09;&#xff0c;它表示交通量与通行能力的比值。 饱和度可分为道路饱和度和路口饱和度。道路饱和度还会进一步分级&#xff0c;有四档和六档之分。 城市道路和…

PDF与Markdown的量子纠缠:一场由VLM导演的文档界奇幻秀

缘起:当格式界的"泰坦尼克号"撞上"黑客帝国" 某个月黑风高的夜晚,在"二进制酒吧"的霓虹灯下: PDF(西装革履地晃着威士忌): “我的每一页都像瑞士手表般精密,连华尔街的秃鹫都为我倾倒!” Markdown(穿着带洞的拖鞋): “得了吧老古董!…

【neo4j数据导出并在其他电脑导入】

停止服务 neo4j stop 导出 neo4j-admin database dump neo4j --to-path"C:\Users\12901\Downloads\test folder" 导入 将 .dump 文件放在一个目录中 mkdir /root/dump-directory mv /root/neo4j.dump /root/dump-directory/ 使用包含 .dump 文件的目录路径作为 …

前端使用WPS WebOffice 做在线文档预览与编辑

先附上官网 WebOffice SDK 1、在下面这个地方找到jdk&#xff0c;然后下载 按照 2、只需要把jdk下载下来&#xff0c;放到项目中&#xff0c;然后引入到项目中就可以了&#xff0c;在wps 官网创建个应用&#xff0c;然后把appId放到代码中就可以了&#xff0c;等待后端把回调…

跨语言微服务架构(Java、Python)——“API中台”

文章目录 一、引言二、系统架构概述2.1 统一单点登录&#xff08;SSO&#xff09;与权限管理设计2.2 API中台与数据中台的融合2.3 跨语言适配器与 JWT 认证机制 三、技术细节与工具选型3.1 SSO 系统的选型与实现3.2 微服务架构与 API 中台的实现3.3 跨语言适配器实现与技术难点…

DeepSeek V3-0324升级:开启人机共创新纪元

一、技术平权&#xff1a;开源协议重构AI权力格局 DeepSeek V3选择MIT协议开源6850亿参数模型&#xff0c;本质上是一场针对技术垄断的“数字起义”。这一决策的深层影响在于&#xff1a; 商业逻辑的重构 闭源AI公司依赖API收费的商业模式面临根本性挑战。当顶级模型能力可通过…

QOpenGLWidget视频画面上绘制矩形框

一、QPainter绘制 在QOpenGLWidget中可以绘制,并且和OpenGL的内容叠在一起。paintGL里面绘制完视频后,解锁资源,再用QPainter绘制矩形框。这种方式灵活性最好。 void VideoGLWidget::paintGL() {glClear(GL_COLOR_BUFFER_BIT);m_program.bind();//绘制视频数据// 解绑VAOg…

3.3 Taylor公式

1.定义 1.1 taylor公式 1.2 麦克劳林公式 1.3 推论 1.4 拉格朗日余项和皮亚诺型余项 2. 例题 3.几种特殊函数的麦克劳林展开

CEF 给交互函数, 添加控制台是否显示交互参数log开关

CEF 控制台添加一函数,枚举 注册的供前端使用的CPP交互函数有哪些 CEF 多进程模式时,注入函数,获得交互信息-CSDN博客 这两篇文章,介绍了注入函数,在控制台中显示 各自提供的交互函数信息。 有些场景下,我们还需要更详细的信息,比如想知道 彼此传递的参数, 如果每次调…

QTcpSocket多线程连接慢问题

20250325记录 环境&#xff1a;Qt5.14.2 64位 msvc编译 在多线程环境下&#xff0c;使用QTcpSocket实现客户端&#xff0c;发现在少部分电脑上&#xff0c;连接时间过长&#xff0c;定时器检查套接字状态时&#xff0c;发现连接处于QAbstractSocket::ConnectingState状态。 …

IntelliJ IDEA创建Maven工程

1、创建空工程 1&#xff09;创建 2&#xff09;配置JDK和Maven 2、创建Maven工程 3、Maven工程结构简介 1&#xff09;目录 pom.xml 2&#xff09;窗口 4、参考 08.IDEA配置本地Maven软件_哔哩哔哩_bilibili

(UI自动化测试web端)第二篇:元素定位的方法_css定位之class选择器

看代码里的【find_element_by_css_selector( )】( )里的表达式怎么写&#xff1f; 文章介绍了第二种写法class选择器。你要根据网页中的实际情况来判断自己到底要用哪一种方法来进行元素定位。每种方法都要多练习&#xff0c;全都熟了之后你在工作当中使用起来元素定位时&#…

加新题了,MySQL 8.0 OCP 认证考试 题库更新

MySQL 8.0 OCP 认证考试 题库更新 MySQL 8.0 Database Administrator 考试科目&#xff1a;1Z0-908 近期发现&#xff0c;MySQL OCP认证考试题库发生变化&#xff0c;出现了很多新题&#xff0c;对此&#xff0c;CUUG专门收集整理了最新版本的MySQL考试原题&#xff0c;并会给…