Jump to content

  • Log In with Google      Sign In   
  • Create Account

Interested in a FREE copy of HTML5 game maker Construct 2?

We'll be giving away three Personal Edition licences in next Tuesday's GDNet Direct email newsletter!

Sign up from the right-hand sidebar on our homepage and read Tuesday's newsletter for details!


We're also offering banner ads on our site from just $5! 1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.


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


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
70 replies to this topic

#1 ynm   Members   -  Reputation: 172

Like
1Likes
Like

Posted 26 December 2012 - 11:03 PM

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



Sponsor:

#2 beans222   Members   -  Reputation: 1131

Like
4Likes
Like

Posted 26 December 2012 - 11:21 PM

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.


Edited by beans222, 26 December 2012 - 11:25 PM.

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

 


#3 rdragon1   Crossbones+   -  Reputation: 1200

Like
9Likes
Like

Posted 27 December 2012 - 12:23 AM

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.



#4 kunos   Crossbones+   -  Reputation: 2207

Like
2Likes
Like

Posted 27 December 2012 - 12:23 AM

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.

Edited by kunos, 27 December 2012 - 12:28 AM.

Stefano Casillo
Lead Programmer
TWITTER: @KunosStefano
AssettoCorsa - netKar PRO - Kunos Simulazioni

#5 Gavin Williams   Members   -  Reputation: 686

Like
1Likes
Like

Posted 27 December 2012 - 12:45 AM

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.



#6 kunos   Crossbones+   -  Reputation: 2207

Like
5Likes
Like

Posted 27 December 2012 - 01:10 AM

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.

Edited by kunos, 27 December 2012 - 01:12 AM.

Stefano Casillo
Lead Programmer
TWITTER: @KunosStefano
AssettoCorsa - netKar PRO - Kunos Simulazioni

#7 rdragon1   Crossbones+   -  Reputation: 1200

Like
0Likes
Like

Posted 27 December 2012 - 01:17 AM

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).



#8 kunos   Crossbones+   -  Reputation: 2207

Like
2Likes
Like

Posted 27 December 2012 - 01:40 AM

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
Lead Programmer
TWITTER: @KunosStefano
AssettoCorsa - netKar PRO - Kunos Simulazioni

#9 Khatharr   Crossbones+   -  Reputation: 3030

Like
0Likes
Like

Posted 27 December 2012 - 02:08 AM

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>


Edited by Khatharr, 27 December 2012 - 02:10 AM.

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.

#10 kunos   Crossbones+   -  Reputation: 2207

Like
2Likes
Like

Posted 27 December 2012 - 02:25 AM

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
Lead Programmer
TWITTER: @KunosStefano
AssettoCorsa - netKar PRO - Kunos Simulazioni

#11 rdragon1   Crossbones+   -  Reputation: 1200

Like
0Likes
Like

Posted 27 December 2012 - 02:30 AM

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.

 

just because it's 'native' code doesn't mean it's as optimized as running it through an offline compiler, which typically has a lot more freedom in how long it can spend generating optimized code. JITted code is typically not as efficient. On the other hand, there are also situations where JIT compilers can use instrumentation to re-JIT and optimize code based on data that offline compilers don't have access to.



#12 kunos   Crossbones+   -  Reputation: 2207

Like
0Likes
Like

Posted 27 December 2012 - 02:33 AM

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.

 

just because it's 'native' code doesn't mean it's as optimized as running it through an offline compiler, which typically has a lot more freedom in how long it can spend generating optimized code. JITted code is typically not as efficient. On the other hand, there are also situations where JIT compilers can use instrumentation to re-JIT and optimize code based on data that offline compilers don't have access to.

 

where exactly did I say that jitted code is "as efficient as offline compiled native code"? 


Stefano Casillo
Lead Programmer
TWITTER: @KunosStefano
AssettoCorsa - netKar PRO - Kunos Simulazioni

#13 rdragon1   Crossbones+   -  Reputation: 1200

Like
0Likes
Like

Posted 27 December 2012 - 02:54 AM

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.

 

just because it's 'native' code doesn't mean it's as optimized as running it through an offline compiler, which typically has a lot more freedom in how long it can spend generating optimized code. JITted code is typically not as efficient. On the other hand, there are also situations where JIT compilers can use instrumentation to re-JIT and optimize code based on data that offline compilers don't have access to.

 

where exactly did I say that jitted code is "as efficient as offline compiled native code"? 

 

was just pointing it out for others' benefit as it's a common misconception



#14 Bluefirehawk   Crossbones+   -  Reputation: 1232

Like
1Likes
Like

Posted 27 December 2012 - 03:50 AM

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.
I am not sure what you mean by "extremely portable". Yes, in theory they are, but thanks to non standard extensions (although very useful, #pragma once for example), and different architectures, I would say they are most definitely not portable. You have to think about portability in your program design, something most java and C# developers don't even have to think about. C's target isn't to be portable, it is a system language, as I recall, Ritchie and Tompsons target was to have a language offering enough high-level support to not get lost in your code, while having the power to dance with single bits.

Back to topic:
I wrote a program comparing java and C++, the later iterations of java have a good runtime optimisation. Both programs syntactically did the same (as a java programmer would write a C++ program), multiplying BIG matrices. There was no big performance gap between java and C++. But when I rewrote the C++ algorithm to simply use pointer arithmetics, C++ ran several times faster than it's counterpart.

The lesson for me was: Yes, C++ is faster when you know how and you have the right problem to solve. But Java has become fast enough, the performance gap for your everyday business application is negligible.

E: You can even write cool indie game engines with C# and Java. Yes it is not as powerful as a C++ engine, but you don't try to make a better looking, more efficient engine than Source,Unreal and friends, right?

Edited by Bluefirehawk, 27 December 2012 - 03:53 AM.

Project: Project
Setting fire to these damn cows one entry at a time!

#15 rdragon1   Crossbones+   -  Reputation: 1200

Like
0Likes
Like

Posted 27 December 2012 - 03:57 AM

I am not sure what you mean by "extremely portable".

 

I mean there is a C / C++ compiler available for virtually every platform. This would be a prerequisite for all of the C# / Java / other language runtimes to exist, since all of the ones I know of are implemented in C / C++.

 

I'm not saying you don't still have to port your engine - you certainly need to port your engine to different OS'es / graphics apis / input apis / etc, but at least you can. For example, I don't think any of X360/PS3/Wii have a C# or Java runtime available - some have a form of C# you can use, but it's a severely restricted environment compared to native C++.



#16 rdragon1   Crossbones+   -  Reputation: 1200

Like
1Likes
Like

Posted 27 December 2012 - 04:03 AM

The lesson for me was: Yes, C++ is faster when you know how

 

This is a very important point. Many game engine developers have the "know how", but the problem you run into with managed environments is that even if you know how, you simply don't have low level enough access to implement the optimizations, so you're stuck. That's a deal breaker for a major engine to adopting a language.



#17 Hodgman   Moderators   -  Reputation: 30901

Like
6Likes
Like

Posted 27 December 2012 - 05:21 AM

C's target isn't to be portable

``C has been characterized (both admiringly and invidiously) as a portable assembly language,'' -- Dennis M. Ritchie

 

 

At the time, porting code meant translating it to a different assembly language. C was invented so that all the common assembly instructions could be represented by high-level code, that was still ambiguous enough for it to be "portable" to every different CPU.

 

"Portable" has come to mean many different things over the years, from this idea of not having to re-write your assembly code, to Java's "WORA" (or Write Once, Debug Everywhere)... For discussions on portability, you've really got to define your terms.

 

If someone says that C is more portable than C#, they're correct because for almost any embedded system that you want to hack, you'll likely be able to find a C compiler for it.

If someone says that C# is more portable than C, they're correct because you can run the same binary code on a Windows and Linux system without re-compiling.

Apples and oranges...

 

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.

Yes, C# is more optimized for general programmer productivity than C++, not many would dispute that, but the topic isn't which one is easier.

 

Handling memory in C++ is complicated, and there's no 'right' way to do it.

IMHO, manual use of new/delete are bad, smart-pointers are bad, and GC is also bad. The only method left that I like are scopes sad.png

 

In the last game I shipped, the GC (Lua, not C#) was consuming too much time per frame (or more to the point, it was consuming too much time every n frames). So, you introduce a limit on the amount of time it can use per frame, and move on... but then you realise that now garbage is building up every frame, and you run out of RAM and crash... so you have to go and fix your code to stop generating garbage, which means not creating objects.

So now the easy memory solution (GC) is out of bounds, and you're left struggling with all the "complex" methods that you use in unmanaged C++ anyway.

 

Performance on modern hardware is less about optimizing instructions and more about memory access patterns and data layout. 

I think most game engines being written in C++ is simply because C and C++ are extremely portable languages.

QFE.

 

Optimizing for memory access patterns is the primary optimisation these days; both C++ and C# give you tools to do this, but IMHO, it's easier to perform these optimisations in C or C++, due to the fact that you can treat RAM as a big array of bytes if you want to.

Every game console has a C/C++ compiler, whereas writing a console game in C# is a lot more of a hassle. This gives C++ a lot of inertia.

 

For an example of memory optimisation, I've got a "shader-collection" class, that has an operation to find the index of a particular "technique" object by name.

Inside this class's memory allocation (which is one contiguous block, for it's members, and all sub-objects, and their members, and so on) there is an array of "string-offsets", which are a pair of a 16-bit hash, and a 16-bit offset value. You hash the input "name" string, then binary-search this array for a matching hash (which is a linear, prefetchable operation). When you find a match, you've also found an offset ahead into the "shader collection"'s allocation, to a contiguous array of character data, where all the string data is actually stored; if a memcmp there matches, then you've found your index.

If you batch up these queries, so you're converting a collection of names into indices at once, then this entire data set can be made to be resident in the cache for the majority of the searches, speeding them up 100-fold. Moreover, the data is carefully arranged so that only data that is required by the current operation exists within a contiguous block, so there is minimal cache pollution.

C-style casting makes writing this kind of stuff really easy, while C#'s tools for programming on this level of abstraction are extremely verbose and complex.


Edited by Hodgman, 27 December 2012 - 07:32 AM.


#18 Rattenhirn   Crossbones+   -  Reputation: 1781

Like
5Likes
Like

Posted 27 December 2012 - 07:55 AM

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.


Dynamic memory management incurs by definition a certain amount of performance penalties. No matter what system is used, these penalties can be managed.

However, in a language that forces you to use one single tool for dynamic memory management, the garbage collector, limits one's flexibility in dealing with issues that arise quite a lot.

This is why languages that allow manual memory management will always have an edge in performance potential. Whether that's used is up to the programmers involved.

I don't think that in the future, general GCs will be that good, that manual memory management won't matter any more. After all, GCs also need to be implemented somehow. ;)

So what will happen (and is happening already, if you look close enough), is that manual and automatic memory management will be mixed.
 

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.


It's important to know that not all platforms allow emitting native code, because you either can't write to executable pages, can't change the executable flag on pages or the platform will only execute code signed with a secret key. This is especially true for the platforms we're usually dealing with in gamedev (consoles, smartphones, tablets).

In all of these cases, there's no (allowed) way to avoid using runtime interpretation of byte code.

It is possible, to "pre-JIT" byte code in some languages, but at that point you're basically back to a standard compiled language with a worse compiler.

Additionally, thanks to the LLVM project (and others like Cint or TCC), it's possible to JIT or interpret C and C++ source or byte code, closing this particular gap even more.

What remains is, that "cafe based" languages (Java, .net) need to assume a virtual machine to work properly. So runtime performance can only ever be as good as this virtual machine matches to the real machine used, causing more and more troubles as the virtual machine ages and the real machines progress.

Therefor, one will, all other things being equal, always pay a performance penalty when using languages targeting virtual machines. The question is how big this gap is. In my opinion, this performance penalty will shrink to almost zero over time, as JIT and regular compilers converge (again, see LLVM).


Edited by Rattenhirn, 27 December 2012 - 07:57 AM.


#19 Khatharr   Crossbones+   -  Reputation: 3030

Like
0Likes
Like

Posted 27 December 2012 - 09:56 AM

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.

 

Gosh, that's clever. I wonder if there's a name for that. Hmm... Let's call it 'caching'. I hope it catches on. Wait, I must be a time traveler because I already said:

 

 (although a good VM can overcome that with caching)

 

It's plain sense that a native instruction will cause less work than a native instruction plus translation. That's the whole reason why the VM caches the translated instructions.


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.

#20 dmreichard   Members   -  Reputation: 384

Like
0Likes
Like

Posted 27 December 2012 - 11:20 AM

What remains is, that "cafe based" languages (Java, .net) need to assume a virtual machine to work properly. So runtime performance can only ever be as good as this virtual machine matches to the real machine used, causing more and more troubles as the virtual machine ages and the real machines progress.

 

I'm not biased towards either option, however I just wanted to point out that the same goes for compilers. There are plenty of good reasons to use either C++ or C# depending on your goal, platform, library restrictions and a multitude of other factors.

 

In response to the OP, yes as other posters have pointed out a lot of your runtime efficiency is all in how you design and structure the code. I'm repeating kunos when saying this but I feel it has to be reiterated. One experienced with C# will more than likely write better performing code than they could write in C++ without additional learning.






Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS