自学内容网 自学内容网

C++(Qt)软件调试---VS性能探查器(27)


更多精彩内容
👉内容导航 👈
👉C++软件调试 👈

1 概述🐜

软件开发中程序性能优化、性能瓶颈排查对开发人员来说是必不可少的技能。

常见的性能优化包括CPU使用率、内存使用率、内存泄漏、文件IO等。

linux下的perf工具功能非常强大,但是可惜不支持Windows;而windows中VS也提供了类似的工具,本文主要讲解VS性能探查器常用工具的基本使用和注意事项。

Visual Studio的性能探查器是开发人员用于检测和优化应用程序性能的重要工具。

不同版本的VS提供的性能探查器有一定区别,旧版本的VS中性能探查器包含的工具种类要少一些。

性能探查器中提供的工具包括:

VS提供的这些工具其中部分适用于C++程序开发,部分不适应。

演示环境:

环境版本
系统Windows11
IDEVS2017、VS2022

2 VS工具说明

2.1 使用场景

性能工具说明何时使用此工具?
CPU 使用率显示 CPU 耗用时间的位置。• 开始调查一般的性能问题。
• 调查高服务器 CPU 使用率。
• 调查 DevOps 场景,例如订单无法发送到零售网站的情况。
• 优化 CPU 使用率。
• 调查 API 调用中的延迟。
火焰图在 CPU 使用率工具中查看,该工具提供调用树的备用可视化效果。调查 API 调用中的延迟
热路径在 CPU 使用率工具中查看,该工具显示应用程序耗用大部分 CPU 时间的位置。调查 API 调用中的延迟
内存使用率显示应用内存以查找内存泄漏等问题。• 优化内存使用情况
• 调查 UI 冻结
• 调查可疑内存泄漏(本机代码)
.NET 对象分配显示 .NET 对象的分配位置以及有关垃圾回收的信息。• 优化 .NET 内存使用情况
• 分析垃圾回收
检测显示确切的调用计数和调用时间。• 需要类似于 CPU 使用率的工具,但希望根据壁挂时钟时间确定在函数中花费的确切调用计数和时间。
• 你希望确定被阻止的时间,例如等待锁所用的时间。
• 注意:此工具需要额外的开销。
文件 I/O显示文件 I/O 操作以及它们花费的时间和要处理的数据量。调查 UI 冻结
性能提示显示与代码交互时性能信息的快速度量。调试时,你希望查看上一步操作(或断点)到当前步骤或断点之间的运行时间。
事件查看器显示 HTTP 请求、日志消息和异常。• 调查 API 调用中的延迟
• 调查远程 Web 服务器上运行缓慢的应用程序
.NET Async显示 .NET 应用中的异步/await 使用情况。调查怀疑异步代码存在的性能问题。
.NET 计数器.NET 计数器的实时报告。• 开始调查常规性能问题。
• 需要跟踪基于 .NET 计数器的指标,例如每秒异常数、垃圾回收和 CPU 利用率。
Database显示数据库查询性能。调查使用 ADO.NET 或 Entity Framework Core 的数据库查询的性能。
GPU 使用情况显示 Direct3D 应用的高级硬件使用情况。检查应用性能是受 CPU 限制还是受 GPU 限制。
应用程序时间线显示 XAML 应用的 UI 性能。调查 XAML 应用中的 UI 性能,例如呈现帧所用的时间。
IntelliTrace调试器工具可用于记录事件,并检查应用程序在不同执行点的状态。你需要一个工具来检查应用程序在不同点的状态,而不仅仅是当前应用程序状态。

2.2 工具适用项目

性能工具.NETC/C++UWPASP.NET/ASP.NET Core
CPU 使用率
内存使用率
.NET 对象分配
检测
文件 I/O
性能提示
事件查看器
.NET Async
.NET 计数器是(仅限 .NET Core/5 及更高版本)是(仅限 ASP.NET Core)
Database是(仅限 .NET Core/5 及更高版本)是(仅限 ASP.NET Core)
GPU 使用情况
应用程序时间线是 (XAML)
性能资源管理器
IntelliTrace仅适用于带有 Visual Studio Enterprise 的 .NET仅适用于带有 Visual Studio Enterprise 的 .NET仅适用于带有 Visual Studio Enterprise 的 .NET

3 CPU使用率

  1. 创建一个工程,这里我创建的是一个Qt工程;

  2. 添加如下所示代码:

    #include "QtWidgetsApplication3.h"
    #include <vector>
    #include <list>
    
    using namespace std;
    
    void fun1()
    {
        vector<int> arr;
        for (int i = 0; i < 10000000; i++)
        {
            arr.push_back(i);
        }
    }
    
    void fun2()
    {
        list<int> arr;
        for (int i = 0; i < 10000000; i++)
        {
            arr.push_back(i);
        }
    }
    void fun3()
    {
        vector<int> arr;
        arr.reserve(10000000);
        for (int i = 0; i < 10000000; i++)
        {
            arr.push_back(i);
        }
    }
    
    QtWidgetsApplication3::QtWidgetsApplication3(QWidget *parent)
        : QWidget(parent)
    {
        ui.setupUi(this);
    
        fun1();
        fun2();
        fun3();
    }
    
    QtWidgetsApplication3::~QtWidgetsApplication3()
    {}
    
    
  3. 选择【Debug】模式,【调试】【性能探查器】或者直接按快捷键【Alt + F2】打开;

    • 如果是【Release】模式,需要选择项目,右键【属性】【链接器】【调试】,将生成调试信息项选为【生成调试信息 (/DEBUG)】,用于生成pdb符号表,否则测试结果没办法看。

    在这里插入图片描述

    在这里插入图片描述

  4. 勾选【CPU使用率】,然后点击【开始】按键;

    在这里插入图片描述

  5. 编译运行起来后就开始检测CPU使用率了,当需要停止检测时直接退出程序或者点击【停止收集】就能自动生成分析结果了;

  6. 如下图所示可以看出在【热路径】栏中CPU占用高的函数为fun2(),或者点击【打开详细信息】更容易观看;

    在这里插入图片描述

  7. 如下图所示,可选择【调用方/被调用方】、【调用树】、【模块】、【函数】、【火焰图】几种显示视图;

    • 点击调用方三个矩形模块可以切换调用函数;
    • 调用树适用于函数调用栈不深的情况,对于Qt开发的程序一般函数调用栈都比较深,使用调用树视图很不方便观看;
    • 模块视图可适用于分析使用到的动态库性能;
    • 使用较多的是函数视图,可以直接看哪个函数CPU占用高,然后双击函数名打开所在代码;
    • 火焰图是CPU性能分析常见视图,在使用perf时常常使用火焰图,但是在VS2022中才有,旧版本的VS没有火焰图。

    在这里插入图片描述

  8. 鼠标在生成的报告上右键可以选择保存报告,最好将此时的代码、可执行程序、pdb符号表保存到一起,用于后续回看。

    在这里插入图片描述

  9. 如同所示可分析不同线程的CPU占用率。

    在这里插入图片描述

  10. 如下图所示,在不同视图中鼠标右键打开菜单可以切换使用其它视图显示。

    在这里插入图片描述

  11. 使用火焰图时如下所示,纵轴为函数调用堆栈,横轴为函数占用CPU时长,函数执行越耗时,越宽。

    在这里插入图片描述

4 内存分析

使用内存分析工具可以分析C++程序出现的内存泄露等问题。

4.1 调试模式下分析内存

例如控制台程序,可能运行后立即就退出了,这种情况就需要使用调试模式来分析内存泄漏。

注意:

  1. 最好使用debug模式,如果使用release模式那就需要设置生成pdb符号表。
  2. vs的内存分析工具不使用于大型项目,并且对程序性能影响较大,执行过程中会在C:\Users\MHF\AppData\Local\Temp文件夹中生成大量数据,几分钟可能就几个G或者几十个G,所以如果C盘内存不足也会导致分析失败或者分析速度非常慢。
  1. 创建一个C++工程,代码如下所示:

    #include <iostream>
    
    void fun()
    {
        int* p = new int[10000];
        for (int i = 0; i < 10000; i++)
        {
            p[i] = i;
        }
    }
    
    void fun1()
    {
        int* p = new int[10000];
        for (int i = 0; i < 10000; i++)
        {
            p[i] = i;
        }
        delete[] p;
    }
    int main()
    {
        for (int i = 0; i < 100; i++)
        {
            fun();
            fun1();
        }
        return 0;
    }
    
  2. 如图所示,在需要分析内存的代码段前后打上断点,然后按F5开始调试;

    在这里插入图片描述

  3. 如图所示,在命中第一个断点后,进入【诊断工具】,选择【内存使用率】,点击【堆分析】;

    在这里插入图片描述

  4. 然后点击【截取快照】,就会生成内存镜像;

    在这里插入图片描述

  5. 再按F5继续执行,命中第二个断点,然后点击【截取快照】就会生成第二个内存快照;

    在这里插入图片描述

  6. 可以看出第二个内存快照相对于第一个出现了内存增长;

  7. 双击第二个内存快照;就会出现内存使用窗口;

    在这里插入图片描述

  8. 然后双击使用的内存项,就会出现分配内存的堆栈,如下所示,在fun函数中,文件第8行分配的内存出现内存泄漏;

    在这里插入图片描述

4.2 非调试模式下分析内存

  1. 创建一个Qt程序;添加一个PushButton,在按键槽函数中分配内存不释放;

    在这里插入图片描述

  2. 然后点击【调试】【性能探查器】;

    在这里插入图片描述

  3. 勾选【内存使用率】,点击【开始】;

    在这里插入图片描述

  4. 如下所示,程序启动后,点击【截取快照】,然后点击操作程序分配内存,再次点击【截取快照】,点击【停止收集】或者直接退出程序,当生成分析报告后可看出第二层快照存在内存增长,点击第二层快照就可以看见内存泄漏的堆栈位置了。

    在这里插入图片描述

5 相关地址🐐




原文地址:https://blog.csdn.net/qq_43627907/article/details/144805239

免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!