1

I'm looking to show the assembler code generated from a piece of code that do the exact same thing. For example:

int a = 100;
int b = 50;
int c = a + b;

I'm doing a small introduction course for my team at work and people have started questions about the performance etc and would like to be able to show them this example.

StefanE
  • 7,578
  • 10
  • 48
  • 75
  • Possible answers: http://stackoverflow.com/questions/1391438/java-byte-code-visualizer http://stackoverflow.com/questions/2425973/open-source-alternatives-to-reflector – Bobby Feb 22 '11 at 10:42
  • 2
    Looking at application compared performance by looking at micro-benchmark is like looking at car performance by examining their valve surface : very complicated to do, and of few interest in general context. – Riduidel Feb 22 '11 at 10:44
  • @Riduidel: Is it as you say (i e an absolute truth) or are you just voicing your opinion (i e that YOU think it is like this or that it is your experience that it is like this)? Do you know for a fact that valve surfaces can have no significant impact on car performance? Are you talking of the surface in the valve guide, the one exposed to the combustion or the one fitting against the seating? – Olof Forshell Feb 22 '11 at 17:28
  • @Olof_Forshell Considering that all my knowledge of valve surface comes from the excellent Monster Garage show, I will say it's just my opinion, however backed by the fact that, to increase horsepower of a mud-racing corvette, the easiest way definitely seems to be nitrogen. – Riduidel Feb 23 '11 at 08:42

3 Answers3

9

Well, neither the Java compiler nor the (normal) C# compiler will generate "native" assembly code from the source code. They'll generate Java bytecode or IL code respectively. Then depending on the VM in use, that code may end up being interpreted or JIT compiled - and possibly JIT compiled more than once (e.g. on HotSpot).

For example, here's the Java bytecode for your source code, as compiled with javac on my workstation (it's unlikely to differ very much for such a simple sample, but obviously for more complicated code there are options for what bytecode/IL to generate):

// Java bytecode
0:  bipush  100
2:  istore_1
3:  bipush  50
5:  istore_2
6:  iload_1
7:  iload_2
8:  iadd
9:  istore_3

and here's the IL from the C# compiler:

// IL
IL_0001:  ldc.i4.s   100
IL_0003:  stloc.0
IL_0004:  ldc.i4.s   50
IL_0006:  stloc.1
IL_0007:  ldloc.0
IL_0008:  ldloc.1
IL_0009:  add
IL_000a:  stloc.2

If you want to find out what native machine code is being run for that code at any one point in time, that will usually require some sort of debugger, e.g. cordbg for .NET. Even then you'll need to make sure you turn on the appropriate JIT compiler optimizations, as often when debugging you don't use optimizations because it makes the act of debugging harder. Then bear in mind that with something like HotSpot, you may well not be running the same code next time that method is hit...

Performance shouldn't generally be considered at the assembly code level, because that ignores too much context - what's in the cache, how often this code is being run, how often it takes one branch or another. Usually what matters is how the overall application behaves (or perhaps some subsystem or other) when used in real life.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thanks for the answer, What I want to prove to people is really that regardless of language it will in the end more or less execute the same thing. I do understand of course that JVM and CLR does have much differences and overhead. – StefanE Feb 22 '11 at 10:48
  • 1
    "Performance shouldn't generally be considered at the assembly code level..." True, but if you want to convince old-school C programmers, it can be very useful to show them the generated assembly code and say "see, that's what the CPU executes, there's no extra cost involved for this operation". People have strange beliefs about performance of languages they don't fully understand. – Niki Feb 22 '11 at 10:48
  • @StefanE: Except that for higher level constructs in can execute radically different code - particularly when you're talking about virtual methods and whether inlining is feasible etc. There can be a lot of nuances around this. – Jon Skeet Feb 22 '11 at 10:49
  • 1
    @nikie: I think it's better to fix the strange beliefs than to play along to them by showing them assembly. Bear in mind that CPUs are so complicated these days that trying to predict performance based on what assembly code is running is a fool's game too - humans are rubbish at taking pipelining, caches etc into account. – Jon Skeet Feb 22 '11 at 10:50
  • @StefanE: the execution environments for Java and C# should have similar performance. Of course where one is faster the other might be slower and vice versa. They will be (at least) an order of magnitude slower than compiled languages such as C and C++. An exception will be when they access functionality which has been written in a compiled language (as opposed to functionality written in Java/C#) where there will be only small differences. The interpreted part will still be slower but the compiled part will be as fast as if it were accessed from a compiled language. – Olof Forshell Feb 22 '11 at 17:03
  • @Olof: No, they *won't* be "at least an order of magnitude slower than compiled languages such as C and C++". In *some* cases they still are, but often they'll be about as fast... because they're JIT compiled into native code too, and JITs are pretty good now. Back when Java was *actually* interpreted for the whole time, it was obviously much slower than C/C++. These days, it's obviously task-dependent but it's not unusual for the margin to be ~10%. In some cases the JIT can even manage *better* than a general static compiler, as it has more machine-specific information at compilation time. – Jon Skeet Feb 22 '11 at 17:20
  • @Jon Skeet: I assume you're not including the JIT translate-to-native-code execution time in the total execution time? Aggressive levels of optimization, for example, take longer on C or c++ compilers than the more basic ones. – Olof Forshell Feb 22 '11 at 17:39
  • @Olof: That's why JITs like HotSpot perform optimization gradually more aggressively as time goes on and it spots where it's most likely to be useful... but if you've got a server with uptime in weeks or months, the JITting time becomes pretty insignificant. Whether you include JIT time or not, your claim that Java/C# will be "(at least) an order of magnitude slower than [...] C and C++" simply doesn't hold up for many uses in long-running processes. – Jon Skeet Feb 22 '11 at 17:47
  • @Olof: for CPU-bound tasks, with integer computations and little memory allocation, performance of good Java code is roughly 1/3rd of what aggressively optimized C code can produce. I get such a ratio for the implementation of cryptographic functions, in particular hash functions (see http://www.saphir2.com/sphlib/ ). The performance-killer for Java is array accesses (each array access is checked with regards to array bounds; sometimes the JIT can optimize some checks away; often it cannot). 3x is not "an order of magnitude". And in a real application, I/O costs will usually dominate. – Thomas Pornin Feb 22 '11 at 21:47
3

Regarding C#: If you run in releasemode and no debugger attached, that entire codeblock will be eliminated since it is not used.

If the "c" variable is used, the value 150 will be compiled into that code, so no addition operation going on.

   13:             Console.WriteLine(c);
00000003 E8 58 70 55 63       call        63557060 
00000008 8B C8                mov         ecx,eax 
0000000a BA 96 00 00 00       mov         edx,96h             <- 150 
0000000f 8B 01                mov         eax,dword ptr [ecx] 
00000011 8B 40 38             mov         eax,dword ptr [eax+38h] 
00000014 FF 50 14             call        dword ptr [eax+14h] 

To be able to see the optimized native code do this:

  • Add a Console.ReadLine() at the end of your code.
  • Compile as Release
  • Start without a debugger
  • Once the app waits for readline, debug -> attach to process
  • Hit break
  • Navigate the stacktrace to the function you want to view.

If you start with the debugger, the output will not be the same. it will be less optimized..

Roger Johansson
  • 22,764
  • 18
  • 97
  • 193
  • Is it the compilation step that produces native code or the fact that you start the program (without or without a debugger)? – Olof Forshell Feb 22 '11 at 17:17
  • It is the fact that it is started w/o debugger, it is the JIT compiler (just in time compiler) that will produce this when it translates IL code into machine code. – Roger Johansson Feb 23 '11 at 07:08
1

Java and C# (normally) don't compile down to machine code, they compile down to byte code. In the case of C# this byte code is executed by the CLR, in the case of Java it's the JVM. If you want to see the java bytecode, you can use javap.

You can't even guarantee it's this bytecode that'll be executed though - Java's Hotspot compiler is constantly dynamically pulling bits of bytecode around at runtime.

As a general rule, C# and Java are at the level where performance is more than good enough for the vast majority of applications. Micro-optimisations just aren't worth worrying about anymore, and picking a language based on which one does more micro-optimisations is a bit of a non starter (especially between Java and C#.)

Michael Berry
  • 70,193
  • 21
  • 157
  • 216