What kind of optimization makes C++ faster than C#?

Started by
65 comments, last by EddieV223 11 years, 3 months ago

Hi,

Just out of curiosity, I heard that game engine is mostly written in C++ because it is faster. But I don't know what kind of optimization C++ can offer over C#, can someone give some highlights so I can have general ideas of it

Regards

Advertisement

C++ operates at a lower level of abstraction than C#. In C++, you could (if you wanted to) trivially fill an array with machine code, cast it to a function and run that code (it would no longer have any amount of portability at that point).

C# compiles to a bytecode that wasn't the machine code of any processor (I say 'wasn't' because I haven't followed C# in a while; they may have built one since). C# programs are run by what is often called an interpreter even though C# runtime has always compiled to machine code on the fly. In a C++ program, the progammer has manual access to freeing memory (as well as the pointers to/within it). A C# program uses the garbage collector to take care of freeing memory (as in, C# doesn't have a 'delete' operator, just 'new').

Some people might even argue that C# could be faster because C# gets compiled on the fly, the runtime knows exactly what the best actual machine instructions are for the users exact hardware (as opposed to a C++ compiler having to target some lower denominator of CPU like no-SSE, unless you actually provide multiple binaries for users to choose from or users compile themselves).

The fact that a C++ process will never slow down, use an extra thread (or *gasp* pause) for a garbage collection is probably one of the best reasons for people who like control. I thought was going to come up with better arguments, but other than memory access and allocation, it's late and I'm drawing a blank. The runtime compiling C# bytecode on the fly takes time too, and I imagine there's a threshold of how many interprets of the function before it decides to compile.

New C/C++ Build Tool 'Stir' (doesn't just generate Makefiles, it does the build): https://github.com/space222/stir

Performance on modern hardware is less about optimizing instructions and more about memory access patterns and data layout. C++ lets you exactly control how your data is laid out in memory, so you can more explicitly control your memory behavior. C# tries to abstract this sort of thing away to favor programmer productivity at the cost of some performance.

I think most game engines being written in C++ is simply because C and C++ are extremely portable languages. If there were another language that was equally as portable you might see more engines written in that. It doesn't hurt that one of C++'s goals is also to be performance king.

there is no particular reason other than language age and general direction. C++ compilers are older and always had performances as their main objective where C# compilers are younger and never had raw performances as their main objective.
The 2 systems have just different agendas.. C++ favors runtime performance over programmers' productivity, C#'s balance is more to favor productivity over runtime performance.

Final note.. no language will make an engine "faster". Programmers write engines, good programmers will write a "faster" engine in C# than some inexperienced programmer using C++. I don't think the reason to adopt C++ in game programming has much to do with performance at all, it's more down to easiness to interact other low level libraries, existing code and resources (developers) reuse and lack of a performing C# runtime on consoles.

Stefano Casillo
TWITTER: [twitter]KunosStefano[/twitter]
AssettoCorsa - netKar PRO - Kunos Simulazioni

It will be interesting to see how much the garbage collectors performance improves with asynchronous operation over the coming years. I've read some recent articles that suggest it is markedly improved.

GC is another topic that always pops up in these kind of discussions... IMO is pure non-sense. GC won't trigger if you don't "new" stuff, and will be VERY fast if you don't have objects with medium life expectancy.. it's just a matter to take some time to understand how the system works and how you can make it work for you... it's much easier to learn to deal with .NET's GC than learning proper memory management in C++, simple or through the 6-7 "smart" pointers available.
Just as you try to avoid new and delete in your game loop in C++, avoid newing class objects in C# and GC won't cause any troubles.

Stefano Casillo
TWITTER: [twitter]KunosStefano[/twitter]
AssettoCorsa - netKar PRO - Kunos Simulazioni

GC is another topic that always pops up in these kind of discussions... IMO is pure non-sense. GC won't trigger if you don't "new" stuff, and will be VERY fast if you don't have objects with medium life expectancy.. it's just a matter to take some time to understand how the system works and how you can make it work for you... it's much easier to learn to deal with .NET's GC than learning proper memory management in C++, simple or through the 6-7 "smart" pointers available.
Just as you try to avoid new and delete in your game loop in C++, avoid newing class objects in C# and GC won't cause any troubles.

But really what you're suggesting here is that to "solve" the problem of the GC needing to halt all threads and needing an unknown amount of time to complete, that you do "manual memory management" and try to avoid invoking the GC, which also means designing around ctors/dtors and doing init/cleanup of your objects yourself so you can reuse instances. IMO that's a lot easier to do when there is no GC to worry about, and you reintroduce the problems GC was meant to solve (ie dangling pointers).

GC is another topic that always pops up in these kind of discussions... IMO is pure non-sense. GC won't trigger if you don't "new" stuff, and will be VERY fast if you don't have objects with medium life expectancy.. it's just a matter to take some time to understand how the system works and how you can make it work for you... it's much easier to learn to deal with .NET's GC than learning proper memory management in C++, simple or through the 6-7 "smart" pointers available.
Just as you try to avoid new and delete in your game loop in C++, avoid newing class objects in C# and GC won't cause any troubles.

But really what you're suggesting here is that to "solve" the problem of the GC needing to halt all threads and needing an unknown amount of time to complete, that you do "manual memory management" and try to avoid invoking the GC, which also means designing around ctors/dtors and doing init/cleanup of your objects yourself so you can reuse instances. IMO that's a lot easier to do when there is no GC to worry about, and you reintroduce the problems GC was meant to solve (ie dangling pointers).

I am not suggesting that. "Young" objects are collected by the GC without stopping the world... so those shouldn't create problems at all.

What I was saying is: be sensible... just as you are sensible with C++. And, if you really get in trouble with the GC, use the available techniques to avoid that (ie. object pools). Again, I don't see this being any more difficult than writing a custom allocator in C++.

I wrote a quite complex simulator using C# and never had any problem whatsoever with GC and/or stutters.. actualy, scratch that, I have never had any problem with any of my C# stuff with GC stutters... and I don't use object pools or any other trickery.. just try to be sensible in what I do.. understand the differences between structs and classes in C# goes a long way.

Stefano Casillo
TWITTER: [twitter]KunosStefano[/twitter]
AssettoCorsa - netKar PRO - Kunos Simulazioni

Portability is the big one for C#, but as for why it's considered 'slower', a VM compiled language like C# has to be translated at runtime, so an operation in native machine code will always have a significant advantage in speed (although a good VM can overcome that with caching). The simple reason is that a simple CPU instruction will execute at least twice as fast as a VM language instruction that has to be translated and then executed. There's more work to do for every single instruction, so you don't get the same speed unless the VM uses tricks to get that speed for you.

That said, modern machines, as was mentioned, can execute horrifically suboptimal code at near instantaneous speeds, so there's no reason to avoid C# unless you're doing something very low-level that needs to be blazing fast for some reason. Even in those cases you can find ways of bending C# to your will if you must. My only real gripe with C# is that it isn't Ruby. If I'm using a VM language I want it to JIT compile from raw scripts that read like Terry Pratchett novels and I want it do some of the crazy stuff that Ruby can get away with such as dynamically redefining classes at runtime or that beautiful, horrible monster known as eval().

C# is taken a lot more seriously than Ruby, though. <sad panda>

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

you dont seem to understand how C# runtime works at all, so your claim are as wrong as it gets.

Every single C# function gets compiled to native code by the JIT the first time it is invoked, from that point on, that function is running native code period. So the "more work to do for every instruction" is just... uninformed and uninformative.

This has been the case for ages, since Java started doing it loooong time ago.

Stefano Casillo
TWITTER: [twitter]KunosStefano[/twitter]
AssettoCorsa - netKar PRO - Kunos Simulazioni

This topic is closed to new replies.

Advertisement