C++ 性能分析的实战指南(gperftools工具)[建议收藏]

文章目录

  • 使用gperftools进行 C++ 性能分析的实战指南
    • 一、编译安装 gperftools
        • 1. 下载源代码:
        • 2. 编译和安装:
    • 二、编写测试程序
    • 三、使用 gperftools 代码示例
    • 四、查看分析结果
    • 五、一份实际代码实例及实操
        • 1.代码实例
        • 2.操作命令
        • 3.结果分析
          • 根据上述数据,对关键函数分析
    • 六、后论
  • - 推荐文章 -
      • C++
      • 音视频

使用gperftools进行 C++ 性能分析的实战指南

在软件开发过程中,性能优化是一项至关重要的任务,尤其是对于复杂的 C++ 应用程序来说。gperftools 是一套功能强大的性能分析工具,它为 C++ 开发者提供了分析 CPU 使用和内存使用的有效手段。在这篇博客中,我们将详细介绍如何在 C++ 项目中使用 gperftools 来识别和解决性能瓶颈。

一、编译安装 gperftools

首先,我们需要安装 gperftools。以下步骤将指导您完成下载和安装过程:

1. 下载源代码:
wget https://github.com/gperftools/gperftools/releases/download/gperftools-2.15/gperftools-2.15.tar.gz
tar -xvf gperftools-2.15.tar.gz
2. 编译和安装:
cd gperftools-2.15
./configure --prefix=$PWD/build --enable-shared=no --enable-static=yes --enable-libunwind
make -j8
make install

注意:请确保你的 CMake 版本在 3.12 或以上。

二、编写测试程序

安装完成后,我们可以编写一个简单的 C++ 程序来测试 gperftools 的功能。以下是编译程序所需的命令:

g++ -o main hot.cpp -I./include -L ./lib -lprofiler -ltcmalloc -lpthread

这里,我们假设源文件名为 hot.cpp

三、使用 gperftools 代码示例

在 C++ 程序中,可以通过以下方式集成 gperftools:

#include <gperftools/profiler.h>
#include <gperftools/heap-profiler.h>int main() {ProfilerStart("cpu-profiler.prof");HeapProfilerStart("memory-profiler");// 你的代码ProfilerStop();HeapProfilerDump("test");HeapProfilerStop();return 0;
}

在上面的例子中,我们使用 ProfilerStartHeapProfilerStart 开启 CPU 和内存分析,执行我们需要测试的代码,然后使用 ProfilerStopHeapProfilerStop 停止分析。

四、查看分析结果

生成的分析文件可以使用 gperftools 提供的 pprof 工具来查看:

pprof ./main ./cpu-profiler.prof --text
pprof ./main ./memory-profiler.0001.heap --text

这些命令将输出文本形式的性能分析报告,帮助理解程序中的热点。

五、一份实际代码实例及实操

1.代码实例

假设应用程序包含了大量的数据库操作和数据处理,可以通过 gperftools 来识别哪些函数占用了最多的 CPU 时间或内存:

#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <chrono>
#include <random>
#include <memory>
#include <functional>// 数据库模拟类
class Database {
private:std::mutex db_mutex;public:int getData(int id) {std::lock_guard<std::mutex> lock(db_mutex);std::this_thread::sleep_for(std::chrono::milliseconds(10)); // 模拟数据库延迟return id * id; // 简单计算,模拟数据处理}void updateData(int id, int value) {std::lock_guard<std::mutex> lock(db_mutex);std::this_thread::sleep_for(std::chrono::milliseconds(15)); // 模拟写入延迟// 实际更新操作省略}
};// 请求类
struct Request {int id;int value;bool isRead; // true if read, false if write
};// 处理请求的工作线程
class Worker {
public:Worker(std::shared_ptr<Database> db) : db_(db) {}void operator()(const Request& request) {auto start = std::chrono::high_resolution_clock::now();if (request.isRead) {int result = db_->getData(request.id);std::cout << "Read request for ID " << request.id << ": " << result << std::endl;} else {db_->updateData(request.id, request.value);std::cout << "Write request for ID " << request.id << " with new value " << request.value << std::endl;}auto end = std::chrono::high_resolution_clock::now();std::chrono::duration<double, std::milli> elapsed = end - start;std::cout << "Request ID " << request.id << " processed in " << elapsed.count() << " ms" << std::endl;}private:std::shared_ptr<Database> db_;
};#include <gperftools/profiler.h>
#include <gperftools/heap-profiler.h>int main() {ProfilerStart("cpu-profiler.prof");HeapProfilerStart("mempry-profiler");std::shared_ptr<Database> db = std::make_shared<Database>();std::vector<std::thread> workers;std::vector<Request> requests;// 生成请求for (int i = 0; i < 1000; ++i) {requests.emplace_back(Request{i, i * 100, i % 2 == 0});}// 创建并启动工作线程for (auto& req : requests) {workers.emplace_back(Worker(db), std::ref(req));}// 等待所有线程完成for (auto& worker : workers) {if (worker.joinable()) {worker.join();}}ProfilerStop();HeapProfilerDump("test");HeapProfilerStop();return 0;
}
2.操作命令
[root@iZj6c0il0t6l26ze71vq4cZ build]# ls
bin  hot.cpp  include  lib  share
[root@iZj6c0il0t6l26ze71vq4cZ build]# g++ -o main hot.cpp -I./include -L ./lib -lprofiler -ltcmalloc -lpthread
[root@iZj6c0il0t6l26ze71vq4cZ build]# ls
bin  hot.cpp  include  lib  main  share
[root@iZj6c0il0t6l26ze71vq4cZ build]# ./main > /dev/null
Starting tracking the heap
PROFILE: interrupts/evictions/bytes = 9/0/672
Dumping heap profile to mempry-profiler.0001.heap (test)
[root@iZj6c0il0t6l26ze71vq4cZ build]# ls
bin  cpu-profiler.prof  hot.cpp  include  lib  main  mempry-profiler.0001.heap  share
[root@iZj6c0il0t6l26ze71vq4cZ build]# bin/pprof ./main ./cpu-profiler.prof --text
Using local file ./main.
Using local file ./cpu-profiler.prof.
Total: 9 samples2  22.2%  22.2%        2  22.2% __GI___munmap2  22.2%  44.4%        2  22.2% __pthread_create_2_11  11.1%  55.6%        1  11.1% MallocHook::InvokeDeleteHookSlow (inline)1  11.1%  66.7%        1  11.1% SpinLock::Lock (inline)1  11.1%  77.8%        1  11.1% __futex_abstimed_wait_common1  11.1%  88.9%        1  11.1% std::forward1  11.1% 100.0%        1  11.1% std::locale::id::_M_id@@GLIBCXX_3.40   0.0% 100.0%        1  11.1% DeleteHook0   0.0% 100.0%        2  22.2% MallocHook::InvokeDeleteHook (inline)0   0.0% 100.0%        2  22.2% MallocHook::InvokeDeleteHookSlow0   0.0% 100.0%        1  11.1% RecordFree (inline)0   0.0% 100.0%        1  11.1% SpinLockHolder::SpinLockHolder (inline)0   0.0% 100.0%        1  11.1% Worker::operator0   0.0% 100.0%        2  22.2% __GI___nptl_deallocate_stack0   0.0% 100.0%        3  33.3% __clone30   0.0% 100.0%        3  33.3% __gnu_cxx::new_allocator::construct0   0.0% 100.0%        6  66.7% __libc_start_call_main0   0.0% 100.0%        6  66.7% __libc_start_main_alias_20   0.0% 100.0%        2  22.2% __nptl_free_stacks0   0.0% 100.0%        3  33.3% __pthread_clockjoin_ex0   0.0% 100.0%        6  66.7% _start0   0.0% 100.0%        2  22.2% invoke_hooks_and_free0   0.0% 100.0%        6  66.7% main0   0.0% 100.0%        3  33.3% start_thread0   0.0% 100.0%        1  11.1% std::__invoke0   0.0% 100.0%        1  11.1% std::__invoke_impl0   0.0% 100.0%        3  33.3% std::allocator_traits::construct0   0.0% 100.0%        3  33.3% std::error_code::default_error_condition@@GLIBCXX_3.4.110   0.0% 100.0%        1  11.1% std::num_put::_M_insert_float0   0.0% 100.0%        1  11.1% std::ostream::_M_insert0   0.0% 100.0%        1  11.1% std::thread::_Invoker::_M_invoke0   0.0% 100.0%        1  11.1% std::thread::_Invoker::operator0   0.0% 100.0%        2  22.2% std::thread::_M_start_thread@@GLIBCXX_3.4.220   0.0% 100.0%        1  11.1% std::thread::_State_impl::_M_run0   0.0% 100.0%        1  11.1% std::thread::_State_impl::_State_impl0   0.0% 100.0%        2  22.2% std::thread::_State_impl::~_State_impl0   0.0% 100.0%        3  33.3% std::thread::join@@GLIBCXX_3.4.110   0.0% 100.0%        3  33.3% std::thread::thread0   0.0% 100.0%        1  11.1% std::use_facet0   0.0% 100.0%        3  33.3% std::vector::emplace_back
[root@iZj6c0il0t6l26ze71vq4cZ build]#
3.结果分析

在这里插入图片描述

根据上述数据,对关键函数分析

__GI___munmap__pthread_create_2_1

  • 这两个函数各占用了22.2%的样本,是CPU使用最多的两个函数。__GI___munmap 被用于内存映射的取消,通常与资源释放相关。而 __pthread_create_2_1 与线程创建相关,表明线程的创建和管理是CPU消耗的一个重要部分。

内存和同步相关的函数

  • MallocHook::InvokeDeleteHookSlowSpinLock::Lock 分别占用了11.1%的样本。这显示内存分配和线程同步也是性能消耗的关键点。

其他系统调用和库函数

  • __futex_abstimed_wait_commonstd::forward 也各占有11.1%,表明系统级同步和模板函数的使用对性能有较大影响。

六、后论

通过这篇博客,应该能够掌握使用 gperftools 来分析和优化 C++ 应用程序的基本方法。无论是 CPU 还是内存优化,gperftools 都是一个强大的工具,可以帮助程序提升应用性能。

gperftools 提供了主要包括 CPU 分析器和堆分析器。适合用来识别程序中的性能热点和内存泄漏。对于想要进行全面性能和资源优化的需求来说,可能还需要考虑一些其他工具和技术作为 gperftools 的互补。

以下是对 gperftools 工具的一些补充:

  1. Valgrind

    • Valgrind 是一个编程工具套件,用于内存调试、内存泄漏检测以及性能分析。虽然 gperftools 的堆分析器能够帮助检测内存泄漏,Valgrind 的 Memcheck 工具在某些情况下可能提供更详细的内存访问和泄漏信息。
  2. AddressSanitizer

    • AddressSanitizer (ASan) 是一个快速的内存错误检测器,可以检测出各种内存访问错误。ASan 被集成在 LLVM/Clang 和 GCC 中,与 gperftools 相比,它在运行时插入的检查可以自动发现如使用后释放、堆栈缓冲区溢出等错误。
  3. ThreadSanitizer

    • ThreadSanitizer (TSan) 是用于检测数据竞争的工具。如果应用程序涉及复杂的多线程,TSan 可以作为 gperftools 的有力补充,帮助识别可能导致不稳定行为和奇怪的 bug 的数据竞争问题。
  4. VisualVM

    • 对于需要分析 Java 应用程序性能的开发者,VisualVM 提供了一套完整的可视化工具,包括线程分析、堆分析和 CPU 分析等。虽然这不是针对 C++ 的工具,但它展示了集成性能分析工具的方向,对于混合语言开发环境非常有用。
  5. Intel VTune Profiler

    • 对于需要在硬件层面上进行性能分析的开发者,Intel VTune Profiler 提供了深入的硬件级性能洞察,包括 CPU 利用率、缓存命中率、分支预测错误等。这对于优化依赖于 CPU 性能的应用程序特别有价值。
  6. Perf

    • Perf 是 Linux 下的一个性能计数器工具,它可以访问 CPU 性能计数器、追踪点等,用于更底层的性能分析。Perf 能够帮助开发者分析应用程序与操作系统之间的交互,并识别潜在的性能问题。
  7. SystemTap

    • SystemTap 提供了一种方法,允许管理员和开发者在 Linux 系统上运行的实时内核中编写和执行脚本,以收集关于系统的运行信息。这是研究和解决操作系统级性能问题的一个强大工具。

通过将 gperftools 与这些工具结合使用,可以获得更全面的性能分析视角,有效地优化和改进软件。

the end~


- 推荐文章 -

C++

  • C++11 玩家不得不学的语法集 [持续更新-建议收藏]
  • C++ 性能分析的实战指南(gperftools工具)
  • C++哈希算法(即散列表)的说明和代码示例

音视频

  • libyuv 操作 YUV420P ,如镜像、旋转、缩放等示例代码

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

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

相关文章

【Cookie和Session的区别(面试重点)】

Cookie和Session的区别 1. Cookie1.1 认识Cookie1.2 Cookie的引出1.3 Cookie工作原理1.4 Cookie重要结论 2. Session2.1 认识Session2.2 Session的引出2.3 Session的工作原理 3. Cookie和Session的区别 1. Cookie 1.1 认识Cookie Cookie&#xff1a;是小型文本文件&#xff0…

实验七 智能手机互联网程序设计(微信程序方向)实验报告

请编写一个用户登录界面&#xff0c;提示输入账号和密码进行登录&#xff0c;要求在输入后登陆框显示为绿色&#xff1b; 二、实验步骤与结果&#xff08;给出对应的代码或运行结果截图&#xff09; index.wxml <view class"content"> <view class"a…

概率图模型在机器学习中的应用:贝叶斯网络与马尔可夫随机场

&#x1f9d1; 作者简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

C++ stl容器list的底层模拟实现

目录 前言&#xff1a; 1.创建节点 2.普通迭代器的封装 3.反向迭代器的封装 为什么要对正向迭代器进行封装&#xff1f; 4.const迭代器 5.构造函数 6.拷贝构造 7.赋值重载 8.insert 9.erase 10.析构 11.头插头删&#xff0c;尾插尾删 12.完整代码简单测试 总结&…

Iterator 迭代器

意图 提供一个方法顺序访问一个聚合对象中的各个元素&#xff0c;且不需要暴漏该对象的内部表示。 结构 Iterator&#xff08;迭代器&#xff09;定义访问和遍历元素的接口。ConcreteIterator&#xff08;具体迭代器&#xff09;实现迭代器接口&#xff1b;对该聚合遍历是跟踪…

爽!极品AI大模型,抓紧收藏!

今天给大家分享一个基于视觉和文本的聊天机器人&#xff0c;使用DeepSeek-VL-7B模型提供文本和图像的自动化生成回复&#xff0c;它允许用户在与机器人交互时提交文本和图像输入。 DeepSeek-VL简介 DeepSeek-VL系列代表了在多模态AI领域的一大突破&#xff0c;提供了两种不同规…

阿赵UE学习笔记——30、HUD简单介绍

阿赵UE学习笔记目录 大家好&#xff0c;我是阿赵。   继续学习虚幻引擎&#xff0c;这次来学习一下HUD的基础使用。 一、 什么是HUD HUD(Head-Up Display)&#xff0c;也就是俗称的抬头显示。很多其他领域里面有用到这个术语&#xff0c;比如开车的朋友可能会接触过&#xf…

Linux驱动开发——(四)内核定时器

一、内核的时间管理 1.1 节拍率 Linux内核中有大量的函数需要时间管理&#xff0c;比如周期性的调度程序、延时程序等等&#xff0c;对于驱动编写者来说最常用的是定时器。 硬件定时器提供时钟源&#xff0c;时钟源的频率可以设置&#xff0c;设置好以后就周期性的产生定时中…

将MySQL数据库查询结果导出为txt文档,并建成实体类

目录 第一章、功能需求和分析1.1&#xff09;具体需求1.2&#xff09;分析需求转为小的问题1、如何获得数据库表的字段&#xff1f;2、如何将数据库查询结果导出&#xff1f;3.将获得的数据库查询结果转为驼峰式4.让AI建个实体类 友情提醒: 先看文章目录&#xff0c;大致了解文…

Python Selenium无法打开Chrome浏览器处理自定义浏览器路径

问题 在使用Python Selenium控制Chrome浏览器操作的过程中&#xff0c;由于安装的Chrome浏览器的版本找不到对应版本的驱动chromedriver.exe文件&#xff0c;下载了小几个版本号的驱动软件。发现运行下面的代码是无法正常使用的&#xff1a; from selenium import webdriver …

2024年【北京市安全员-B证】考试题及北京市安全员-B证考试试卷

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 北京市安全员-B证考试题参考答案及北京市安全员-B证考试试题解析是安全生产模拟考试一点通题库老师及北京市安全员-B证操作证已考过的学员汇总&#xff0c;相对有效帮助北京市安全员-B证考试试卷学员顺利通过考试。 1…

第十六届“华中杯”B 题使用行车轨迹估计交通信号灯周期问题

某电子地图服务商希望获取城市路网中所有交通信号灯的红绿周期,以便为司机提供更好的导航服务。由于许多信号灯未接入网络,无法直接从交通管理部门获取所有信号灯的数据,也不可能在所有路口安排人工读取信号灯周期信息。所以,该公司计划使用大量客户的行车轨迹数据估计交通…

RISC-V CVA6 在 Linux 下相关环境下载与安装

RISC-V CVA6 在 Linux 下相关环境下载与安装 所需环境与源码下载 CVA6 源码下载 首先&#xff0c;我们可以直接从 GitHub 一次性拉取所有源码&#xff1a; git clone --recursive https://github.com/openhwgroup/cva6.git如果这里遇到网络问题&#xff0c;拉取失败&#x…

语音聊天app软件、语音房软件开发

最近我们收到了众多客户咨询,他们都对语音聊天app非常感兴趣! 语音聊天app,在线组CP,一起连麦聊天、唱歌、打游戏,年轻人非常喜欢的语音社交软件,可以语音通话、多人语音房聊天、发布动态、会员充值等功能.大家可以在虚拟世界里快乐社交! 里面还有好玩的互动小游戏,帮助客户增…

MLLM | Mini-Gemini: 挖掘多模态视觉语言大模型的潜力

香港中文、SmartMore 论文标题&#xff1a;Mini-Gemini: Mining the Potential of Multi-modality Vision Language Models Code and models are available at https://github.com/dvlab-research/MiniGemini 一、问题提出 通过更高分辨率的图像增加视觉标记的数量可以丰富…

电磁仿真--基本操作-CST-(2)

目录 1. 回顾基操 2. 操作流程 2.1 创建工程 2.2 修改单位 2.3 创建 Shape 2.4 使用拉伸 Extrude 2.5 修改形状 Modify Locally 2.6 导入材料 2.7 材料解释 2.8 材料分配 2.9 查看已分配的材料 2.10 设置频率、背景和边界 2.11 选择 Edge&#xff0c;设置端口 2.…

npm install 卡在still idealTree buildDeps不动

前言 再使用npm install 安装包依赖时 发现一直卡住 停留在 观察node_cache下的_logs文件 发现一直在拉取包 37 silly idealTree buildDeps 38 silly fetch manifest riophae/vue-treeselect0.4.0尝试解决 尝试设置了taobao镜像源 依然如此 获取已经设置的镜像源 确实是ta…

软文发稿对于企业的重要性

随着社会的发展和科技的进步&#xff0c;软文发稿已成为企业和个人推广和传播信息的一种非常重要的方式。它以隐性的广告形式&#xff0c;通过内容发布&#xff0c;为品牌广告和产品推广铺设了一条隐形高速公路。下面我们就详细解析一下软文发稿的优点和好处。 软文发稿帮助增…

setTimeout回调函数 this指向问题

本文主要介绍setTimeout的回调函数的this指向问题 例子1&#xff1a;回调函数是一个普通函数 setTimeout 的回调函数是一个普通函数&#xff0c;而不是箭头函数&#xff0c;因此它有自己的上下文&#xff0c;this 指向全局对象&#xff08;在浏览器中是 window 对象&#xff…

【linux】匿名管道|进程池

1.进程为什么要通信&#xff1f; 进程也是需要某种协同的&#xff0c;所以如何协同的前提条件(通信) 通信数据的类别&#xff1a; 1.通知就绪的 2.单纯的数据 3.控制相关的信息 2.进程如何通信&#xff1f; 进程间通信&#xff0c;成本会高一点 进程间通信的前提&#xff0c;先…