Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/loops/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Loops 分析循环中的循环时间抖动_Loops_Profiling - Fatal编程技术网

Loops 分析循环中的循环时间抖动

Loops 分析循环中的循环时间抖动,loops,profiling,Loops,Profiling,我在制作游戏时遇到了一个小问题,虽然问题并不局限于游戏编程本身,但我将用我正在经历的具体示例来表达: 正如预期的那样,我在循环中运行游戏代码,控制游戏逻辑并在每个循环中绘制一个框架。然而,在完成每个周期所需的时间中,我经历了相当多的抖动。正常循环时间在最短测量时间和最长测量时间之间变化约两倍,有时(更罕见)出现较大的峰值,需要明显的时间 我想消除两者。我想消除正常抖动,使绘图更平滑,我想消除尖峰,因为它们是明显的和恼人的。问题是,我不知道是什么代码导致它们出现。我已经尝试过测量几个特定的代码路径

我在制作游戏时遇到了一个小问题,虽然问题并不局限于游戏编程本身,但我将用我正在经历的具体示例来表达:

正如预期的那样,我在循环中运行游戏代码,控制游戏逻辑并在每个循环中绘制一个框架。然而,在完成每个周期所需的时间中,我经历了相当多的抖动。正常循环时间在最短测量时间和最长测量时间之间变化约两倍,有时(更罕见)出现较大的峰值,需要明显的时间

我想消除两者。我想消除正常抖动,使绘图更平滑,我想消除尖峰,因为它们是明显的和恼人的。问题是,我不知道是什么代码导致它们出现。我已经尝试过测量几个特定的代码路径或收集系统资源使用情况,但到目前为止,它只起到了帮助作用

我想做的是,能够运行CPU探查器,在每个周期重置计数器数据。这样,我可以很容易地从一帧到另一帧比较花费时间的地方,以及哪些代码路径本身或多或少地抖动。但是我该怎么做呢?我在Java和C中都遇到了这个问题,Java的
-Xprof
选项和GCC的
gprof
程序打印只测量了从程序生命周期开始到结束的时间(如预期)。是否有任何方法可以在每个周期转储和重置这些分析器的数据,和/或是否有其他分析器可以提供这种功能?我还认为它们每秒钟只采集大约100个样本,当一帧只采集大约10-20毫秒时,这是非常不够的分辨率。有没有办法提高它们的采样率


当然,还有其他方法可以解决这些问题吗?

我相信还有比这更简单的方法,但我们就是这样做的: 编写自己的(非常简单的)探查器计时器(带有开始/结束宏),它也接受阈值时间,并在超过给定阈值时记录

用这个包装游戏循环中的每个顶级组件。通过这种方式,您可以获得哪些组件是瓶颈的日志

一旦识别了组件,就可以使用相同的方法深入到代码中

这需要一些手工工作(可能有一些工具可以做到这一点),但很快你就能找到问题的确切位置

例如(psuedo代码):


让我们考虑C代码,所以我们不包括GC时间。< /P> 我假设多余的时间不会发生在每一帧上,但很少发生,所以任何只测量聚合时间的分析器都不会告诉你太多

我要做的(这可能不容易)是尝试只在程序运行时告诉它正在做什么,而不是一直告诉它。 如果可能的话,在每一帧的开始,我会设置一个闹钟定时器,设置为在足够的毫秒数后停止,这样它就会在额外的时间内触发。 然后在计算帧结束时,我会禁用计时器,这样它就不会在帧结束后熄灭。 向计时器添加少量随机毫秒数可能会很有用

然后当计时器关闭时,我会收集一个堆栈样本并研究它,看看程序在做什么

可能很难收集样本并保存起来以便事后查看。 当警报响起时,只需输入一个调试器,然后研究堆栈以及程序当时的正确操作,可能会更简单、信息更丰富。 要获得另一个示例,请恢复执行或从头开始

关键是,你想知道它在做什么,为什么它会在多余的时间里这么做。 你希望它所做的多余的工作,或者其中的一部分,在多余的时间里发生

你可能需要20个样本才能看到有趣的东西, 但是,一旦不止一个示例显示发生了您没有真正意识到它在做的事情,并且您可以将其清除,这将显著减少多余的时间使用。 在你看到之前,你必须采集的样本越少,时间就越短

这样的问题可能不止一个,所以在你发现第一件事后不要停下来。
反复这样做,直到你找不到任何可以删除的内容。

我想补充Asaf的答案,建议让你的配置文件例程记录自上一帧开始以来每个配置文件单元花费的时间可能会有所帮助。我不知道现有的配置文件工具有多容易做到这一点,但如果你自己旋转

void START_PROFILE(PROFILE_REC *rec) { if (rec->frame != total_frame_count) { rec->frame = total_frame_count; rec->time_before_frame = rec->total_time; } rec->time_before_last = rec->total_time; rec->start_time = get_precise_system_time(); } void END_PROFILE(PROFILE_REC *rec) { rec->total_time += get_precise_system_time - rec->start_time; } 无效开始配置文件(配置文件记录*记录) { 如果(记录->帧!=总帧计数) { 记录->帧=总帧计数; 记录->帧前时间=记录->总时间; } 记录->上次之前的时间=记录->总时间; rec->start_time=获取精确的系统时间(); } 无效端部外形(外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形外形) { rec->total_time+=获取精确的系统时间-rec->start_时间; } 上面没有任何错误检查,以确保END_配置文件调用平衡START_配置文件调用;如果呼叫不匹配,它将记录无意义的号码,但如果没有错误报告,错误检查可能没有帮助,错误报告将增加另一层复杂性

顺便说一句,如果您有一些任务可以容忍其精确计时中存在相当大的抖动,那么在每个帧上执行必须在该帧上执行的所有操作,然后在“任何时候”执行可以执行的操作,直到下一帧到达,这可能会有所帮助。然后处理该帧,完成后,返回到处理“where”任务。根据您的喜好,您可以使用成熟的多任务操作系统,或简单的循环堆栈切换器,或一组状态机,或其他任何东西来实现这一点。我用这种方法 void START_PROFILE(PROFILE_REC *rec) { if (rec->frame != total_frame_count) { rec->frame = total_frame_count; rec->time_before_frame = rec->total_time; } rec->time_before_last = rec->total_time; rec->start_time = get_precise_system_time(); } void END_PROFILE(PROFILE_REC *rec) { rec->total_time += get_precise_system_time - rec->start_time; }