简体   繁体   中英

how does a c# profiler work?

I'm curious how does a typical C# profiler work?

Are there special hooks in the virtual machine?

Is it easy to scan the byte code for function calls and inject calls to start/stop timer?

Or is it really hard and that's why people pay for tools to do this?

(as a side note i find a bit interesting bec it's so rare - google misses the boat completely on the search " how does a c# profiler work? " doesn't work at all - the results are about air conditioners...)

There is a free CLR Profiler by Microsoft, version 4.0.

https://www.microsoft.com/downloads/en/details.aspx?FamilyID=be2d842b-fdce-4600-8d32-a3cf74fda5e1

BTW, there's a nice section in the CLR Profiler doc that describes how it works, in detail, page 103. There's source as part of distro.

Is it easy to scan the byte code for function calls and inject calls to start/stop timer?

Or is it really hard and that's why people pay for tools to do this?

Injecting calls is hard enough that tools are needed to do it.

Not only is it hard, it's a very indirect way to find bottlenecks. The reason is what a bottleneck is is one or a small number of statements in your code that are responsible for a good percentage of time being spent, time that could be reduced significantly - ie it's not truly necessary, ie it's wasteful. IF you can tell the average inclusive time of one of your routines (including IO time), and IF you can multiply it by how many times it has been called, and divide by the total time, you can tell what percent of time the routine takes. If the percent is small (like 10%) you probably have bigger problems elsewhere. If the percent is larger (like 20% to 99%) you could have a bottleneck inside the routine. So now you have to hunt inside the routine for it, looking at things it calls and how much time they take. Also you want to avoid being confused by recursion (the bugaboo of call graphs).

There are profilers (such as Zoom for Linux, Shark, & others) that work on a different principle. The principle is that there is a function call stack, and during all the time a routine is responsible for (either doing work or waiting for other routines to do work that it requested) it is on the stack . So if it is responsible for 50% of the time (say), then that's the amount of time it is on the stack, regardless of how many times it was called, or how much time it took per call. Not only is the routine on the stack, but the specific lines of code costing the time are also on the stack. You don't need to hunt for them. Another thing you don't need is precision of measurement. If you took 10,000 stack samples, the guilty lines would be measured at 50 +/- 0.5 percent. If you took 100 samples, they would be measured as 50 +/- 5 percent. If you took 10 samples, they would be measured as 50 +/- 16 percent. In every case you find them , and that is your goal. (And recursion doesn't matter. All it means is that a given line can appear more than once in a given stack sample.)

On this subject, there is lots of confusion. At any rate, the profilers that are most effective for finding bottlenecks are the ones that sample the stack, on wall-clock time, and report percent by line. (This is easy to see if certain myths about profiling are put in perspective.)

1) There's no such thing as "typical". People collect profile information by a variety of means: time sampling the PC, inspecting stack traces, capturing execution counts of methods/statements/compiled instructions, inserting probes in code to collect counts and optionally calling contexts to get profile data on a call-context basis. Each of these techniques might be implemented in different ways.

2) There's profiling "C#" and profiling "CLR". In the MS world, you could profile CLR and back-translate CLR instruction locations to C# code. I don't know if Mono uses the same CLR instruction set; if they did not, then you could not use the MS CLR profiler; you'd have to use a Mono IL profiler. Or, you could instrument C# source code to collect the profiling data, and then compile/run/collect that data on either MS, Mono, or somebody's C# compatible custom compiler, or C# running in embedded systems such as WinCE where space is precious and features like CLR-built-ins tend to get left out.

One way to instrument source code is to use source-to-source transformations, to map the code from its initial state to code that contains data-collecting code as well as the original program. This paper on instrumenting code to collect test coverage data shows how a program transformation system can be used to insert test coverage probes by inserting statements that set block-specific boolean flags when a block of code is executed. A counting-profiler substitutes counter-incrementing instructions for those probes. A timing profiler inserts clock-snapshot/delta computations for those probes. Our C# Profiler implements both counting and timing profiling for C# source code both ways; it also collect the call graph data by using more sophisticated probes that collect the execution path. Thus it can produce timing data on call graphs this way. This scheme works anywhere you can get your hands on a halfway decent resolution time value.

This is a link to a lengthy article that discusses both instrumentation and sampling methods:

http://smartbear.com/support/articles/aqtime/profiling/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM