12

If C#, Java, or C++, for example, all compile to machine code, why are they not equally performant?

My understanding is that such languages are an abstraction of machine code, which is what they all eventually compile to. Shouldn't the processor determine performance?

Mohamad
  • 32,727
  • 31
  • 131
  • 208
  • 3
    It's not so much the language's fault, but the implementation's (though some language features can make optimization difficult) JavaScript can be fast, C can be slow.. – harold Oct 03 '11 at 17:51
  • 1
    @harold: Language design of course largely affects performance. E.g., you can't optimize away bounds checking if the language requires it and you have external input; equally, C has the restrict-modifier for pointers, there is no such notion in standard C++ or Java. Then, how many stuff gets pulled in that you didn't really ask for in Java and C#? Let us not talk about the definition of Java generics. The lack of dedicated sorting functions in C#, where C++ has stuff like nth_element, partition, partial_sort and the like. The design of a language significantly affects performance. – Sebastian Mach Oct 04 '11 at 21:44
  • @phresnel based on actual benchmarks, I respectfully disagree. Sure, it makes a difference, but if you look here: http://shootout.alioth.debian.org/u32/which-programming-languages-are-fastest.php it turns out that most of them are pretty much in the same league, until one gets to the (semi)interpreted implementations. – harold Oct 05 '11 at 07:50
  • @phresnel it's a shame they don't test HDL compilers, the differences are huge there, even staying within the same language. Different implementations there can easily be two orders of magnitude apart in performance - and they can also be completely reversed in an other benchmark. – harold Oct 05 '11 at 07:53
  • @harold: If I understand them right, then, compared to C, JavaScript programs have a 4.22 higher median runtime, Java has a 1.5 higher runtime. The only competitor that stays within a 10% window is C++. But maybe I misinterpreted the table; feel free to teach me better. – Sebastian Mach Oct 07 '11 at 09:35
  • @phresnel well that table isn't comparing the performance of languages, it's comparing the performance of specific implementations of them. CINT is also a C implementation, but it's the worst in the table. A comparison of different C compilers here http://www.willus.com/ccomp_benchmark.shtml?p10 shows that the difference can be worse than a factor of 4.5 even without including interpreters – harold Oct 07 '11 at 09:58
  • @harold: I see, your statement was about the possibility of C being slow; I misinterpreted your mentioning of that test. Then we still miss a comparison about the potential performance of a language's design, which I think we both agree, is near to impossible. But still my conjecture is that potential performance is largely driven by the language's design. Some tests that come nearer: http://jmvanel.free.fr/perf/java-cpp.html (see g++ vs. gcj) [...] – Sebastian Mach Oct 07 '11 at 10:37
  • @harold: [...] And, as said, I think it is not too unobvious that if a language requires array bounds checking, then it is a required property that is not free. Such limitations can not be circumvented for non-trivial input. – Sebastian Mach Oct 07 '11 at 10:37
  • @phresnel - java-cpp.html really isn't a good example, here's why http://stackoverflow.com/questions/7638283/if-they-eventually-become-machine-code-why-arent-compiled-languages-equally-pe/7785864#7785864 – igouy Oct 16 '11 at 17:07

5 Answers5

8

For one thing, C++ optimizers are much more mature. Another, performance has always been the overarching goal of the C++ language designers ("you don't pay for what you don't use" is the mantra, which clearly can't be said about Java's every-method-is-virtual policy).

Beyond that, C++ templates are far more optimization-friendly than Java or C# generics. Although JITs are often praised for their ability to optimize across module boundaries, generics stops this dead in its tracks. The CLR (.NET runtime) generates only one version of machine code for a generic which covers all reference types. On the other hand, the C++ optimizer runs for each combination of template parameters, and can inline dependent calls.

Next, with C# and Java you have very little control over memory layout. Parallel algorithms can suffer an order of magnitude performance degradation from false sharing of cache lines, and there's almost nothing that the developer can do about it. OTOH C++ provides tools to place objects at specific offsets relative to RAM pages and cache boundaries.

Ben Voigt
  • 260,885
  • 36
  • 380
  • 671
  • +1 Although the CLR is a good bit better than the JVM in terms of generics and run-time specialization (at least for "value types" ;-) –  Oct 03 '11 at 17:34
  • @pst: But programmers are discouraged from using value types, especially for mutable data. And at least the current version of the CLR has serious optimization deficiencies when dealing with value types. – Ben Voigt Oct 03 '11 at 17:36
  • as far as I know, only custom value types are somewhat discouraged (and they are often treated badly by the JIT too, a foreach over an array of structs results in horrible code for example), but ints etc aren't too bad (though the specialized ex-generic types often include things like `cmp rsi,rsi` to compare the type of int with the type of int - if the JIT team is reading this: please fix, even a simple sanity check would get rid of this) – harold Oct 03 '11 at 17:49
  • @Ben Voigt Ahh, I was meaning to focus on `System.Int32` vs `java.lang.Integer` and reified vs. erased generics (and what can be done with the non-boxed values) -- not that a custom value type should necessarily be used. –  Oct 03 '11 at 21:51
6

Consider the differences in the languages and the overhead this occurs -- even if such additional work is done "at the same efficiency" there is just more to do. Period. (This is the price that often comes with abstraction: development time can be [substantially] decreased for [moderate] increases in run-time.)

On the other hand, with a trivial function using no "language features" such as a loop computing a factorial ... then the numbers can become very competitive in certain cases. This can be seen in The Computer Language Benchmark Game (here is Java7 vs. C++, for instance).

Note that the implementation of a language (including JIT) and the optimizations ("-Ox") applied is also a major factor. (A language arguably "has no speed" in and of itself.)

Happy coding.


As Been Voigt pointed out, the JIT/AOT models are optimized for different aspects. (The Sun Oracle Java implementation even has a separate server VM and client VM which each prioritize difference use-cases.) Here are some SO posts discussing JIT vs. AOT:

Community
  • 1
  • 1
  • 1
    Also, the optimization effort (and time) of a good C++ compiler isn't suitable for JIT compilation, you could be adding minutes to the startup time if a JIT used all those optimization techniques. – Ben Voigt Oct 03 '11 at 17:22
5

"If C#, Java, or C++, for example, all eventually compile to machine code, why are they not equally performant?"

Both C# and Java compile to a bytecode which is eventually reduced to machine code by a virtual machine (e.g. for Java its called JVM). C++, however, is generally compiled down to assembly level initially.

The virtual machines can actually perform certain optimizations at runtime (one common example is bimorphic inlining) but in other cases the added overhead adversely affects the performance

Foo Bah
  • 23,255
  • 5
  • 49
  • 78
  • 3
    "Both C# and Java compile to bytecode" -- true. "evaluated at runtime" -- false, .NET (excluding MF) requires JIT compilation, and Java also uses it extensively. – Ben Voigt Oct 03 '11 at 17:19
  • @BenVoigt JIT compilation occurs when the program is first run (hence "runtime") I will clarify – Foo Bah Oct 03 '11 at 17:26
3

Are you aware that the same C++ code does not produce the same machine code with different compilers or different versions of the same compiler? Some compilers will take the same source and create a binary for the same target that is significantly faster than another compiler. For the same reasons other languages that compile to machine code will not perform the same. Some languages are easier to compile/optimize than others. Languages like Java do not compare as they do not compile to machine code they normally compile to a system independent bytecode and then run on a jvm, a virtual machine. the jvm being some code in some language compiled by some compiler which can be fast or slow depending on the code and compiler chosen. interpreted languages like java (bytecode) are slow compared to compiled directly to machine code.

Take some time to learn how to disassemble binaries that you have compiled. Read up on the bytecode type instruction sets behind java, python, etc. p-code that pascal used to use, etc, etc.

if you are talking about x86 computers you have a vast performance difference across that family. You can compile a binary that runs very fast relative to the clock rate on one x86 processor but the same binary runs really slow on another, usually the newer processor with the faster clock rate runs an older binary slower. Within the x86 world it is a futile effort to create a single binary that runs fast everywhere, so your compiler, if performance is desired, has to work significantly harder to try to target performance per system/processor.

Your question is similar to asking, if all vehicles basically have an engine and four wheels why can some go faster? Why can some haul more stuff than others?

old_timer
  • 62,459
  • 8
  • 79
  • 150
2

If C#, Java, or C++, for example, all eventually compile to machine code, why are they not equally performant?

Most simply - they don't all compile to the same machine code.

On a slightly different note, please understand that a lot of the claims about performance you'll see on the web will be wrong, and a lot of the measurements of performance you'll see on the web are out-of-date or unreliable or broken in some other way.

For example, phresnel pointed to measurements of a tiny multiplication program, and those measurements were:

  • made with Java 1.4 back in 2003 - the current version is Java 7

  • made in a very naïve way which prevented Java from completing compilation

Let's just run his program half-a-dozen times without re-starting the JVM and see what happens:

public class mult {

    public static void main(String[] args){
        for (int i=0; i<6; ++i) mult.program_main(args);
   }

    public static void program_main(String[] args) {
        long nbStep = 1000000000;
        long tCPU = System.currentTimeMillis();
        double t=1. , r= 0.9999999999999999999999999999999999;

        if ( args.length > 0 ) {
            nbStep = Integer.parseInt(args[0]);
            System.out.println( args[0] + " demandees" );
        }
        for ( long i = 0; i < nbStep; i++ ) {
            t = t * r;
        }
        tCPU = - tCPU + System.currentTimeMillis();
        System.out.println(nbStep + " multiplications en " +
            tCPU + " millisecondes ." );
    }
}


$ /usr/local/src/jdk1.7.0/bin/java -XX:+PrintCompilation -XX:+PrintGC mult
     53    1 %           mult::program_main @ 57 (122 bytes)
   4662    1 %           mult::program_main @ -2 (122 bytes)   made not entrant
1000000000 multiplications en 4609 millisecondes .
   4662    1             mult::program_main (122 bytes)
   4669    2 %           mult::program_main @ 57 (122 bytes)
1000000000 multiplications en 4612 millisecondes .
1000000000 multiplications en 564 millisecondes .
1000000000 multiplications en 563 millisecondes .
1000000000 multiplications en 563 millisecondes .
1000000000 multiplications en 563 millisecondes .

Once Java completes compilation the times drop from 4609ms to 563ms.

The Java code is 8 times faster than the naïve measurement would have you believe.

igouy
  • 2,433
  • 17
  • 15
  • thanks for that explanation! Interesting stuff especially since I'm learning Java now. – Mohamad Oct 16 '11 at 19:00
  • Thanks for the update. Two key-points are `Once Java completes compilation` and `without re-starting the JVM`. This may be good for some use cases, but is bad for some others. Readily compiled and optimized code runs fast right from scratch and without the need of a big abstract machine being held in memory. – Sebastian Mach Nov 02 '11 at 09:48