八、游戏内置性能剖析
要确保游戏高效运行,需要对代码进行优化。根据二八定律,只需要找出影响性能的关键代码进行优化即可。
要知道哪里需要进行优化,就需要用剖析器剖析游戏的性能。很多第三方剖析工具有很多限制,所以很多游戏引擎都有某种形式的内置剖析器。
剖析其让程序员标记一些代码段落并给每个段落命名。剖析器会使用CPU的高分辨率时钟去度量每段代码所花的时间,并记录在内存。
1.层阶式剖析:
大部分计算机程序是层阶式的,即可以实现函数的层级调用。调用堆栈(call stack)在函数层级调用中可表示从当前执行的函数到达根函数的路径。
很多商用的剖析软件能自动地在被剖析程序的每个函数中加入测控。此功能可以同时度量在剖析期间每个函数调用的包含(inclusive)和排除(exclusive)时间。此外有些剖析工具还能记录每个函数的调用次数。
2.一个简单的游戏内置剖析工具实现:
可以在游戏引擎里实现一个简单的剖析工具:
while(true)
{
{
PROFIE("PollJoypad");
PollJoypad();
}
{
PROFILE("Update");
Update();
}
{
PROFILE("RenderScene");
RenderScene();
}
}
PROFILE()宏会以一个类去实现,该类的构造函数负责开始计时,而析构函数则停止计时,并以指定的名字记录执行时间。
这个类只会为其块作用域(block scope)内的代码计时。这是由于C++特性:对象进出作用域时会自动构造和析构。
3.高级剖析工具:
处理层级调用:
上面的简单方法不适合层阶式的函数调用分析。解决办法是在采样时描述层级关系,可以在采样代码之外的地方(例如在引擎初始化时)预先声明所有样本箱,样本箱之间也要定义父子级关系。
这种方法也有问题,即默认每个函数都只有一个父函数,对于一个函数被多个父函数调用不适用。
调用计数:
记录函数在每一帧的调用次数非常简单-剖析器在每次收到时间样本时,使样本箱子里的计数加一即可。每帧开始时把计数器重置。
九、游戏内置内存分析器
PC游戏对内存方面的限制较少,因为有强大的虚拟内存系统。
追踪内存使用的难点:
- 你不能控制他人代码的分配行为。(引擎通常会链接一些第三方库,优良的库会提供内存分配钩子,而有些库不提供。)
- 内存有不同形式。(内存有主内存和显存,显存几乎不可能跟踪,因为图形API会隐藏显存分配细节。)
- 分配器有不同形式。(如果只跟踪new/delete的调用,那么各种分配器除了调用了一次new之外就没有其他信息了。我们需要分别跟踪每个分配器内的分配情况。)
编写优良内存分析工具的要点:
- 提供准确信息。
- 把数据以直观的方式呈现。
- 提供上下文信息以追踪问题根源。
(END)