新手友好 | 如何使用Tracy完成在线的性能分析

科技   2024-11-14 17:20   湖北  

0. 简介


Tracy(https://github.com/wolfpld/tracy/releases/latest/download/tracy.pdf)是一款实时、纳秒级分辨率的混合帧和采样剖析器,可用于远程或嵌入式遥测游戏和其他应用程序。它可以对 CPU(C、C++11、Lua)、GPU(OpenGL、Vulkan、Direct3D 11/12、OpenCL)和内存进行剖析。它还能监控线程所持有的锁,并显示竞争发生的位置。Tracy 可以像其他统计分析工具(如 VTune、perf 或 Very Sleepy)一样,对取样的调用堆栈数据进行统计分析,但它主要侧重于对源代码进行手动标记,以便逐帧检查程序的执行情况。你将能清楚地看到哪些函数被调用,在这些函数中花费了多少时间,以及它们在多线程环境中是如何相互影响的。相比之下,统计分析可能会让你看到代码中的hot spots,但却无法准确找出每隔几秒就会出现的半随机帧卡顿的根本原因。尽管 Tracy 以帧剖析为目标,重点分析实时应用程序(如游戏)中的帧时间,但它也适用于不使用帧概念的实用程序。Tracy中分为两个部分,一个是Client ,采样数据的生产者,即我们要分析的程序。另一个是Server ,采样数据的接受者,同时是一个数据的viewer,并支持数据存储,以及导出csv。相关的代码在Github(https://github.com/wolfpld/tracy)上。


具体流程大概为:

1、将Tracy存储库添加到您的项目目录中。

2、项目/tracy/public目录中包含Tracy源文件。

3、将TracyClient.cpp作为源文件添加。

4、将tracy/Tracy.hpp作为包含文件添加。

5、在您有兴趣进行性能分析的每个文件中包含Tracy.hpp。

6、为整个项目定义TRACY_ENABLE。

7、在每个帧循环的末尾添加宏FrameMark。

8、在您的函数定义的第一行添加宏ZoneScoped,以便将其包含在性能分析中。

9、编译并运行您的应用程序和性能分析服务器。

10、在性能分析服务器上点击连接。

11、完成!您正在对您的程序进行性能分析!



1. 所需的源文件


这里是client的关键部分,路径在https://github.com/wolfpld/tracy/tree/master/public下:



两个关键文件夹:common/、client/,以及从根目录复制出来的TracyClient.cpp和相关文件。我也在使用OpenGL进行性能分析,但您可以根据需要删除其他文件。我保留了它们,因为在添加后端时这样做比较容易。我不使用lua,所以我将其移除。我将这些文件复制到我的项目源代码文件夹下的src/tracy/目录中,例如 src/tracy/common。




2. 编译文件


您只需将TracyClient.cpp包含到构建中。根据需要将其添加到您的项目、Makefile或构建脚本中。


您需要两件事:


在您的代码中添加一个定义- TRACY_ENABLE 在您的构建中添加额外的包含路径 - src/tracy/ 然后它就可以找到其包含文件,从而告诉分析器它的存在。如果您删除TRACY_ENABLE,那个构建中就没有分析代码,也没有来自分析器的额外开销。


Premake已经包含了includedirs { "src/tracy/" }defines { "TRACY_ENABLE" },您可以添加或编辑。在Visual Studio中,对于包含路径,它是 属性 -> C/C++ -> 常规 -> 附加包含目录 对于定义,它是 属性 -> C/C++ -> 预处理器 -> 预处理器定义


对于clang或GCC在命令行上,看起来像这样:-DTRACY_ENABLE -Itracy/


如果是


Windows头文件错误不好处理。如果您遇到一些特别是围绕错误C2011:'sockaddr': 'struct'类型重定义,尝试向您的项目定义中添加一个定义WIN32_LEAN_AND_MEAN。这与windows.h和winsock 2与winsock原始API有关,相当混乱。


您也可以尝试在包含tracy头文件之前定义这个。


#define WIN32_LEAN_AND_MEAN#include "Tracy.hpp"


2.1 项目打包导入


如果整个项目导入到当中,我们可以发现在CmakeList中有对应的封装


add_library(TracyClient ${TRACY_VISIBILITY} "${TRACY_PUBLIC_DIR}/TracyClient.cpp")target_compile_features(TracyClient PUBLIC cxx_std_11)target_include_directories(TracyClient SYSTEM PUBLIC    $<BUILD_INTERFACE:${TRACY_PUBLIC_DIR}>    $<INSTALL_INTERFACE:include>)target_link_libraries(    TracyClient    PUBLIC        Threads::Threads        ${CMAKE_DL_LIBS})


然后我们就可以使用下面的代码导入Tracy


if (PERCEPTION_ENABLE_TRACY)    ## tracy profile    target_compile_definitions(${OBJECT_PERCEPTION_LIB_NAME} PUBLIC "PERCEPTION_PROFILE=$<IF:$<BOOL:${PERCEPTION_ENABLE_TRACY}>,1,0>")    target_link_libraries(${OBJECT_PERCEPTION_LIB_NAME} PUBLIC $<IF:$<BOOL:${PERCEPTION_ENABLE_TRACY}>,Tracy::TracyClient,>)endif()


并指定Tracy相关的配置


set(CMAKE_POSITION_INDEPENDENT_CODE ON)if (PERCEPTION_ENABLE_TRACY)  message(STATUS "Enable tracy profiling")  set(TRACY_STATIC ON)  option(TRACY_ENABLE "" ON)  set(TRACY_ONLY_IPV4 ON CACHE BOOL "" FORCE )  add_definitions(-DTRACY_CLIENT_ADDRESS="255.255.255.255")  add_subdirectory(tracy)endif()


2.2 软件打包


使用


cd tracy/profiler/mkdir build && cd buildcmake ..make -j10




3. 包含和使用


首先要包含Tracy。如果需要的话,可以使用C头文件。


#include "Tracy.hpp"


帧标记


接下来,我们要告诉Tracy你的帧标记在哪里。在引擎的tick或update函数中,添加FrameMark("main");。这样就足够让Tracy开始工作了。如果你构建并编译,你应该能够连接Tracy应用程序并查看帧时序。


打开Tracy性能分析应用程序


既然我们已经在项目中包含了代码,这就是第一步。这意味着我们的游戏(客户端)能够发送信息出去。它需要一个地方来发送信息,要么是一个名为capture的命令行工具,要么是一个带有用户界面的性能分析应用程序。现在我们需要的是带有用户界面的应用程序。


通常你可以从Tracy的发布下载部分获取匹配的版本。7z文件是一个包含文件的zip文件,比如Tracy.exe,这就是应用程序。


运行这个应用程序,然后运行你的游戏,你应该能够看到“已发现的客户端”列表中出现你的应用程序。我在一个名为7dfps-0.0.9.exe的游戏上运行了这个操作,所以点击列表中的7dfps-0.0.9.exe应用程序将启动一个性能分析会话。



请注意,Tracy也可以通过网络进行工作,因此您可以在不同的计算机上运行游戏的Linux版本,并从Windows(或移动设备、或控制台设备等)进行性能分析。


作用域测量


接下来,我们可能想要专门对某些范围进行性能分析,比如render函数。在顶部添加ZoneScopedN("render");,这将为该范围自动添加一个命名区域到性能分析中。还有ZoneScoped;。


就是这样!现在您应该能够在帧内的某个地方看到一个单一的区域。



4. 清洁集成


由于我使用或尝试了多个性能分析工具,包括自定义的工具,我喜欢使用宏来共享性能分析标签。因此,对于Tracy,我有类似这样的东西。


#include "Tracy.hpp"#define LUXE_LOCK_SCOPE(name) ZoneScopedN(name)#define LUXE_PROFILE ZoneScoped#define LUXE_PROFILE_FRAME(x) FrameMark#define LUXE_FRAME_MARKER() FrameMark#define LUXE_LOCK(type, var, name) TracyLockableN(type, var, name)#define LUXE_LOCK_MARKER(var) LockMark(var)#define LUXE_SET_THREAD_NAME(name) tracy::SetThreadName(name)#define LUXE_PROFILE_SECTION(x) ZoneScopedN(x)#define LUXE_PROFILE_TAG(y, x) ZoneText(x, strlen(x))#define LUXE_PROFILE_LOG(text, size) TracyMessage(text, size)#define LUXE_PROFILE_VALUE(text, value) TracyPlot(text, value)


LUXE_LOCK_SCOPE(name) - 定义了一个作用域级的性能分析区域(zone),通常用于监测一个作用域(如一个函数或一个代码块)的执行时间。ZoneScopedN 是 Tracy 的一个宏,它自动记录当前作用域的性能数据,其中 name 提供了该作用域的标签。


LUXE_PROFILE - 类似于 LUXE_LOCK_SCOPE,但通常用于无需自定义名称的情况。ZoneScoped 也是 Tracy 的宏,用于自动记录当前作用域的性能,但不提供自定义名称。


LUXE_PROFILE_FRAME(x) - 用于标记帧的结束,通常用在图形应用或游戏的渲染循环中。FrameMark 是 Tracy 的宏,用于在性能分析中标记一帧的结束。


LUXE_FRAME_MARKER() - 同 LUXE_PROFILE_FRAME(x),但没有参数。


LUXE_LOCK(type, var, name) - 用于跟踪多线程中锁的使用情况。TracyLockableN 是 Tracy 的宏,它记录与指定锁(如互斥锁)相关的性能数据,其中 type 是锁的类型,var 是锁的变量名,name 提供了锁的标签。


LUXE_LOCK_MARKER(var) - 用于标记锁的获取或释放的特定点。LockMark 是 Tracy 的宏,用于在性能分析中标记锁的状态改变。


LUXE_SET_THREAD_NAME(name) - 设置当前线程的名称,使其在 Tracy 的分析工具中显示。这对于区分和跟踪多线程程序中的不同线程非常有用。


LUXE_PROFILE_SECTION(x) - 定义了一个具有自定义名称的性能分析区域,与 LUXE_LOCK_SCOPE 类似。


LUXE_PROFILE_TAG(y, x) - 为当前性能分析区域添加一个文本标签,其中 x 是文本内容。


LUXE_PROFILE_LOG(text, size) - 向 Tracy 日志添加一条消息,text 是消息文本,size 是文本的长度。


LUXE_PROFILE_VALUE(text, value) - 在 Tracy 的实时图表中绘制一个值,text 是该值的标签。



5. 内存分析器


还有一个非常酷的内存分析器,你应该研究一下。luxe中的所有内存都经过自定义分配器,因此很容易添加性能分析。出于某种原因,我使用了C API。12是堆栈深度。


#include "TracyC.h"#define LUXE_PROFILE_ALLOC(p, size) TracyCAllocS(p, size, 12)#define LUXE_PROFILE_FREE(p) TracyCFreeS(p, 12)


有了这个,我可以看到所有的内存分配、释放、调用堆栈等。



6. OpenGL性能分析


首先通过TracyGpuContext告诉Tracy你的上下文。这是在你的OpenGL上下文初始化之后,任何GPU区域被分析之前调用的。


SDL_GL_CreateContext(window);TracyGpuContext;


然后定义一个GPU区域宏


  #include "TracyOpenGL.hpp"  #define LUXE_PROFILE_GPU(x) TracyGpuZone(x


然后我可以在作用域内使用它,比如 LUXE_PROFILE_GPU("use program"); LUXE_PROFILE_GPU("draw indexed");


这些被添加在作用域的顶部,任何OpenGL调用都将在其中进行测量,即使它们是异步的。


在交换缓冲区后,添加 TracyGpuCollect;这将把信息发送到Tracy。


SDL_GL_SwapWindow(window);TracyGpuCollect;



7. 成果展示


7.1 最终结果


凭借如此少的努力,您已经获得了大量信息



7.2 以管理员身份运行


如果你以管理员身份运行,Tracy就可以获取更多信息,比如系统中所有线程的信息,并可以显示代码运行在哪些线程上的上下文切换。它提供了更多的信息



7.3 装配视图


还有拆卸视图,包括指令细节等等。真的非常酷






8. 界面解释


8.1 初始化连接界面



button功能介绍:

📇Manual:点击该按钮,可以跳转到用户使用手册。

🌎Web:点击该按钮,会跳转到Tracy官方主页以及功能演示视频的网页。

💬Chat:点击该按钮,可以跳转到该工具的讨论社区。

🤍Sponsor:点击该按钮,可以给开源社区的建设者们提供资金支持。

🔧:Abort Tracy。

button:可以查看历史连接记录,快速重连之前的IP和对应的端口。

Connect:想要实时捕获,输入IP和port之后点击connect可以查看实时性能。

Open saved trace:如果你想打开保存好的数据文件,你可以点击该按钮,查看捕捉文件的程序性能。


8.2 连接成功信息弹窗



Save trace:使用 Save trace(保存跟踪)按钮将当前配置文件数据保存到文件中。


Stop:点击stop按钮,断开与客户端的连接。


Discard:按Discard按钮用于丢弃当前配置文件数据。


8.3 profiler 主窗口



8.3.1. ⚙️Options:



Draw empty labels :默认情况下,没有任何内容显示的线程会被隐藏。启用此选项后,这些线程还是会显示出来。


Draw frame targets:如果启用,当前所选帧集中任何帧的时间区域、超过指定目标 FPS 值的时间区域将在时间线视图上以红色背景标记。


Draw context switches:勾选该选项,显示在timeline上线程状态的切换。


Darken inactive threads:勾选该选项,将inactive threads在Main Thread Zone 绘制,颜色加深。


Draw CPU data:勾选该选项以绘制CPU数据。


Draw CPU usage graph:勾选该选项会显示CPU使用率图。


Draw stack samples:勾选该选项,将会在Main Thread timeline 上方以点的方式绘制堆栈样本,鼠标移动到点上,会显示具体信息。


Draw CPU zones:勾选该选项,可以显示 CPU 区段。


Zone colors:内核态的调用颜色会比用户态颜色更深。


Zone name shortening:调用接口参数是否显示,有以下选项:


Disabled:不缩短区域名称,名称始终以全名显示。(例如 bool ns::container::add(const float&))。 Minimal length :只有函数名即可。Only normalize :仅去掉函数参数,但不删除名称空间(如 ns::container<>::add() )。AS needed :只有在没有空间显示完整的区名时,才会执行名称缩短步骤。直到名称适合可用空间,或缩写不再可行(例如:container<>:: add() AS needed + normalize :同上,但区段名称归一化总是要执行的、即使整个区域名称符合可用空间。


Draw plots:勾选该选项,可以显示CPU 使用率时间线。


Visible threads:点击该选项,可以指定某些thread是否被展示。


Visable frame sets:点击该选项,可在此处启用或禁用帧集显示。


8.3.2. Messages:切换消息日志窗口,该窗口显示客户端发送的自定义消息。


当你的程序中的代码很庞大、很复杂的时候,你或许会想到打log去调试相应的代码,Messages的功能就在于可以让你快速定位到你的log所在thread。


Tracy 提供了消息日志功能。您可以使用(例如,典型的调试输出)。或者使用TracyMessageL(text) 宏来发送字符串字面信息。如果你想对信息进行颜色编码(例如,使关键信息更容易看清),可以使用 TracyMessageL(text) 宏、可以使用 TracyMessageC(text, size, color) 或 TracyMessageLC(text, color) 宏。


参数:

text:字符串

size:指定显示字符串的大小

color:你可以为你的log进行颜色设置

你也可以在左上角Filter messages中搜索你想要的某一个log 字符串



运行之后的效果:




8.3.3. Find zone:点击按钮可切换查找区段窗口,通过该窗口可检查区段行为统计



Histogram:Self time和Running time不可以同时勾选!!!!


Log values:勾选该选项,可以在y轴上查看调用次数。Log time:勾选该选项,可以在x轴上查看时间分段。Cumulate


time:更改直方图分档值的计算方式。


Self time:删除分析区段中的子区段时间,从而只显示在区段本身(或非仪器功能调用)中花费的时间。或非仪器功能调用中花费的时间。当运行时间激活时无法选择。


Running time:- 删除区的线程执行被操作系统暂停的时间由于其他线程抢占、等待系统资源、锁竞争等原因而被操作系统暂停的时间。仅在


profiler执行上下文切换捕获时可用。当 Self time间激活时无法使用。


Found zones 根据以下标准分组:线程 - 在此模式下,可以查看哪些线程在执行区段。用户文本 - 根据自定义用户文本分割区段。区域名称 - 根据调用设置的名称分组区域。调用堆栈 - 根据起始调用堆栈分组区段。父区 - 根据父区对区段进行分组。这种模式依赖于区段层次结构,而不是调用堆栈信息。无分组 - 禁用区段分组


8.3.4. Statistics:查看时间轴视图可以让你对事物有一个更具体的看法。不过,有时你想查看程序行为的总体概览。例如,您想知道哪个函数占用了程序最多的执行时间。统计窗口恰好能为您提供这些信息。


statistics主要有两种模式:Instrumentation mode && Sampling


勾选Instrmentation,可以看到以下基本信息:Name、Location、Total time、Counts、MTPC


Sampling与Instrmentation有相同之处,但也有几点不同 @ Address:Sampling可以勾选Address选项查看程序的二进制地址 Show all:Samping可以勾选Show all选项查看该程序的所有调用


8.3.5. Memory:(打开抓取的tracy文件,目前没有memory信息,待补充


8.3.6. Compare:该按钮的功能是将程序之间的性能进行比较,在开始工作时,您需要记录一条代表程序通常行为的参考轨迹。然后,在代码优化完成后,再保存记录优化后的程序。最后,打开优化后的程序,在比较窗口中选择 i Open second选项,并加载原来的程序。


通常情况下,轨迹是用颜色和符号编码的。当前轨迹用黄色符号标记,外部轨迹用红色 v 符号标记。


9. 参考链接


https://blog.csdn.net/ahuyccc/article/details/134125422


https://luxeengine.com/integrating-tracy-profiler-in-cpp/


https://blog.csdn.net/ahuyccc/article/details/134125422



讲师招募


招募要求

完成符合要求的机器人相关视频制作

总时长需达到 3小时以上

视频内容需为精品课程,确保高质量和专业性


讲师奖励

享受课程收入分成

赠送 2门 古月学院在售精品课程(训练营除外)


联系我们

添加工作人员微信:GYH-xiaogu




古月居
专业的ROS机器人知识社区和产业服务平台
 最新文章