HazelEngine 学习记录 - Profiling

news2025/7/9 5:53:56

Profiling

Intro To Profiling

为了能够清晰的观察整个引擎的性能消耗情况,我们需要一个可视化的工具来进行性能分析,例如 Unity 的内置 Profiler:

profiler-window-layout

其实本质就是将每个函数运行的时间进行可视化,这里我们借用标准库 chrono 来进行时间的计算

#include <chrono>

template<typename Fn>
class Timer
{
public:
	Timer(const char* name, Fn&& func)
		: m_Name(name), m_Func(func), m_Stopped(false)
	{
		m_StartTimepoint = std::chrono::high_resolution_clock::now();
	}

	~Timer()
	{
		if (!m_Stopped)
			Stop();
	}

	void Stop()
	{
		auto endTimepoint = std::chrono::high_resolution_clock::now();

		long long start = std::chrono::time_point_cast<std::chrono::microseconds>(m_StartTimepoint).time_since_epoch().count();
		long long end = std::chrono::time_point_cast<std::chrono::microseconds>(endTimepoint).time_since_epoch().count();

		m_Stopped = true;

		float duration = (end - start) * 0.001f;
		m_Func({ m_Name, duration });
	}
private:
	const char* m_Name;
	Fn m_Func;
	std::chrono::time_point<std::chrono::steady_clock> m_StartTimepoint;
	bool m_Stopped;
};


创建了一个用于计算时间间隔的类:Timer,在 Timer 的构造函数中首先记录了当前的时刻 std::chrono::high_resolution_clock::now() 其中当前时刻的获取是采用了 chrono 提供给我们的高精度失时钟:high_resolution_clock,然后在析构函数中调用了函数 Stop() 获取到析构时候的当前时间,然后计算时间间隔并进行返回。

之后在函数作用域内创建实例来进行分析:

#define PROFILE_SCOPE(name) Timer timer##__LINE__(name, [&](ProfileResult profileResult) { m_ProfileResults.push_back(profileResult); })
void Sandbox2D::OnUpdate(Hazel::Timestep ts)
{
	PROFILE_SCOPE("Sandbox2D::OnUpdate");

	// Update
	{
		PROFILE_SCOPE("CameraController::OnUpdate");
		m_CameraController.OnUpdate(ts);
	}

	// Render
	{
		PROFILE_SCOPE("Renderer Prep");
		Hazel::RenderCommand::SetClearColor({ 0.1f, 0.1f, 0.1f, 1 });
		Hazel::RenderCommand::Clear();
	}

	{
		PROFILE_SCOPE("Renderer Draw");
		Hazel::Renderer2D::BeginScene(m_CameraController.GetCamera());
		Hazel::Renderer2D::DrawQuad({ -1.0f, 0.0f }, { 0.8f, 0.8f }, { 0.8f, 0.2f, 0.3f, 1.0f });
		Hazel::Renderer2D::DrawQuad({ 0.5f, -0.5f }, { 0.5f, 0.75f }, { 0.2f, 0.3f, 0.8f, 1.0f });
		Hazel::Renderer2D::DrawQuad({ 0.0f, 0.0f, -0.1f }, { 10.0f, 10.0f }, m_CheckerboardTexture);
		Hazel::Renderer2D::EndScene();
	}
}
//SandBox2D.h 
	struct ProfileResult
	{
		const char* Name;
		float Time;
	};

	std::vector<ProfileResult> m_ProfileResults;

这里创建了一个结构体 ProfileResult 用于保存每条记录的时间和名字,定义了一个宏 PROFILE_SCOPE,传入了一个 lamda 表达式用于将返回的名字和时间间隔以 ProfileResult 的形式存储到 vector 容器当中,便于我们后续进行访问。

利用 ImGui 进行显示:

void Sandbox2D::OnImGuiRender()
{
	ImGui::Begin("Settings");
	ImGui::ColorEdit4("Square Color", glm::value_ptr(m_SquareColor));

	for (auto& result : m_ProfileResults)
	{
		char label[50];
		strcpy(label, "%.3fms ");
		strcat(label, result.Name);
		ImGui::Text(label, result.Time);
	}
	m_ProfileResults.clear();

	ImGui::End();
}

显示结果:

在这里插入图片描述

Visual Profiling

之前已经可以显示某个函数运行时间情况了,但是这样的显示结果并不够直观,只是单纯的能够看到某个函数的运行时间,我们需要的是像 Unity 那样的可视化 Profiler,如何做呢?

这里添加了一个专门用于将 Profiling 的结果导出到 json 文件的操作,在原有的 Timer 基础上,新增了部分内容:

#pragma once

#include <string>
#include <chrono>
#include <algorithm>
#include <fstream>

#include <thread>

namespace Hazel {
	struct ProfileResult
	{
		std::string Name;
		long long Start, End;
		uint32_t ThreadID;
	};

	struct InstrumentationSession
	{
		std::string Name;
	};

	class Instrumentor
	{
	private:
		InstrumentationSession* m_CurrentSession;
		std::ofstream m_OutputStream;
		int m_ProfileCount;
	public:
		Instrumentor()
			: m_CurrentSession(nullptr), m_ProfileCount(0)
		{
		}

		void BeginSession(const std::string& name, const std::string& filepath = "results.json")
		{
			m_OutputStream.open(filepath);
			WriteHeader();
			m_CurrentSession = new InstrumentationSession{ name };
		}

		void EndSession()
		{
			WriteFooter();
			m_OutputStream.close();
			delete m_CurrentSession;
			m_CurrentSession = nullptr;
			m_ProfileCount = 0;
		}

		void WriteProfile(const ProfileResult& result)
		{
			if (m_ProfileCount++ > 0)
				m_OutputStream << ",";

			std::string name = result.Name;
			std::replace(name.begin(), name.end(), '"', '\'');

			m_OutputStream << "{";
			m_OutputStream << "\"cat\":\"function\",";
			m_OutputStream << "\"dur\":" << (result.End - result.Start) << ',';
			m_OutputStream << "\"name\":\"" << name << "\",";
			m_OutputStream << "\"ph\":\"X\",";
			m_OutputStream << "\"pid\":0,";
			m_OutputStream << "\"tid\":" << result.ThreadID << ",";
			m_OutputStream << "\"ts\":" << result.Start;
			m_OutputStream << "}";

			m_OutputStream.flush();
		}

		void WriteHeader()
		{
			m_OutputStream << "{\"otherData\": {},\"traceEvents\":[";
			m_OutputStream.flush();
		}

		void WriteFooter()
		{
			m_OutputStream << "]}";
			m_OutputStream.flush();
		}

		static Instrumentor& Get()
		{
			static Instrumentor instance;
			return instance;
		}
	};

	class InstrumentationTimer
	{
	public:
		InstrumentationTimer(const char* name)
			: m_Name(name), m_Stopped(false)
		{
			m_StartTimepoint = std::chrono::high_resolution_clock::now();
		}

		~InstrumentationTimer()
		{
			if (!m_Stopped)
				Stop();
		}

		void Stop()
		{
			auto endTimepoint = std::chrono::high_resolution_clock::now();

			long long start = std::chrono::time_point_cast<std::chrono::microseconds>(m_StartTimepoint).time_since_epoch().count();
			long long end = std::chrono::time_point_cast<std::chrono::microseconds>(endTimepoint).time_since_epoch().count();

			uint32_t threadID = std::hash<std::thread::id>{}(std::this_thread::get_id());
			Instrumentor::Get().WriteProfile({ m_Name, start, end, threadID });

			m_Stopped = true;
		}
	private:
		const char* m_Name;
		std::chrono::time_point<std::chrono::high_resolution_clock> m_StartTimepoint;
		bool m_Stopped;
	};
}

#define HZ_PROFILE 1
#if HZ_PROFILE
	#define HZ_PROFILE_BEGIN_SESSION(name, filepath) ::Hazel::Instrumentor::Get().BeginSession(name, filepath)
	#define HZ_PROFILE_END_SESSION() ::Hazel::Instrumentor::Get().EndSession()
	#define HZ_PROFILE_SCOPE(name) ::Hazel::InstrumentationTimer timer##__LINE__(name);
	#define HZ_PROFILE_FUNCTION() HZ_PROFILE_SCOPE(__FUNCSIG__)
#else
	#define HZ_PROFILE_BEGIN_SESSION(name, filepath)
	#define HZ_PROFILE_END_SESSION()
	#define HZ_PROFILE_SCOPE(name)
	#define HZ_PROFILE_FUNCTION()
#endif

之后在代码块添加宏之后就会写入到 json 文件中,然后可以在 Chrome//:tracing 进行显示:

在这里插入图片描述
在这里插入图片描述

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

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

相关文章

笔记本电脑没有声音如何解决

​笔记本电脑没有声音的现象&#xff0c;也是笔记本电脑的常见运用病况之一,遇到这种情况的话,大家是否知道如何处理呢?下面小编来跟大家说说笔记本电脑没有声音解决方法&#xff0c;希望可以帮助到大家。 工具/原料&#xff1a; 系统版本&#xff1a;windows10系统 品牌型…

python--谷歌恐龙快跑小项目

用300行代码左右实现谷歌休闲的恐龙快跑游戏&#xff01; 主函数&#xff1a; import sys import math import time import random import pygame from pygame.locals import * from Scene import Scene from Obstacle import Plant, Ptera from Dinosaur import Dinosaur #…

嵌入式开发:当用微控制器构建嵌入式GUI时,有哪些注意事项

在嵌入式开发中&#xff0c;借助基于MCU的设计&#xff0c;你可以消除额外的RAM和闪存芯片&#xff0c;并使用板载外设而不是板外逻辑&#xff0c;所有这些都将随着当今功能强大的芯片而变得更加简单。当然&#xff0c;与成熟的微处理器相比&#xff0c;MCU本身也提供了额外的成…

【校内篇】如何安装一台虚拟机

咱们的微机老师要求上微机课用的电脑必须要用 Windows7Windows\ 7Windows 7&#xff0c;但是很多同学的电脑也许并不匹配&#xff0c;造成了诸多不便。 作为班长&#xff0c;我觉得有必要把自己的一些技术共享给大家&#xff0c;方便大家使用。 文章目录一、准备材料&#x1f6…

如何扩大电脑c盘分区,c盘空间不足怎么扩容

当电脑使用一段时间后&#xff0c;C盘会存储一定的数据&#xff0c;包括操作系统以及其他的文件。在实际的运用中&#xff0c;许多应用程序的默认下载路径就是C盘&#xff0c;如果用户没有更改为其他磁盘&#xff0c;会导致C盘的空间越来越小&#xff0c;电脑越来越卡顿。从根源…

【C版本】静态通讯录与动态通讯录的实现,以及各自所存在的缺陷对比。(含所有原码)

目录静态版本通讯录前期思路具体实现1、框架2、初始化通讯录3、增加联系人4、显示已有联系人5、查找联系人6、删除指定联系人7、排序联系人8、修改联系人信息9、清空联系人静态版本通讯录存在的缺陷动态版本通讯录&#xff08;静态版本的部分功能发生改动&#xff09;初始化增加…

优雅的使用Webstack打造个人网址导航

原文链接&#xff1a;优雅的使用Webstack打造个人网址导航 前言 一款基于 WebStackPage 的 Hexo 主题。本人选择的是 hexo-theme-webstack。 效果预览 具体效果请移步 个人网址导航。 步骤 在目标路径&#xff08;我这里选的路径为【D:/studytype/My_Blog】&#xff09;打开…

基于C#制作一个桌面宠物

此文主要基于C#制作一个桌面宠物&#xff0c;可自定义宠物素材图片及打开外部exe的快捷菜单。 实现流程1.1、创建项目1.2、准备素材1.3、控件设置&#xff08;1&#xff09;PictureBox控件&#xff08;2&#xff09;timer控件&#xff08;3&#xff09;contextMenuStrip控件1.4…

学习MySQL必须掌握的13个关键字,你get了吗?

1、三范式 第一范式&#xff1a;每个表的每一列都要保持它的原子性&#xff0c;也就是表的每一列是不可分割的&#xff1b;第二范式&#xff1a;在满足第一范式的基础上&#xff0c;每个表都要保持唯一性&#xff0c;也就是表的非主键字段完全依赖于主键字段&#xff1b;第三范…

【微服务】Nacos2.x服务发现?RPC调用?重试机制?

&#x1f496;Spring家族及微服务系列文章 ✨【微服务】Nacos通知客户端服务变更以及重试机制 【微服务】SpringBoot监听器机制以及在Nacos中的应用 ✨【微服务】Nacos服务端完成微服务注册以及健康检查流程 ✨【微服务】Nacos客户端微服务注册原理流程 ✨【微服务】SpringClou…

Vue 和 React 比,React 好在哪里?

​ 这两个设计理念上就有所区别&#xff0c;类比过来就是&#xff1a;Vue 是自动挡汽车&#xff0c;React 是手动挡汽车。 在 Vue 中&#xff0c;不需要去注意视图和数据的一致性&#xff0c;因为有双向绑定看帮你处理&#xff0c;响应式的。还有一些很方便的 v-if、v-model 之…

软考 - 面向对象开发

⭐设计模式UML详解&#xff1a;https://blog.csdn.net/qq_40274514/article/details/124047443 面向对象基础 面向对象的程序设计 和 面向对象设计区别 面向对象的程序设计涉及到具体的编程语言 面向对象设计只从系统逻辑结构设计解决方案 常见的机制 动态绑定&#xff1a;过…

ESXi5.5远程升级到ESXi6.7 (VMware Hypervisor)

1、ESXi的介质分为两类&#xff0c;以6.7为例&#xff1a; VMware vSphere Hypervisor (ESXi ISO) image (Includes VMware Tools)&#xff08;ISO包&#xff09; VMware vSphere Hypervisor (ESXi) Offline Bundle &#xff08;ZIP包&#xff09; 如果要升级&#xff0c;需要Z…

【畅购商城】详情页详情之商品详情

1.构建详情页 步骤0&#xff1a;确定访问路径 http://localhost:3000/Goods?id1 步骤二&#xff1a;复制 ~/static/goods.html 内容&#xff0c;导入第三方资源&#xff08;css、js&#xff09; head: {title: 列表页面,link: [{rel:stylesheet,href: /style/goods.css},{re…

Sysweld笔记:利用稳态算法加速算法模拟焊接过程的残余应力

作者&#xff1a;贾亚波博士&#xff0c;仿真秀专栏作者 在进行热力耦合的仿真过程中&#xff0c;如果模型足够的长并且热源速度恒定&#xff0c;通常其热学&#xff0c;相变以及热力耦合都会达到稳态的过程&#xff0c;因此如何直接计算稳态问题成为了大家研究的热点问题。 …

【C++笔试强训】第二十四天

&#x1f387;C笔试强训 博客主页&#xff1a;一起去看日落吗分享博主的C刷题日常&#xff0c;大家一起学习博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a;夜色难免微凉&#xff0c;前方必有曙光 &#x1f31e;。 &#x1f4a6;&a…

【数据结构】栈基本操作的实现(C语言)

&#x1f680; 作者简介&#xff1a;一名在后端领域学习&#xff0c;并渴望能够学有所成的追梦人。 &#x1f40c; 个人主页&#xff1a;蜗牛牛啊 &#x1f525; 系列专栏&#xff1a;&#x1f6f9;初出茅庐C语言、&#x1f6f4;数据结构 &#x1f4d5; 学习格言&#xff1a;博…

SpringBoot项目打包时配置文件区分日常、测试、预发、正式环境

前言&#x1f34a; 在我们开发项目的时候&#xff0c;一般有四套环境&#xff1a;日常、测试、预发、正式。日常环境作为我们开发环境&#xff1b;测试环境给测试同学测试功能&#xff1b;预发环境给正式环境发布时提供准备&#xff1b;正式环境则是稳定的生产环境。 这四套环…

面试官问我new Vue阶段做了什么?

前言 本篇录入吊打面试官专栏&#xff0c;希望能祝君拿下Offer一臂之力&#xff0c;各位看官感兴趣可移步&#x1f6b6;。这段时间面了很多家公司&#xff0c;被问到的题我感觉不重复不止100道&#xff0c;将会挑选觉得常见且有意义的题目进行分析及回答。有人说面试造火箭&am…

Redis字符串、hash、列表方法使用以及Redis管道与Django集成Redis

目录标题一、Redis字符串操作二、Redis-hash操作三、Redis列表操作四、Redis管道五、Redis其他操作六、Django中集成Redis七、Celery介绍一、Redis字符串操作 名称属性setex:过期时间&#xff08;秒&#xff09;px:过期时间(毫秒) nx:如果设置为True&#xff0c;则只有name不存…