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 samples
       2  22.2%  22.2%        2  22.2% __GI___munmap
       2  22.2%  44.4%        2  22.2% __pthread_create_2_1
       1  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_common
       1  11.1%  88.9%        1  11.1% std::forward
       1  11.1% 100.0%        1  11.1% std::locale::id::_M_id@@GLIBCXX_3.4
       0   0.0% 100.0%        1  11.1% DeleteHook
       0   0.0% 100.0%        2  22.2% MallocHook::InvokeDeleteHook (inline)
       0   0.0% 100.0%        2  22.2% MallocHook::InvokeDeleteHookSlow
       0   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::operator
       0   0.0% 100.0%        2  22.2% __GI___nptl_deallocate_stack
       0   0.0% 100.0%        3  33.3% __clone3
       0   0.0% 100.0%        3  33.3% __gnu_cxx::new_allocator::construct
       0   0.0% 100.0%        6  66.7% __libc_start_call_main
       0   0.0% 100.0%        6  66.7% __libc_start_main_alias_2
       0   0.0% 100.0%        2  22.2% __nptl_free_stacks
       0   0.0% 100.0%        3  33.3% __pthread_clockjoin_ex
       0   0.0% 100.0%        6  66.7% _start
       0   0.0% 100.0%        2  22.2% invoke_hooks_and_free
       0   0.0% 100.0%        6  66.7% main
       0   0.0% 100.0%        3  33.3% start_thread
       0   0.0% 100.0%        1  11.1% std::__invoke
       0   0.0% 100.0%        1  11.1% std::__invoke_impl
       0   0.0% 100.0%        3  33.3% std::allocator_traits::construct
       0   0.0% 100.0%        3  33.3% std::error_code::default_error_condition@@GLIBCXX_3.4.11
       0   0.0% 100.0%        1  11.1% std::num_put::_M_insert_float
       0   0.0% 100.0%        1  11.1% std::ostream::_M_insert
       0   0.0% 100.0%        1  11.1% std::thread::_Invoker::_M_invoke
       0   0.0% 100.0%        1  11.1% std::thread::_Invoker::operator
       0   0.0% 100.0%        2  22.2% std::thread::_M_start_thread@@GLIBCXX_3.4.22
       0   0.0% 100.0%        1  11.1% std::thread::_State_impl::_M_run
       0   0.0% 100.0%        1  11.1% std::thread::_State_impl::_State_impl
       0   0.0% 100.0%        2  22.2% std::thread::_State_impl::~_State_impl
       0   0.0% 100.0%        3  33.3% std::thread::join@@GLIBCXX_3.4.11
       0   0.0% 100.0%        3  33.3% std::thread::thread
       0   0.0% 100.0%        1  11.1% std::use_facet
       0   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://www.mfbz.cn/a/568307.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

我有一种不完美的完美主义,必须要有缺点,没有缺点就是不完美的

《程客有话说》是我们最新推出的一个访谈栏目&#xff0c;邀请了一些国内外有趣的程序员来分享他们的经验、观点与成长故事&#xff0c;我们尝试建立一个程序员交流与学习的平台&#xff0c;也欢迎大家推荐朋友或自己来参加我们的节目&#xff0c;一起加油。 本期我们邀请的程…

App Inventor 2 如何预览PDF文档?

预览PDF文档的方式 你可以使用Activity启动器查看已存储在你的设备上的 pdf 文档&#xff0c;也可以使用Web客户端通过网址URL打开 pdf 文档。 App Inventor 2 可以使用 .pdf 扩展名从程序包资产中查看 pdf 文件&#xff0c;不再需要外部 pdf 查看器&#xff01; 代码如下&a…

二进制,八进制,十六进制 开头的标识符号

1、引言 每个进制开头的标识符号容易忘&#xff0c;写篇小小博客&#xff0c;拯救一下俺的小脑袋瓜子。 2、进制开头标识符号

Ts类型体操详讲 之 extends infer (下)

目录 1、函数 &#xff08;1&#xff09;提取参数类型 &#xff08;2&#xff09;提取返回值类型 2、构造器 &#xff08;1&#xff09;提取构造器返回值 &#xff08;2&#xff09;提取构造器参数类型 3、索引类型 本章我们继续上节的内容继续&#xff0c;展示我们对ex…

干货教程【AI篇】| 文字生成视频工具图文详细使用教程及整合包下载

需要这个工具整合包的小伙伴可以关注一下文章底部公众号&#xff0c;回复关键词【wzsp】即可获取&#xff0c;双击即可使用无需本地部署。 使用教程 首先我们从链接下载到这个zip压缩文件&#xff1a; 然后我们解压之后进入文件夹&#xff0c;出现下面的界面&#xff1a; 我…

自然语言处理: 第二十八章大模型基底之llama3

项目地址: meta-llama/llama3: The official Meta Llama 3 GitHub site 前言 LLaMa系列一直是人们关注的焦点&#xff0c;Meta在4月18日发布了其最新大型语言模型 LLaMA 3。该模型将被集成到其虚拟助手Meta AI中。Meta自称8B和70B的LLaMA 3是当今 8B 和 70B 参数规模的最佳模…

OceanBase 开发者大会 - 见闻与洞察

文章目录 前言主论坛见闻技术专场见闻产品技术专场技术生态专场 同行论道启发互动展区写在最后 前言 4 月 20 日&#xff0c;我有幸受邀参加了第二届 OceanBase 开发者大会。 50 余位业界知名数据库大咖和数据库爱好者&#xff0c;与来自全国近 600 名开发者相聚。共同探讨一体…

【ks爬虫软件】把快手评论API接口封装成GUI采集工具

用Python开发爬虫采集软件&#xff0c;可自动抓取快手评论数据&#xff0c;且包含二级评论。 快手的评论接口URL&#xff1a; # 请求地址 url https://www.kuaishou.com/graphql开发者模式分析过程&#xff1a; 进而封装成GUI界面软件&#xff0c;如下&#xff1a; 软件效…

排序算法:顺序查找

简介 顺序查找&#xff08;也称为线性查找&#xff09;是一种简单直观的搜索算法。按照顺序逐个比较列表或数组中的元素&#xff0c;直到找到目标元素或搜索完整个列表。 应用场景 数据集比较小&#xff0c;无需使用复杂的算法。数据集没有排序&#xff0c;不能使用二分查找…

重磅发布 | 《网络安全专用产品指南》(第一版)

2017年6月1日&#xff0c;《中华人民共和国网络安全法》正式实施&#xff0c;明确规定“网络关键设备和网络安全专用产品应当按照相关国家标准的强制性要求&#xff0c;由具备资格的机构安全认证合格或者安全检测符合要求后&#xff0c;方可销售或者提供。国家网信部门会同国务…

小程序AI智能名片S2B2C商城系统:解锁内容深耕新境界,助力品牌企业高效定制内容策略

在数字化时代&#xff0c;内容营销已成为品牌企业获取市场份额、增强用户黏性的关键武器。然而&#xff0c;面对海量的互联网信息和复杂多样的社交媒体平台&#xff0c;如何有效地深耕内容&#xff0c;成为众多品牌企业面临的难题。 传统的内容分类与识别方式&#xff0c;往往依…

【SpringCloud】Consul-服务注册中心及配置中心快速入门

【SpringCloud】Consul-服务注册中心及配置中心快速入门 文章目录 【SpringCloud】Consul-服务注册中心及配置中心快速入门1. 下载安装及启动2. 服务注册2.1 引入依赖2.2 yml配置2.3 启动类配置2.4 测试 3. 服务配置3.1 引入依赖3.2 yml配置3.3 创建配置文件3.4 动态刷新配置3.…

(超级详细)JAVA之Stream流分析-------持续更新喔!!!

学习目标&#xff1a; 掌握 Java Stream流的相关api 掌握 Java Stream流的基本实现 掌握 java Stream流的使用场景 代码已经整理上传到了gitee中&#xff0c;有需要的小伙伴可以取查看一下源码点个小心心喔 大家也可以帮我提交一点案例喔&#xff01;&#xff01;&#xff01;&…

水平越权,垂直越权

水平越权和垂直越权 水平越权 首先自己创建一个账号 然后在自己的修改密码&#xff0c;抓包&#xff0c;修改用户名等 但一般都会固定&#xff0c;它会固定当前用户名 垂直越权 不用登录就可以删除 当我们复制管理员的删除地址&#xff0c;然后访问它 它会跳出登录地址&#…

美国签证证件照规格要求(51mm*51mm)

美国签证证件照规格要求&#xff08;51mm*51mm&#xff09;

解码数据世界:统计学入门与应用指南

引言 统计学可以被定义为研究数据的科学&#xff0c;它涉及到数据的收集、分析、解释和呈现。其目标是从数据中提取有意义的信息&#xff0c;并使用这些信息来做出推断与决策。 统计学主要分别以下几个主要领域&#xff1a; 描述性统计&#xff1a;使用图表、图形和其他工具…

计算二维主应力的前端界面

<!DOCTYPE html> <html> <head> <title>二维主应力</title> </head> <body> <h2>计算二维主应力</h2> <form> <label for"input1">σ_1(Mpa):</label> <input type"t…

【数据结构】stack queue —— 栈和队列

前言 这阵子一直在学数据结构&#xff0c;知识点消化地有点慢导致博客一直没写&#xff0c;现在总算是有时间歇下来补补前面落下的博客了。从现在起恢复周更&#xff0c;努努力一周两篇也不是梦……闲话少说&#xff0c;今天就让我们一起来认识栈和队列 1. 栈的介绍和使用 栈…

《HCIP-openEuler实验指导手册》1.4 Apache MPM工作模式调整

MPM介绍 二、配置步骤 查看MPM当前工作模式 方法一&#xff1a; httpd -M | grep mpm方法二&#xff1a; 浏览器访问&#xff1a;http://IP:端口/server-status 方法三&#xff1a; cat /etc/httpd/conf.modules.d/00-mpm.conf查看 LoadModule mpm_event_module modules/mo…

C语言 字符类型

下面 我们来说字符类型 我们来看这个 保险单 金额 和 总额 都可以用数字类型 而性别则需要字符型 字符数据的存储 – ASCI码 字符类型 char 就是专为存储字符(如字母&#xff0c;标点和数字)而设计的类型。 使用单引号包含单个字符或转义字符去表示一个 char 类型的常量。 …