Coffeehouse Thread

69 posts

is managed code faster than native?

Back to Forum: Coffeehouse
  • FuncOfT

    , Sven Groot wrote

    *snip*

    Nice theory, except the C# compiler is written in C++ too

    Not any more, it was rewritten in C#.

  • kettch

    @FuncOfT: In fact, I think they mentioned that they wrote each language's compiler in it's target language. C++, C# and VB.Net. I don't know about F#.

  • felix9

    , FuncOfT wrote

    *snip*

    Not any more, it was rewritten in C#.

    See: ~04:10 http://channel9.msdn.com/Events/Ch9Live/Channel-9-Live-at-Tech-Ed-Europe-2012/Dustin-Campbell-Roslyn

    Roslyn is slower WRT raw performance.

  • magicalclick

    Assembly > C++ > C# in raw performance.

    C# > C++ > Assembly in ease of programing.

    And since C# is very close to C++ in performance, you shouldn't worry about that. Often you ended up making cleaner faster program because it is easier using C# and gives your more time and less intimidation when trying to improve your program.

    To me, C# for most apps and decent 3D games, C++ for extreme games and it is not limited to .Net platform.

    Leaving WM on 5/2018 if no apps, no dedicated billboards where I drive, no Store name.
    Last modified
  • evildictait​or

    , magicalclick wrote

    Assembly > C++ > C# in raw performance.

    I'd like the caveat that:

    Optimal assembly > optimal C++ > optimal C# in raw performance.

    With very few exceptions, people that write assembly write worse assembly (in terms of correctness and in terms of performance) than comes out the back of the C++ compiler and worse assembly than comes out of the C# JITter.

    And people that make use of __asm in C++ not only make their code no longer portable to platforms other than x86 (including ARM and x64), they also cause the compiler to turn off inlining for that function, turn off optimisations for the function and cause the compiler to save and restore all of the registers it heuristically thinks it touches. It can also cause the compiler to make really suboptimal use of registers in the code:

    Consequently the inline code inside a C++ function:

    __asm { xor ebx, ebx; mov [_local], ebx }

    is technically equivilent to _local = 0, but can be thousands of times slower in practice (since the compiler now has to save EBX over the block, no longer knows that _local is zero for optimisations later in the function, and can't initialize _local at the same time as it initializes all of the other local variables (using STOSDs not movs), it can't inline the function, can't make the function EBP-less and can't shuffle the asm block around to get better store/fetch performance on the processor, can't use SIMD and can't perform any compile-time checks of the code.

    Even worse - if you dare to use that code in a declspec naked function without saving EBX over the call yourself, you might find that EBX is a register that you shouldn't be blindly destroying - and if you do it in kernel mode there's an exploitable error in there too.

    It's also much harder to do algorithmic improvement in lower level languages. For example, doing a quick-sort in assembler is so difficult that in practice people do easier-to-implement, less-likely-to-go-wrong, but worse-big-Oh-performance algorithms when forced to use lower level languages, and consequently 2 days work on a handwritten assembler algorithm is likely to yield a slower algorithm than an equivalent amount of work on optimising the algorithm in a higher level language.

    Morale of the story is that optimal assembler > optimal C++, but for nearly all values of "you", your assembler <<< Microsoft C++ compiler release output of your C++.

  • Ion Todirel

    , blowdart wrote

    *snip*

     

    Oh tosh. You could make that claim of anything that compiles to native. Speed is dependant on the compiler, the runtime, and how crappy the user's code is.

    sure, but C++ is also one of its kind with the not paying for what you don't use philosophy, being native doesn't necessarily mean control or speed given high enough abstractions

  • Bass

    I haven't really seen a good argument, ever, on why managed languages (or dynamic/static) inherently perform better or worse than unmanaged. You can look at benchmarks all day, but all it will tell you is compilers/runtimes are better at optimization than others. Nothing about languages.

  • evildictait​or

    Dynamic languages versus static languages is easy: dynamic languages need more runtime checks and an embedded parser in order to run code. Static languages get to remove certain checks (e.g. C++ doesn't need to decide if a variable should use an ADD opcode to add two integers or an FADD or possible a full-blown strncat, whereas PHP given a random variable doesn't know until runtime.

    Managed languages versus unmanaged ones is less obviously one side or the other - managed languages get to take certain optimisations based on runtime data that static ones can't do, whereas static languages get to make slower, but ultimately more effective optimisations because they're not under pressure to return quickly so that the runtime isn't held up by the JIT.

    Similarly the heap versus the GC are much of a muchness. The GC has to do expensive collects, but native heaps have to do expensive free-block coalescing and lose performance due to stuff like fragmentation pushing up the working set of the process and hence accesses being more likely to fault.

    What I would really like though is the ability to tell C# to compile my app down not to CIL but to x86 and for it to burn in or link to a GC implementation. If it could strip all symbols, types and reflective information from the binary like I can strip PDBs from C++ that would be extraordinarily awesome too.

  • Bass

    The situations where you can know that when a function is called from somewhere it will always be passed values of a certain type are the same in static and dynamic languages (that's how type inference works). And in the situations where you can't, static typing doesn't buy you anything because you'd have to check the type anyway (eg, typecasting unpredictable input from I/O) or use interfaces to accomplish what dynamic languages give you for free via duck typing. (Also poorly and more verbosely.)

  • Ion Todirel

    , Sven Groot wrote

    *snip*

    There are several reasons for this. Most importantly, C++ is a far more complex language (grammar wise) than C# so it takes longer to parse (particularly thanks to templates, which are a Turing-complete compile-time language), and the C++ compiler performs far more extensive optimizations (in the .Net world, most optimizations are performed by the JIT, so the work of the C# compiler is relatively light).

    Based on research made by folk at the LLVM, it seems that the include system is the main offender, with modules they proved the the compilation time can be greatly reduced. The include system is flexible, and for any project you could probably find the optimal structure that yields the optimal compilation times, but not without sacrificing maintainability and readability.

  • evildictait​or

    , Bass wrote

    The situations where you can know that when a function is called from somewhere it will always be passed values of a certain type are the same in static and dynamic languages (that's how type inference works).

    Type inference can sometimes tell what type a parameter will be (if you can, you get fast speeds. If you can't you have to do a slow runtime check).

    Static types mean that you can always tell that a parameter will be, meaning that you never need to do a slow runtime check to ask what a parameter is to decide whether + means ADD or FADD or call variant_Add.

     

     

  • Bass

    @evildictaitor:

    Can you point out a situation where type inference can't tell what a type would be, but a programmer can?

  • Bass

    There is major things that I feel constitute what I expect out of dynamic languages:

    • Types as values instead of types as variables
    • Duck typing
    • Optional metaprogramming / reflection

    I'd like to really know why, on an objective level, these features would effect performance in any way, that a static language would not suffer from, ie. from virtual calls or type casting when needed to do certain things in a static language like I/O or polymorphism and some kinds of signalling. Reflection/metaprogramming is the one feature that I can see really complicating optimization (especially AOT), because it allows the programmer to literally f**k with code in arbitrary ways at runtime. But that sort of thing is optional even in dynamic languages and I think it's just "slow" in any language that offers it.

    I like strongly typed dynamic languages. Also, the language doesn't need to be compiled at runtime, it could have a compiler that precompiles directly into machine code, if this improves performance.

    Just to be specific..a dynamic language still should have a rich type system that exposes a reasonable amount of the functionality available on the CPU. If it only supports is the String datatype or floats, well that's not really what I'm looking for.

    JavaScript/V8 engine is pretty interesting because at least on Debian's benchmark game it performance competitively with Mono and Java (both considered very fast), despite having inherited JavaScript's various optimization difficulties and not doing any precompilation like they do, oh and being significantly newer.

  • evildictait​or

    , Bass wrote

    @evildictaitor:

    Can you point out a situation where type inference can't tell what a type would be, but a programmer can?

    function  add(var1, var2) { return var1 + var2; }

    alert(add(1, 2)); alert(add("1", "2"));

    In this case, add will be implemented by a call, not by an ADD because two type inference passes will come back with different types.

    If this was done via a C++ template the first time will be an ADD opcode for integer addition and the second one will be done by a call to a string concatenation routine.

  • Bass

    I don't get why that would require runtime type checking. The type of the values you are passing to add could be unambiguously determined to be (integer,integer) and (string,string). Thus the compiler could create two functions (or even inline the code), one for add(string,string) and one for add(int,int).

  • evildictait​or

    , Bass wrote

    I don't get why that would require runtime type checking. The type of the values you are passing to add could be unambiguously determined to be (integer,integer) and (string,string). Thus the compiler could create two functions (or even inline the code), one for add(string,string) and one for add(int,int).

    If you think type inference in real programs is quick, easy or accurate, I suggest you go work for a compilers team for a short while. They will disfranchise you of this opinion.

    Perhaps then you will understand why knowing what a type is allows you to generate faster code than trying to guess what it is.

  • Bass

    , evildictait​or wrote

    *snip*

    If you think type inference in real programs is quick, easy or accurate, I suggest you go work for a compilers team for a short while. They will disfranchise you of this opinion.

    Perhaps then you will understand why knowing what a type is allows you to generate faster code than trying to guess what it is.

    Explain when it isn't accurate. In your example, it would be trivial for the compiler to know the type, the parens give away the string and the fact that the value is a number gives away the int. Thus the compiler could optimize the function call without losing the advantages of dynamic behavior.

    I'm trying to understand the instances when you wouldn't know what a type would be ahead-of-time. The only times I can think of involve metaprogramming and indirect calls (ie. polymorphism/interfaces/virtual calls, like using something like an "IAddable" and one function, which is what static languages use to get around the lack of duck typing in the language).

    I didn't say writing an optimizing compiler is easy for dynamic language (or for static language even), just that I don't see what static languages inherently buy in performance (ie. static languages are "inherently" more optimization). If there isn't a inherent performance advantage, I want to see at least some example of a hard AI problem involved in decorating variables with types.

  • evildictait​or

    , Bass wrote

    *snip*

    Explain when it isn't accurate. 

    Because type inference between functions is a whole program optimisation, and is expensive and infeasible for large programs. Inlining functions can also only be done for certain classes of functions - namely small ones with no recursion.

    Ultimately your solution for making a program written without types faster is to change it into a program written with types by type inference. Surely even you can see that it's faster (and more likely to find bugs) if you ask the programmer what the type should be in rather than trying (and often failing) to infer the type by second guessing the programmer.

Comments closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums, or Contact Us and let us know.