第 6 章:优化动态分配内存的变量_《C++性能优化指南》_notes

news2025/3/31 8:20:11

优化动态分配内存的变量

      • 第六章核心知识点详解
      • 总结
      • 第六章 动态内存优化 重点难点梳理
    • 一、多选题(每题至少2个正确答案)
    • 二、设计题
    • 答案与详解
      • 多选题答案
      • 设计题答案示例

第六章核心知识点详解

  1. 动态内存分配的开销
    知识点:动态内存分配需要调用操作系统API,涉及锁竞争和内存碎片,频繁分配释放会导致性能瓶颈。

代码示例(比较动态数组 vs 静态数组):

#include <iostream>
#include <vector>
#include <chrono>

void test_dynamic_allocation() {
    auto start = std::chrono::high_resolution_clock::now();
    for (int i=0; i<100000; ++i) {
        int* arr = new int[100]; // 频繁动态分配
        delete[] arr;
    }
    auto end = std::chrono::high_resolution_clock::now();
    std::cout << "Dynamic: " 
              << std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count() 
              << " ms\n";
}

void test_static_allocation() {
    auto start = std::chrono::high_resolution_clock::now();
    for (int i=0; i<100000; ++i) {
        int arr[100]; // 栈上分配,快速
    }
    auto end = std::chrono::high_resolution_clock::now();
    std::cout << "Static: " 
              << std::chrono::duration_cast<std::chrono::milliseconds>(end-start).count() 
              << " ms\n";
}

int main() {
    test_dynamic_allocation();
    test_static_allocation();
    return 0;
}

输出

Dynamic: 120 ms
Static: 0 ms

分析:动态分配比栈分配慢百倍,需避免高频使用。


  1. 智能指针优化
    知识点std::shared_ptr引用计数有性能损耗,优先使用std::unique_ptr

代码示例

#include <memory>
#include <iostream>

struct Widget {
    Widget() { std::cout << "Widget created\n"; }
    ~Widget() { std::cout << "Widget destroyed\n"; }
};

void use_shared() {
    auto ptr = std::make_shared<Widget>(); // 引用计数+1
    auto ptr2 = ptr; // 引用计数+1
}

void use_unique() {
    auto ptr = std::make_unique<Widget>(); // 独占所有权
    // auto ptr2 = ptr; // 错误!无法复制unique_ptr
}

int main() {
    std::cout << "Shared ptr:\n";
    use_shared();
    std::cout << "Unique ptr:\n";
    use_unique();
}

输出

Shared ptr:
Widget created
Widget destroyed
Unique ptr:
Widget created
Widget destroyed

分析shared_ptr适合共享所有权,但引用计数带来开销;unique_ptr零开销,更高效。


  1. 避免不必要的复制
    知识点:C++11引入移动语义,通过std::move转移资源,避免深拷贝。

代码示例

#include <vector>
#include <iostream>

class HeavyData {
    std::vector<int> data;
public:
    HeavyData(size_t size) : data(size, 42) {
        std::cout << "HeavyData constructed\n";
    }
    // 移动构造函数
    HeavyData(HeavyData&& other) noexcept : data(std::move(other.data)) {
        std::cout << "HeavyData moved\n";
    }
    // 移动赋值运算符
    HeavyData& operator=(HeavyData&& other) noexcept {
        data = std::move(other.data);
        std::cout << "HeavyData moved assigned\n";
        return *this;
    }
};

int main() {
    HeavyData a(1000);
    HeavyData b = std::move(a); // 调用移动构造
    HeavyData c(2000);
    c = std::move(b); // 调用移动赋值
    return 0;
}

输出

HeavyData constructed
HeavyData moved
HeavyData constructed
HeavyData moved assigned

分析:移动操作避免了复制vector内容,性能显著提升。


  1. 返回值优化(RVO)
    知识点:编译器优化,消除函数返回时的临时对象复制。

代码示例

#include <iostream>

struct Data {
    Data() { std::cout << "Data created\n"; }
    Data(const Data&) { std::cout << "Data copied\n"; }
    Data(Data&&) { std::cout << "Data moved\n"; }
};

Data createData() {
    return Data(); // 可能触发RVO
}

int main() {
    Data d = createData();
    return 0;
}

输出(启用RVO时):

Data created

分析:RVO跳过了拷贝和移动构造,直接构造目标对象。


  1. 自定义内存分配器
    知识点:通过重载new/delete或使用内存池减少分配开销。

代码示例(简单内存池):

#include <iostream>
#include <vector>

class MemoryPool {
    std::vector<void*> blocks;
public:
    void* allocate(size_t size) {
        void* block = ::operator new(size);
        blocks.push_back(block);
        return block;
    }
    ~MemoryPool() {
        for (auto p : blocks) ::operator delete(p);
    }
};

struct FastObject {
    static MemoryPool pool;
    void* operator new(size_t size) {
        return pool.allocate(size);
    }
    void operator delete(void* p) {
        // 内存池统一释放
    }
};

MemoryPool FastObject::pool;

int main() {
    FastObject* obj = new FastObject;
    delete obj;
    return 0;
}

分析:集中管理内存分配,减少碎片和系统调用次数。


总结

第六章的核心在于减少动态内存操作,优先使用栈对象和移动语义,合理选择智能指针,利用编译器优化如RVO。每个优化点都有对应的代码实践,通过测量性能差异可验证优化效果。实际项目中需结合性能分析工具(如Valgrind、perf)定位热点,针对性优化。

第六章 动态内存优化 重点难点梳理

核心知识点:

  1. 智能指针所有权管理(shared_ptr/unique_ptr)
  2. 减少动态内存分配策略(预分配、对象池)
  3. 移动语义与右值引用优化
  4. 自定义内存分配器实现
  5. 写时复制(Copy-on-Write)优化模式
  6. 扁平数据结构设计
  7. 内存碎片管理
  8. 线程安全的内存管理

一、多选题(每题至少2个正确答案)

  1. 关于shared_ptr线程安全性,正确的是:
    A) 引用计数是原子操作
    B) 指向的对象本身线程安全
    C) 多个线程写同一个shared_ptr需要同步
    D) make_shared比new更高效

  2. 哪些方法能有效减少动态内存分配?
    A) 使用内存池
    B) 优先使用栈分配
    C) 使用vector::reserve预分配
    D) 全局静态对象

  3. 移动语义的优势包括:
    A) 消除深拷贝开销
    B) 允许资源转移
    C) 保证异常安全
    D) 自动处理循环引用

  4. 实现高效内存分配器的关键点:
    A) 固定大小内存块管理
    B) 线程本地存储
    C) 内存对齐保证
    D) 使用系统malloc

  5. 关于写时复制(COW),正确的是:
    A) 修改时触发真实复制
    B) 适用于高频读取场景
    C) 需要引用计数
    D) C++11后推荐使用

  6. 扁平数据结构的优势:
    A) 更好的缓存局部性
    B) 减少间接指针
    C) 简化内存管理
    D) 支持快速插入

  7. 自定义内存分配器的应用场景:
    A) 高频小对象分配
    B) 实时系统
    C) 多线程环境
    D) 持久化存储

  8. 优化动态数组性能的方法:
    A) reserve预分配容量
    B) 使用emplace_back
    C) 避免中间临时对象
    D) 使用链表替代

  9. 移动构造函数应具备:
    A) noexcept声明
    B) 转移资源所有权
    C) 深拷贝实现
    D) 修改原对象状态

  10. 线程安全内存管理策略:
    A) 使用TLS分配器
    B) 全局互斥锁保护
    C) 无锁队列分配
    D) 静态内存池


二、设计题

  1. 实现线程安全的内存池
    要求:支持固定大小内存块的分配/释放,多线程环境下高效工作,提供性能对比测试

  2. 优化动态数组高频插入
    设计一个支持快速插入的优化版vector,避免频繁扩容,提供基准测试对比

  3. 移动语义优化矩阵运算
    实现矩阵类,使用移动语义优化矩阵运算返回值,消除临时对象复制

  4. 写时复制字符串优化
    实现COW字符串类,支持高效的拷贝和修改操作,测量性能提升

  5. 自定义STL分配器
    实现符合C++标准的固定大小内存分配器,与std::vector集成并测试性能


答案与详解

多选题答案

  1. ACD
    B错误:指向对象需自行保证线程安全
    D正确:make_shared合并内存分配

  2. ABC
    D错误:全局对象可能导致初始化顺序问题

  3. AB
    C错误:移动可能抛出异常
    D错误:无关循环引用

  4. ABC
    D错误:自定义分配器应避免直接调用malloc

  5. ABC
    D错误:C++11后移动语义更优

  6. ABC
    D错误:扁平结构插入效率低

  7. ABC
    D错误:持久化需要其他机制

  8. ABC
    D错误:链表缓存不友好

  9. ABD
    C错误:移动应转移而非深拷贝

  10. AC
    B错误:全局锁影响性能
    D错误:静态池灵活性差


设计题答案示例

  1. 线程安全内存池实现
#include <iostream>
#include <vector>
#include <memory>
#include <mutex>
#include <chrono>

template <size_t BlockSize, size_t PoolSize>
class ThreadSafeMemoryPool {
public:
    ThreadSafeMemoryPool() {
        for (int i = 0; i < PoolSize; ++i) {
            free_blocks.push(new char[BlockSize]);
        }
    }

    void* allocate() {
        std::lock_guard<std::mutex> lock(mtx);
        if (free_blocks.empty()) {
            return ::operator new(BlockSize);
        }
        void* block = free_blocks.top();
        free_blocks.pop();
        return block;
    }

    void deallocate(void* block) {
        std::lock_guard<std::mutex> lock(mtx);
        free_blocks.push(static_cast<char*>(block));
    }

    ~ThreadSafeMemoryPool() {
        while (!free_blocks.empty()) {
            delete[] free_blocks.top();
            free_blocks.pop();
        }
    }

private:
    std::mutex mtx;
    std::stack<char*> free_blocks;
};

// 测试用例
int main() {
    constexpr size_t TEST_SIZE = 1000000;
    ThreadSafeMemoryPool<64, 1000> pool;

    auto start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < TEST_SIZE; ++i) {
        void* p = pool.allocate();
        pool.deallocate(p);
    }
    auto end = std::chrono::high_resolution_clock::now();

    std::cout << "Pool time: " 
              << std::chrono::duration_cast<std::chrono::microseconds>(end - start).count()
              << "μs\n";

    // 对比系统malloc
    start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < TEST_SIZE; ++i) {
        void* p = malloc(64);
        free(p);
    }
    end = std::chrono::high_resolution_clock::now();

    std::cout << "Malloc time: " 
              << std::chrono::duration_cast<std::chrono::microseconds>(end - start).count()
              << "μs\n";
}
  1. 优化版Vector设计
template <typename T>
class OptimizedVector {
public:
    OptimizedVector() : capacity_(4), size_(0) {
        data_ = static_cast<T*>(::operator new(capacity_ * sizeof(T)));
    }

    void push_back(T&& value) {
        if (size_ >= capacity_) {
            reserve(capacity_ * 2);
        }
        new (&data_[size_]) T(std::move(value));
        ++size_;
    }

    void reserve(size_t new_cap) {
        if (new_cap <= capacity_) return;

        T* new_data = static_cast<T*>(::operator new(new_cap * sizeof(T)));
        for (size_t i = 0; i < size_; ++i) {
            new (&new_data[i]) T(std::move(data_[i]));
            data_[i].~T();
        }
        ::operator delete(data_);
        data_ = new_data;
        capacity_ = new_cap;
    }

    // ...其他接口

private:
    T* data_;
    size_t capacity_;
    size_t size_;
};

// 测试用例
int main() {
    OptimizedVector<std::string> vec;
    constexpr int TEST_SIZE = 1000000;

    auto start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < TEST_SIZE; ++i) {
        vec.push_back(std::string(100, 'a'));
    }
    auto end = std::chrono::high_resolution_clock::now();

    std::cout << "Optimized vector: "
              << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
              << "ms\n";

    // 对比std::vector
    std::vector<std::string> std_vec;
    start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < TEST_SIZE; ++i) {
        std_vec.emplace_back(100, 'a');
    }
    end = std::chrono::high_resolution_clock::now();

    std::cout << "std::vector: "
              << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
              << "ms\n";
}

其他设计题目, 稍后补充


测试验证要点:

  1. 编译命令:g++ -std=c++17 -O2 test.cpp -o test
  2. 内存泄漏检测:Valgrind工具检查
  3. 性能对比:至少3次测试取平均值
  4. 多线程测试:使用std::async创建并发任务

通过这些问题和实现,可以有效巩固动态内存优化的核心概念,并在实践中验证各种优化技术的实际效果。

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

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

相关文章

Vue3 项目通过 docxtemplater 插件动态渲染 .docx 文档(带图片)预览,并导出

Vue3 项目通过 docxtemplater 插件动态渲染 .docx 文档&#xff08;带图片&#xff09;预览&#xff0c;并导出 预览安装插件示例代码项目目录结构截图实际效果截图 动态渲染 .docx 文档&#xff08;带图片&#xff09;&#xff0c;预览、导出安装插件docx 模板文件内容完整代码…

ollama迁移已下载的单个模型到服务器

ollama迁移已下载的单个模型到服务器 场景 ollama是面向用户级的&#xff0c;部署和运行都很简单&#xff0c;是否高效就另说了。但最起码&#xff0c;他能充分利用用户的硬件设备&#xff0c;在GPU不足也能调用cpu和内存去加持。 ollama运行的模型基本是量化版本的&#xf…

Photoshop 2025安装教程包含下载安装包,2025最新版图文安装教程

文章目录 前言一、Photoshop 2025下载二、Photoshop 2025安装教程1. 安装包解压2. 找到安装程序3. 以管理员身份运行4. 安装选项设置5. 选择安装路径6. 开始安装7. 安装完成8. 启动软件9. 软件主界面 前言 无论你是专业设计师&#xff0c;还是刚接触图像处理的新手&#xff0c…

【Python · PyTorch】时域卷积网络 TCN

1. 概念 1.1 定义 TCN 是时域卷积网络&#xff08;Temporal Convolutional Network&#xff09;的简称。TCN是于2018年 Shaojie Bai 等人提出的一个处理时序数据的卷积模型。 TCN结合了CNN卷积并行性计算和RNN长期依赖的优势&#xff0c;CNN可在多个通道同时处理卷积核运算&…

Mysql update更新数据执行流程

update 的执行流程是以select查询为基础执行的&#xff01;&#xff01;你不明白select执行流程&#xff1f;没关系&#xff0c;这篇博客照样让你明白&#xff0c;update执行流程&#xff01; 存储引擎是什么&#xff1f; 如果把数据库比作一个大仓库&#xff0c;那么存储引擎…

WMS WCS系统架构

1.1立体仓库现场网络架构图 1.2立体仓库WMS系统与WCS系统架构 1.3系统技术选型 WEB端技术&#xff1a;node.js、vue 、element、jquery、html、js、css等 API端技术&#xff1a;spring boot 、msyql、redis、mybatis等 WCS技术&#xff1a;c#、winform、OPC、socket、S7等 …

23种设计模式-状态(State)设计模式

状态设计模式 &#x1f6a9;什么是状态设计模式&#xff1f;&#x1f6a9;状态设计模式的特点&#x1f6a9;状态设计模式的结构&#x1f6a9;状态设计模式的优缺点&#x1f6a9;状态设计模式的Java实现&#x1f6a9;代码总结&#x1f6a9;总结 &#x1f6a9;什么是状态设计模式…

kaggle上经典泰坦尼克项目数据分析探索

之前了解在kaggle上这个项目很火&#xff0c;最近想要加强一下python数据分析&#xff0c;所以在kaggle上找到这个项目进行学习探索&#xff0c;下面是将一些学习资料以及过程整理出来。 一、首先我们了解一下项目背景以及如何找到这个项目。 kaggle项目地址: https://www.k…

15 python 数据容器-字典

在 Python 的编程世界里&#xff0c;字典是一种超实用的数据类型&#xff0c;它就像打工人的工作资料夹&#xff0c;能把各种不同类型的信息有条理地存起来&#xff0c;还能快速找到你需要的内容。对于刚开始学习编程的小伙伴来说&#xff0c;掌握字典的用法&#xff0c;能让你…

Linux的一些常见指令

一、ls指令 语法&#xff1a; ls (选项) 功能&#xff1a; ls可以查看当前目录下的所有文件和目录。 常用选项&#xff1a; -a:列出目录下的所有文件&#xff0c;包括以点&#xff08;.&#xff09;开头的隐含文件 。-d:将目录像文件一样显示&#xff0c;不显示其下的文件。…

jmm-java内存模型

java内存模型----底层原理 底层原理 从Java代码到最终执行的CPU指令的流程&#xff1a; 最开始&#xff0c;我们编写的Java代码&#xff0c;是*.java文件在编译&#xff08;javac命令&#xff09;后&#xff0c;从刚才的*.java文件会变出一个新的Java字节码文件&#xff08;…

合宙780E开发学习-LUATOS-SOC云编译自定义固件

登录https://luatos.com 点击登录&#xff0c;使用合宙erp账号登录即可 点击右上角构建&#xff0c;点击右上角菜单新构建&#xff0c;自定义构建名称&#xff0c;可新建多个 勾选想要的组件 点击右上角保存修改&#xff0c;只有点击准备就绪&#xff08;注意&#xff1a;一定…

好用的Markdown阅读编辑器Typora破解记录

Typora破解 一、下载Typora二、安装Typora三、破解Typora &#x1f600; 记录一下Typora破解记录&#xff0c;怕不常用忘记咯&#xff0c;感觉自己现在的脑子就像我的肠子一样&#xff0c;刚装进去就么得了。。。&#x1f614; Typroa算是用起来很舒服的Markdown阅读器了吧&am…

自然语言处理(13:RNN的实现)

系列文章目录 第一章 1:同义词词典和基于计数方法语料库预处理 第一章 2:基于计数方法的分布式表示和假设&#xff0c;共现矩阵&#xff0c;向量相似度 第一章 3:基于计数方法的改进以及总结 第二章 1:word2vec 第二章 2:word2vec和CBOW模型的初步实现 第二章 3:CBOW模型…

无人机宽带自组网机载电台技术详解,50KM超远图数传输系统实现详解

以下是关于无人机宽带自组网机载电台技术以及50KM超远图数传输系统实现的详解&#xff1a; 无人机宽带自组网机载电台技术详解 无人机宽带自组网机载电台是一种专门为无人机设计的通信设备&#xff0c;它支持宽带数据传输和自组网功能。这种电台的实现技术涉及多个方面&#x…

MySQL 表 t1 建立联合索引 (a, b, c),在 where a < ? and b > ? and c < ? 中哪些索引生效

文章目录 联合索引 abc 均范围扫描时的索引生效情况无回表 表数据量非常少无回表 表数据量多有回表总结 联合索引 abc 均范围扫描时的索引生效情况 场景&#xff1a;表 t1 建立联合索引 (a, b, c)&#xff0c;在 where a < ? and b > ? and c < ? 中哪些索引生效…

BP神经网络+NSGAII算法(保真)

BP神经网络NSGAII算法 非常适合用来当作实验验证自己的结论&#xff0c;构建一个神经网络模型&#xff0c;并使用NSGAII多目标优化算法来实现多领域的毕业论文的设计。仅仅使用简单的matlab代码就可以实现自己的多目标优化任务。 BP神经网络算法 我的任务是预测三个变量的值…

每日一题-力扣-2829. k-avoiding 数组的最小总和 0326

解决"k-avoiding 数组的最小总和"问题 这道题有两种主要解法。 解法一&#xff1a;直接数学计算&#xff08;最优解&#xff09; 通过数学推导直接计算出结果&#xff0c;不需要构建实际的数组。 class Solution:def minimumSum(self, n: int, k: int) -> int…

OSI模型_TCP/IP模型_五层模型

文章目录 OSI模型_TCP/IP模型_五层模型模型对比模型层级对比关键区别对比 OSI模型OSI模型概述举例说明流程图示 TCP/IP 四层模型模型结构举例说明流程图示 TCP/IP 五层模型模型的结构举例说明流程图示 OSI模型_TCP/IP模型_五层模型 学OSI&#xff0c;用TCP/IP&#xff0c;分析选…

SpringCould微服务架构之Docker(2)

Docker和虚拟机的差别&#xff1a; 虚拟机是在操作系统中模拟硬件设备&#xff0c;然后运行另外一个操作系统。