 |
|
 |
Hi ,
Very nice article.
The diffecence grows when you use small timer intervals.
But adding this code to all methods:
Random rnd = new Random();
int t = rnd.Next();
int ty = rnd.Next();
int l = t + ty;
++count;
Reflection has 45% - 50% iterations.
It seems that the method call overhead gets much smaller when a method has
a small body.
Mud
|
|
|
|
 |
|
 |
Hello everyone,
I've just come across this usefull article! I tried to run the benchmark on my computer, and I'm surprised with the poor performance of delegates... I did not expect invocation via reflection to be a champ, but delegates are - compared to direct calls - also not showing nice numbers to me, but they are 40x slower that direct calls! (I had to change counter from int to long, because it overflows on my PC).
Static Direct Call Without Params: 44573675
Static Direct Call With Params: 44422734
Instance Direct Call Without Params: 46474033
Instance Direct Call With Params: 47621956
Static Delegate Call Without Params: 1002392
Static Delegate Call With Params: 978512
Instance Delegate Call Without Params: 1689502
Instance Delegate Call With Params: 1609608
Static Reflection Invoke Call Without Params: 45125
Static Reflection Invoke Call With Params: 27282
Instance Reflection Invoke Call Without Params: 43302
Instance Reflection Invoke Call With Params: 26314
|
|
|
|
 |
|
 |
Have you run your bench with more complicated methods ?
That's just a thought but haven't JIT or even the compiler broken your function calls to inline code if it has detected small function. I remember having done so while building a pseudo-c compiler in my student years and that really made a difference !
Michael
|
|
|
|
 |
|
 |
That's a good question. I haven't explored the differences between debug and release mode versions, but from discussions I've had, it appears there are several!
Marc
Help! I'm an AI running around in someone's f*cked up universe simulator. Sensitivity and ethnic diversity means celebrating difference, not hiding from it. - Christian Graus Every line of code is a liability - Taka Muraoka
|
|
|
|
 |
|
|
 |
|
 |
I had you in mind when I wrote this benchmark, and it DOES include an instance interface. The static member instantiates itself and then uses its own instance to call into non-static members.
(If this wasn't what you had in mind, let me know!)
Marc
Help! I'm an AI running around in someone's f*cked up universe simulator. Sensitivity and ethnic diversity means celebrating difference, not hiding from it. - Christian Graus Every line of code is a liability - Taka Muraoka
|
|
|
|
 |
|
 |
Marc Clifton wrote:
If this wasn't what you had in mind, let me know!)
Yes here it is I'll mail u the code now I modded it slightly (and got rid of some unneccasry code ).
Here's the output:
Static Direct Call Without Params: 29926358
Static Direct Call With Params: 19828623
Instance Direct Call Without Params: 25430878
Instance Direct Call With Params: 21053449
Static Delegate Call Without Params: 6918542
Static Delegate Call With Params: 6697049
Instance Delegate Call Without Params: 8483002
Instance Delegate Call With Params: 8660518
Static Reflection Invoke Call Without Params: 38896
Static Reflection Invoke Call With Params: 28502
Instance Reflection Invoke Call Without Params: 35093
Instance Reflection Invoke Call With Params: 29968
Instance Reflection Interface Call Without Params: 14636213
Instance Reflection Interface Call With Params: 15152848
As you can see an interface will give you the fastest possible performance for loading "unknown" types via reflection.
Let me know what your opinion is
WebBoxes - Yet another collapsable control, but it relies on a "graphics server" for dynamic pretty rounded corners, cool arrows and unlimited font support.
|
|
|
|
 |
|
 |
Some what's the end of the story ? Should interface always be used when working with reflection ?
Jonathan de Halleux.
|
|
|
|
 |
|
 |
I like your article Marc, but I do have a small suggestion. You start off by showing us your benchmark matrix. But then you don't use it? I think it's hard to read that CMD-screenshot. Sure, all the data is in there, but it's just a bunch of numbers which have to be interpreted.
How about generating a HTML-table to match your proposed matrix? It would be piece of cake to modify the program to generate the HTML code for you.
Just a thought.
--
Only in a world this sh***y could you even try to say these were innocent people and keep a straight face.
|
|
|
|
 |
|
|
 |
|
 |
I have modified your program a little so that it generates HTML output if given the command line argument "-html".
It generates tables like this one:
<table>
<tr><th align="right"> <span style = "color: green">calls</span> / <span style = "color: red">calls/ms</span></th><th>Direct</th><th>Delegate</th><th>Reflection</th></tr>
<tr>
<th align="left">Static w/o params</th>
<td><span style="color: green">90354542</span> / <span style = "color: red">225886</span></td>
<td><span style="color: green">10689731</span> / <span style = "color: red">26724</span></td>
<td><span style="color: green">38233</span> / <span style = "color: red">95</span></td>
</tr>
<tr>
<th align="left">Static w/ params</th>
<td><span style="color: green">75360709</span> / <span style = "color: red">188401</span></td>
<td><span style="color: green">9082769</span> / <span style = "color: red">22706</span></td>
<td><span style="color: green">37657</span> / <span style = "color: red">94</span></td>
</tr>
<tr>
<th align="left">Instance w/o params</th>
<td><span style="color: green">93233127</span> / <span style = "color: red">233082</span></td>
<td><span style="color: green">17441213</span> / <span style = "color: red">43603</span></td>
<td><span style="color: green">38388</span> / <span style = "color: red">95</span></td>
</tr>
<tr>
<th align="left">Instance w/ params</th>
<td><span style="color: green">104220700</span> / <span style = "color: red">260551</span></td>
<td><span style="color: green">14247093</span> / <span style = "color: red">35617</span></td>
<td><span style="color: green">37524</span> / <span style = "color: red">93</span></td>
</tr>
</table>
Unfortunately CP won't render tables in posts, so you have to copy the text to notepad and then save it as a html file for viewing in IE. I can send the modifications to you if you like.
--
Only in a world this sh***y could you even try to say these were innocent people and keep a straight face.
|
|
|
|
 |
|
 |
I'd love the code!
Funny, I'm getting all sorts of code snippets from people with this article. It seems to have tickled a bone somewhere.
Marc
Help! I'm an AI running around in someone's f*cked up universe simulator. Sensitivity and ethnic diversity means celebrating difference, not hiding from it. - Christian Graus Every line of code is a liability - Taka Muraoka
|
|
|
|
 |
|
 |
The JIT Compiler will optimize code, when the code is run outside the debugger. In those case, direct calls are twice as fast as they were before, leaving everything else in the dust!!
Static Direct Call Without Params: 18869748
Static Direct Call With Params: 19394757
Instance Direct Call Without Params: 18179939
Instance Direct Call With Params: 26653965
Static Delegate Call Without Params: 2758885
Static Delegate Call With Params: 2459350
Instance Delegate Call Without Params: 3648431
Instance Delegate Call With Params: 3454415
Static Reflection Invoke Call Without Params: 8310
Static Reflection Invoke Call With Params: 7180
Instance Reflection Invoke Call Without Params: 7680
Instance Reflection Invoke Call With Params: 7076
Static Delegate Reflection Invoke Call Without Params (MethodInfo): 2627810
Static Delegate Reflection Invoke Call Without Params: 2567481
Static Delegate Reflection Invoke Call With Params: 2443410
Instance Delegate Reflection Invoke Call Without Params: 3463589
Instance Delegate Reflection Invoke Call With Params: 3215606
|
|
|
|
 |
|
 |
OK, I'm going to profess real ignorance here.
There's a difference between debug and release mode code, other than debug information? What does it mean when I compile the code in the IDE?
I'm confused (and ignorant)!
Marc
Help! I'm an AI running around in someone's f*cked up universe simulator. Sensitivity and ethnic diversity means celebrating difference, not hiding from it. - Christian Graus Every line of code is a liability - Taka Muraoka
|
|
|
|
 |
|
 |
MSDN says that it's optimized, so just accept it based on blind faith :
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtskdebuggingmanagedcclasslibrary.asp[^]
Ah, I see now:
(Optimized code is harder to debug, since the generated instructions do not correspond directly to your source code. If you find your program has a bug that appears only in optimized code, you can turn this setting on, but remember that code shown in the Disassembly window is generated from optimized source that may not match what you see in your source windows. Other features, such as stepping, may not behave as expected.)
So the IL that the compiler generates in Release and Debug modes must be different. I never knew that. It might be interesting to examine this more thoroughly.
I don't know whether it's just the light but I swear the database server gives me dirty looks everytime I wander past.
-Chris Maunder
Microsoft has reinvented the wheel, this time they made it round.
-Peterchen on VS.NET
|
|
|
|
 |
|
 |
Which breaks one of the commandments of programming--only optimize when necessary and in selected locations. At least, it's one of the commandments I use. I almost never turn on optimizations.
As for instructions not corresponding directly to my code, I usually don't care. Half the time I seem to be looking at assembly code anyways. If Microsoft ever takes out that view I'll switch to another compiler!
Marc
Help! I'm an AI running around in someone's f*cked up universe simulator. Sensitivity and ethnic diversity means celebrating difference, not hiding from it. - Christian Graus Every line of code is a liability - Taka Muraoka
|
|
|
|
 |
|
 |
I can't read ASM, so I just go down to IL level and look at that code. I can read that thanks to Serge Lidin's book.
I don't know whether it's just the light but I swear the database server gives me dirty looks everytime I wander past.
-Chris Maunder
Microsoft has reinvented the wheel, this time they made it round.
-Peterchen on VS.NET
|
|
|
|
 |
|
 |
That's because you program in C#, and I come from a C++ background. Actually, I learned 6502 assembly language after BASIC and programmed a ton of stuff in assembly language before foraying into Pilot, Logo, Fortran, Pascal, and then C. And interspersed in their were about 10 other types of microprocessors.
Actually, IL is a lot like assembly language that's non-processor specific. Gee, I wonder why that is!
Marc
Help! I'm an AI running around in someone's f*cked up universe simulator. Sensitivity and ethnic diversity means celebrating difference, not hiding from it. - Christian Graus Every line of code is a liability - Taka Muraoka
|
|
|
|
 |
|
 |
The only bummer with IL is that there isn't any built in editor that I've been able to find for it. So I can't partake in any of the IntelliSense features that could have been nice. Besides, if I can have a multilanguage solution, why can't IL be one of the languages?
I don't know whether it's just the light but I swear the database server gives me dirty looks everytime I wander past.
-Chris Maunder
Microsoft has reinvented the wheel, this time they made it round.
-Peterchen on VS.NET
|
|
|
|
 |
|
 |
Not turning on optimisations seems a little overly religious! As I hear it, there are all sorts of optimisations that you might not think about but are usually done.
e.g. When returning an object by value from a function, and assigning the result to a variable. In theory, I believe the copy constructor would be executed twice (local stack variable -> return value, then return value to local variable). Most (if not all) compilers will optimise this sort of thing out when optimisations are turned on.
.NET introduces all sorts of funny extra bits into this equation (e.g. array bounds checking). I noticed on the MSDN show that the C# compiler does all sorts of funky optimisations. e.g.
int length = array.Count();
for (i=0; i < length; i++)
doSomething(array[i]);
is (counter-intuitively) slower than
for (i=0; i < array.Count(); i++)
doSomething(array[i]);
because in the first case the compiler spots what you are doing and will optimise out the bounds checks on all the array[i] operations.
I don't know how much of this happens at the c#->IL stage, and how much at the JIT stage, but I'm willing to bet that a lot happens in the former, because that stage has more information about the code paths involved. I would imagine that the JIT stage is probably fairly lightweight.
|
|
|
|
 |
|
 |
Not turning on optimisations seems a little overly religious!
Considering how many times I've been burned by optimizations, I would say no.
Marc
Help! I'm an AI running around in someone's f*cked up universe simulator. Sensitivity and ethnic diversity means celebrating difference, not hiding from it. - Christian Graus Every line of code is a liability - Taka Muraoka
|
|
|
|
 |
|
 |
An alternative to what you are trying to do is to support code compilation. There have been many articles in this website showing how to do C# Eval!!! Then, you get the benefits of direct calls.
Thanks,
Wes
|
|
|
|
 |
|
 |
What's the cost of executing "eval" then? What do you get back from eval? A thunk or...what?
--
Only in a world this sh***y could you even try to say these were innocent people and keep a straight face.
|
|
|
|
 |
|
 |
It's always good to put our ideas to simple tests to determine how practical our implementations, code or style is. Doing so early, as opposed to late, in the development process can make things much easier as time goes on.
Abstractions always have a cost, don't they. Either you loose performance (reflection), flexibility (MFC) or product lifetime (pick any GUI toolkit that has ever existed) or something else.
HOWEVER, abstractions have tremendous benefit. Your AAL framework (as it is right now) as a framework for certain types of applications is not bad at all. I can definitely see using it in areas where the performance cost is insignifigant (GUI back-end plugins) where # of messages is low or asynchronous feedback is desirable and planned for.
One word of caution about performance testing I learned recently (which I'm sure you already know). If the method call being used as the test does not come close to representing real-world scenario it really isn't that valid of a test.
Case-in-point: I recently published an article on simple performance testing primarily intended to show the relative cost of using simple input-parameter validation techniques (http://www.codeproject.com/useritems/StandaloneProgrammer4.asp[^]). If you run the simple demo and use zero as the function time and then again with a function time of say 2 or 3 milliseconds, you will notice that the results tend to even-out especially for the IsBadReadPtr and IsBadWritePtr related tests.
After some research into why this happens, I believe I understand now that the reason has to do with how Windows timeslices thread time. Basically, if a function can execute within a single timeslice there is virutaly no overhead to calling it, but once it exceeds it's timeslice all bets are off.
Just my 2 cents.
|
|
|
|
 |
|
 |
Hi Matt,
Abstractions always have a cost, don't they.
Definitely. What I'm is a different implementation of the primary abstraction--using delegates instead of reflection. And this is where prototyping and benchmarking is invaluable and can often affect the design.
If the method call being used as the test does not come close to representing real-world scenario it really isn't that valid of a test.
Excellent point! I remember when I used Borland's profiler for some DOS code and consistently, where I thought the problems were, I was wrong. The same thing is true for performance testing, as your example illustrated.
However, in my usage of the AAL in C++/MFC, I notice that I'm almost always stringing process calls together into workflows at the script level and often doing iteration things. So, performance of the indirect call is fairly high in the overall ranking of performance issues (I think!)
BTW, I noticed your article just yesterday when I was getting the link to the "quality" article. It must have flown onto and off of the "last 10 articles" screen! I glanced at it briefly but I'll read it more thoroughly today.
If you run the simple demo and use zero as the function time and then again with a function time of say 2 or 3 milliseconds, you will notice that the results tend to even-out especially for the IsBadReadPtr and IsBadWritePtr related tests.
Isn't this simply because the test takes microseconds while the function takes milliseconds, and therefore the percent of time spent on the test is negligible with regards to the entire function? I've run into this problem myself.
Basically, if a function can execute within a single timeslice
Hmmm. I thought with a pre-emptive multitasker it can jump to another process no matter where it is in the code. Of course, this depends on what kind of code you're executing. If it's a message invoked function, then yes, after your function exits and there are no more messages to process, the framework (whether MFC or .NET, I believe) releases the remaining timeslice.
Makes benchmarking Windows apps a real pain, doesn't it!
BTW, another thing you can play with in C++ (and probably in C#) is setting the thread priority. I've had very interesting and unexpected (as in, not good) results when I do that, but it can help take out the timeslice issue from the equation.
Marc
Help! I'm an AI running around in someone's f*cked up universe simulator. Sensitivity and ethnic diversity means celebrating difference, not hiding from it. - Christian Graus Every line of code is a liability - Taka Muraoka
|
|
|
|
 |