分析代码的快速而肮脏的方法
当您想要获取有关特定代码路径的性能数据时,您会使用什么方法?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
当您想要获取有关特定代码路径的性能数据时,您会使用什么方法?
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(8)
在 C++11 中, chrono 标头提供了许多有用的计时函数。 因此,您可以测量代码的特定部分并将其转换为适当的测量值,例如秒或毫秒。
输出:
With C++11 the chrono header provides many useful timing functions. So you can measure a specific section of code and cast it to the appropriate measurement e.g. seconds or milliseconds.
Output:
我有一个快速而肮脏的分析类,即使在最紧密的内部循环中也可以使用它进行分析。 重点是极其轻量级和简单的代码。 该类分配一个固定大小的二维数组。 然后我在各处添加“检查点”调用。 当检查点 M 之后立即到达检查点 N 时,我将经过的时间(以微秒为单位)添加到数组项 [M,N] 中。 由于这是为了分析紧密循环而设计的,因此我还有“迭代开始”调用来重置“最后一个检查点”变量。 测试结束时,
dumpResults()
调用会生成所有相互跟随的检查点对的列表,以及已计算和未计算的总时间。I have a quick-and-dirty profiling class that can be used in profiling in even the most tight inner loops. The emphasis is on extreme light weight and simple code. The class allocates a two-dimensional array of fixed size. I then add "checkpoint" calls all over the place. When checkpoint N is reached immediately after checkpoint M, I add the time elapsed (in microseconds) to the array item [M,N]. Since this is designed to profile tight loops, I also have "start of iteration" call that resets the the "last checkpoint" variable. At the end of test, the
dumpResults()
call produces the list of all pairs of checkpoints that followed each other, together with total time accounted for and unaccounted for.为此,我编写了一个简单的跨平台类,名为 nanotimer 。 目标是尽可能轻量级,以免通过添加太多指令从而影响指令缓存来干扰实际代码性能。 它能够在 Windows、Mac 和 Linux(可能还有一些 UNIX 变体)上获得微秒级的精度。
基本用法:
start() 还会在必要时重新启动计时器。 “暂停”计时器可以通过存储经过的时间来实现,然后在“取消暂停”时重新启动计时器,并在下次检查经过的时间时添加到存储的结果中。
I wrote a simple cross-platform class called nanotimer for this reason. The goal was to be as lightweight as possible so as to not interfere with actual code performance by adding too many instructions and thereby influencing the instruction cache. It is capable of getting microsecond accuracy across windows, mac and linux (and probably some unix variants).
Basic usage:
start() also restarts the timer when necessary. "Pausing" the timer can be achieved by storing the elapsed time, then restarting the timer when "unpausing" and adding to the stored result the next time you check elapsed time.
好吧,我有两个代码片段。 在 pseudocode 中,它们看起来像(这是一个简化版本,我正在使用 QueryPerformanceFrequency):
第一个片段:
第二个片段:
一点热键功夫,我可以说这段代码从我的 CPU 中偷走了多少时间。
Well, I have two code snippets. In pseudocode they are looking like (it's a simplified version, I'm using QueryPerformanceFrequency actually):
First snippet:
Second snippet:
A bit of hot-keys kung fu, and I can say how much time this piece of code stole from my CPU.
文章代码分析器和优化提供了大量有关 C++ 代码分析的信息,并且还提供了程序/类的免费下载链接,该链接将向您展示不同代码路径/方法的图形演示。
The article Code profiler and optimizations has lots of information about C++ code profiling and also has a free download link to a program/class that will show you a graphic presentation for different code paths/methods.
注意,以下内容都是专门针对Windows编写的。
我还有一个计时器类,我编写它来进行快速而肮脏的分析,它使用 QueryPerformanceCounter() 来获得高精度计时,但略有不同。 当 Timer 对象超出范围时,我的计时器类不会转储经过的时间。 相反,它将经过的时间累积到一个集合中。 我添加了一个静态成员函数 Dump(),它创建一个运行时间表,按计时类别(在 Timer 的构造函数中指定为字符串)排序,并进行一些统计分析,例如平均运行时间、标准偏差、最大值和最小值。 我还添加了一个 Clear() 静态成员函数,用于清除集合和集合。 让你重新开始。
如何使用 Timer 类(psudocode):
示例输出:
文件 Timer.inl:
文件 stringext.h(提供 formatstr( )函数):
文件algorithmext.h(提供transform_if()函数):
Note, the following is all written specifically for Windows.
I also have a timer class that I wrote to do quick-and-dirty profiling that uses QueryPerformanceCounter() to get high-precision timings, but with a slight difference. My timer class doesn't dump the elapsed time when the Timer object falls out of scope. Instead, it accumulates the elapsed times in to an collection. I added a static member function, Dump(), which creates a table of elapsed times, sorted by timing category (specified in Timer's constructor as a string) along with some statistical analysis such as mean elapsed time, standard deviation, max and min. I also added a Clear() static member function which clears the collection & lets you start over again.
How to use the Timer class (psudocode):
Sample output :
file Timer.inl :
file stringext.h (provides formatstr() function):
File algorithmext.h (provides transform_if() function) :
我通过创建两个类来创建配置文件:
cProfile
和cProfileManager
。cProfileManager
将保存cProfile
生成的所有数据。cProfile
具有以下要求:cProfile
有一个初始化当前时间的构造函数。cProfile
有一个解构函数,它将类的总生存时间发送给cProfileManager
要使用这些配置文件类,我首先创建一个
cProfileManager
实例。 然后,我将要分析的代码块放在大括号内。 在大括号内,我创建了一个cProfile
实例。 当代码块结束时,cProfile
会将代码块完成所需的时间发送给cProfileManager
。示例代码
下面是代码示例(简化):
要使用
cProfile
,我会执行以下操作:或者这样:
技术说明
此代码实际上滥用了范围界定方式、构造函数和解构函数在 C++ 中工作。
cProfile
仅存在于块作用域(我们要测试的代码块)内。 一旦程序离开块作用域,cProfile
就会记录结果。其他增强
您可以向构造函数添加一个字符串参数,这样您就可以执行如下操作:
cProfile Profile("复杂计算的配置文件");
您可以使用宏使代码看起来更干净(注意不要滥用这一点。与我们对该语言的其他滥用不同,宏在使用时可能会很危险)。
示例:
#define START_PROFILE cProfile Profile(); {
#define END_PROFILE }
cProfileManager
可以检查一段代码被调用了多少次。 但是您需要代码块的标识符。 第一个增强功能可以帮助识别块。 当您想要分析的代码位于循环内时(如上面的第二个示例),这可能很有用。 您还可以添加代码块所花费的平均、最快和最长执行时间。如果您处于调试模式,请不要忘记添加一个检查以跳过分析。
如果
I do my profiles by creating two classes:
cProfile
andcProfileManager
.cProfileManager
will hold all the data that resulted fromcProfile
.cProfile
with have the following requirements:cProfile
has a constructor which initializes the current time.cProfile
has a deconstructor which sends the total time the class was alive tocProfileManager
To use these profile classes, I first make an instance of
cProfileManager
. Then, I put the code block, which I want to profile, inside curly braces. Inside the curly braces, I create acProfile
instance. When the code block ends,cProfile
will send the time it took for the block of code to finish tocProfileManager
.Example Code
Here's an example of the code (simplified):
To use
cProfile
, I would do something like this:or this:
Technical Note
This code is actually an abuse of the way scoping, constructors and deconstructors work in C++.
cProfile
exists only inside the block scope (the code block we want to test). Once the program leaves the block scope,cProfile
records the result.Additional Enhancements
You can add a string parameter to the constructor so you can do something like this:
cProfile Profile("Profile for complicated calculation");
You can use a macro to make the code look cleaner (be careful not to abuse this. Unlike our other abuses on the language, macros can be dangerous when used).
Example:
#define START_PROFILE cProfile Profile(); {
#define END_PROFILE }
cProfileManager
can check how many times a block of code is called. But you would need an identifier for the block of code. The first enhancement can help identify the block. This can be useful in cases where the code you want to profile is inside a loop (like the second example aboe). You can also add the average, fastest and longest execution time the code block took.Don't forget to add a check to skip profiling if you are in debug mode.
这种方法有一些局限性,但我仍然发现它非常有用。 我将预先列出(我知道的)限制,并让任何想要使用它的人自行承担风险。
当手头的问题无法证明对所有代码进行分析或我从分析器获取一些我想要验证的数据时,我会使用此类。 基本上它总结了您在特定块中花费的时间,并在程序结束时将其输出到调试流(可通过 DbgView),包括代码执行的次数(当然还有平均花费的时间))。
This method has several limitations, but I still find it very useful. I'll list the limitations (I know of) up front and let whoever wants to use it do so at their own risk.
I use this class when the problem at hand doesn't justify profiling all my code or I get some data from a profiler that I want to verify. Basically it sums up the time you spent in a specific block and at the end of the program outputs it to the debug stream (viewable with DbgView), including how many times the code was executed (and the average time spent of course)).