8 ms·
The Hidden Cost of C++
- JoeAltmaier 17y agoI fixed a single line of code that drove our embedded processor to 50% cpu - it called 5 ctor/dtors. Changing it to a ref arg with a ref return, and dropped to under 10%. And yes, class defs had to be redeclared, the line of code was unchanged. Very indirect, utterly beyond the original authors comprehension.
- cppppp 17y agoThus proving it's possible to write crap code in any language.
- groby_b 17y agoWell, yes. The point here is that C++ makes it much easier to generate crap code.
- hazzen 17y agoDo you know how to fix almost every one of these cases for good? Just don't allow implicit copying of your classes. Problem solved. If someone tries to write that horrible code, they can't. This is more a question of knowing what you are doing and less of C++ sucking. C doesn't solve this - you still have to know what you are doing there or you end up shooting yourself in the foot. I much prefer having an interface with virtual methods to a struct with function pointers.
- JoeAltmaier 17y agoOf course! However the original author of the offending code didn't know to define private X(X&), so had the problem. C++ is a power saw; you can cut off your hand.
- psyklic 17y agoWhy do you use C++ for an embedded processor?
- JoeAltmaier 17y agoNo reason not to. Control your runtime and it's fine even for OS work.
- ecaradec 17y agoFounded and fixed in exactly 5 seconds with a profiler. So what ?
- icefox 17y agoAnd the hidden cost of using Java is that there is a JVM! These are only hidden to developers who don't really know C++. If you have virtual functions everywhere and have api that encourages copy or anonymous constructors you get what you asked for. And any profiler worth its salt should be able to show you what is really happening so you can quickly fix it. For a game shop who is worried about performance there are many little things you can have your developers do to prevent the compiler from doing many of these things and you should have it be part of your normal API reviews to make sure that developers follow them. A simple example class Box { public: Box(int x); }; class Foo { public: Foo(Box a) }; ... Foo(1); ... Pop Quiz: How would you change Foo (or the whole api) to cause Foo(1) to cause an error?
- bendotc 17y agoI'm pretty sure the author was comparing C++ to C, not to Java, but that's kinda beside the point, because as the article says, "The real hidden cost is that now, instead of looking at one piece of source -- the function itself -- I need to look at up to four different classes. Add possible ancestors to find out if a call is virtual." Put another way, the performance details we so often care about in projects for which we're using C++ are often non-local to the calling site. In order to know the performance characteristics of "a = foo(b, c)" I need to know a lot of things about the types of a, b, c, and foo, in addition to knowing what "foo" does inside its curly braces. In contrast, while it's hard to reason about the instructions being executed in Java in the same way we do in C or even to a large degree in C++, the types of a, b, c, and foo don't really matter, since passing in b and c is always a pass by reference or a primitive value copy, and foo is pretty much always going to be dispatched the same way (and constructors are preceded by a "new" operator at the call site, so we don't have to worry about that, either). Now, the only thing I care about is what's inside the foo method. I am of course not disputing that you need to know the language you're using. It's just that even when you know C++, if you can't trust everyone who ever touched the code, you need to check in a bunch of different places to reason about the performance characteristics of this one piece of code. Also, reasoning about the performance of C is all well and good right up until you realize that your compiler is better at translating C to assembler than you are and also, some jackass wrote a preprocessor macro called "foo" which does 18 different things. At some point, writing high-performance code is not about assuming you can guess what will happen but about seeing what's happening and trying to make something better happen. Disclaimer: it's been a good 4 years or so since I worked with Java, and on top of that, I've never cared too much about Java performance, so please take this with a grain of salt.
- huhtenberg 17y agoThere are constructors in C++. The water is wet, how sneaky of it. You either know the code you are working with or you don't. C++ or not.
- uriel 17y agoWhat he is pointing out is that C++ makes it harder to 'know the code you are working with'.
- ubernostrum 17y agoFWIW, I think you meant to say "what she is pointing out"...
- huhtenberg 17y agoI understand the point, and I am saying it is a trivial one. Calling func(x) in C may as well be causing x to be cloned or have some other nasty side effects that are not immediately obvious from looking at the code. Similarly how one should always keep in mind the existence of macros when reading C code, one should also keep in mind the existence of constructors and casting operators with C++. That's hardly the hidden cost. It is easier to write messy or obscure code in C++, especially for a novice programmer. But this has less to do with the language per se and more with established coding practices within specific team. If there is a hidden cost to C++, it is the fact that it requires higher level of competence. Not that it has constructors.
- groby_b 17y agoThe point is that, with a rational team, I can tell (roughly) what a function is doing, based on the name. It's not going to be called 'dot_product' and clone data passed in ;) What I can't tell is how much extra code the compiler will generate at this point, and that is the problem. > If there is a hidden cost to C++, it is the fact that it requires higher level of competence. Not that it has constructors. I didn't say the cost is in the constructors. They're a symptom of the cost. The cost is creating the mental model of your app. Sure, if the entire team consisted of C++ superstars, we might be able to get away with this. Given the team size on current games, (and the fact that superstars are rare) that's unlikely to be the case.
- dkarl 17y agoThe argument boils down to the fact that C++ can express more in a single line of code than C can. a = func(b,c); .... Is it a function call, a member function call, or is it an anonymous constructor? Are b and c implicitly invoking copy constructors for other classes as part of type coercion? Is that a normal assignment, or an assignment operator? Is there a cast operator involved? If it's such a complicated expression, then the equivalent C code would be correspondingly large and hard to understand, and it would be just as complicated to figure out its cost. The not-so-hidden cost of C is that even simple things end up being many, many lines of code. C++ was invented because certain kinds of C programs -- programs that were already being written in C -- were painfully verbose to express and complicated to change. If writing your code in C would be simple and clear, then you would have to be stupid to write it as complex, obscure C++. When your C++ code gets complex and you start banging your head against a wall, imagine re-expressing it in C. If the result would be an improvement, then you screwed up. If you blame C++ for screwing up, then you're a language feature junkie. Admit it and check yourself into rehab. That is the hidden cost. The mental model for a simple function call became incredibly large and complex, and every function call is potentially as complex. Which makes reasoning about performance a rather hard thing to do.... All that translates ultimately into either worse performance or longer development time. Neither one is something you like to hear about. I don't know how it is a "hidden cost," because it's quite well understood that any reasonably expressive language can say more in a single line of code than C. Anyway, the size of the mental model of your program is what you should be worried about. C++ doesn't give you worse performance or longer development time. It gives you more choices for how you express your program. It means more different problems, because you can write C-style code and have C-style problems, or you can use the possibilities C++ offers and have different problems. If you already know that the choices forced on you by C are usually the right ones for your domain, then by all means apply that knowledge to how you write C++. For instance: Worse, it makes profiling harder than necessary. All the type coercions that happen at the API level will show up as separate functions, not attributed to the callee, but the caller. If you want them attributed to the callee, it's pretty simple. Do the coercions in the callee, just like you would have in C.
- groby_b 17y agoYour counterargument boils down to "use C instead". That is exactly what I am currently advocating. I don't blame C++ for screwing up - I blame it for being an ill-designed language that makes it easy to screw up. Any of the high-level features of C++ are well-implemented in any number of decent languages that don't obfuscate your code and incur horrible link times. And my argument (for game development) is that C++ is indeed the wrong language for the domain. In fact, I'd argue it's wrong for most, if not all domains. And I'm not exactly alone - I can't recall any prominent figure that actually thinks C++ is a decent language, except Bjarne Stroustrup. (Correct me if I'm wrong - I'd love to hear about it!) <blockquote> If you want them attributed to the callee, it's pretty simple. Do the coercions in the callee, just like you would have in C. </blockquote> The issue is that in many instances, I own the caller, but not the callee. And the person owning the callee can unintentionally make my code perform worse by simply changing the API. Or adding a destructor.
- st3fan 17y agoWow it is C++ bashing season again it seems. Yes you have to know what you are doing in C++. But this is not different in other languages. Understanding and optimizing code is a touch discipline. No matter what language. There is also another side to this story. C++ can absolutely generate highly efficient code. Code that performs much better than C equivalents. For example, I've looked a lot at generated (iPhone/ARM) code for cases where (function) templates are used and C++ wins most of the time. Doing compile-time configuration of code is usually much more efficient than having runtime-configured code. By using templates and inline functions the compiler can do some seriously cool performance tricks. Sire, there may be more code (bigger text segment) but that is a small sacrifice for what you gain in speed. Specially these days where a 8MB fart app is easily downloaded over a 3G connection without hesitation.
- groby_b 17y agoYes, you can use C++ to do amazing things. In large-scale projects, that usually backfires, though, unless you contain the "magic", because interactions in C++ are harder to predict. The problem with C++ is not that you have to know what you are doing - you also have to know what everybody else is doing. And the "small sacrifice" of a bigger text segment is a big one in this particular domain. We only have 512M available, and a tremendous chunk of that is eaten up by graphics. An executable seriously exceeding ~20MB would spell trouble. And I'd argue that AAA games are slightly more complex than a fart app ;)
- psyklic 17y agoWhen I code in a team, we don't just write one big mass of C++ and hope that all the interactions work out okay! Instead, we follow modular programming practices. If some interactions are too slow, we know who didn't optimize his section and we kick the problem back to him.
- groby_b 17y agoThat would be nice. Except that - again, for performance reasons - we often have to break the boundaries between modules. Most game engines are a tangled web, since many parts are talking to many other parts. (I think there are ways around that, and there will certainly be a post on it. But that's the way things work right now) So it's not always that easy to just find a guilty party and blame them.
- psyklic 17y agoThe author is worried about optimizing. He should know that in high-performance applications, you should just write the code then profile it. We all know that C++ is fast; the real question the author should be asking is whether C or C++ can more quickly express ideas/is more maintainable/etc. This post about not knowing how a function is called is really splitting hairs, bordering on irrelevant imo -- sure, it can affect the time taken to call a function by ten times, but this is not a large performance hit at all for 95% of your code, and even less since most high performance items are on the GPU. If it ends up that it does affect performance in a critical area (found via profiling), the author even suggested how it can easily be fixed. I don't see what the big deal is. If you're a game programmer, change your coding conventions so that you don't get into problems like this.
- groby_b 17y agoShe does know that you need to profile, TYVM. But ideally, you pay at least cursory attention to performance even when you write the code. Writing code with complete disregard to performance, saying "oh, the profiler will find it" is not an acceptable choice. You don't write sloppy code and argue that you can find bugs in the debugger, either, do you? And no, most high-performance items are not on the GPU. Most games are already GPU-bound, so you want to take load off of it. (Also, with the arrival or LRB et al., the GPU/CPU distinction becomes moot) If those issues were irrelevant, I wouldn't care about them. There's one thing I do care about: Shipping high quality games, developed as fast as possible. C++ hurts that goal. And no, the 95%/5% rule does not hold true in game development. But don't trust me - read, for example, Tim Sweeny, the guy behind the Unreal engine. According to him, there are not hotspots. Another Unreal programmer: http://lambda-the-ultimate.org/node/1277#comment-14238 http://lambda-the-ultimate.org/node/1277#comment-14238 "Dan Vogel, was fond of pointing out that UE2 had a basically flat profile" That is the big deal. In a flat profile, things like calling overhead matter everywhere.
- psyklic 17y agoWorrying about not knowing how functions are called on your first iteration is such a moot point. Simple programming conventions (e.g. no multiple inheritance) can eliminate this problem. Even if each function call does take 10x longer, I would argue that in 99% of the cases you wouldn't even notice the difference, practically speaking. Shipping high quality games, developed as fast as possible. If this is what you care about, then you should care about optimizing AFTER you write the initial version and identify hot spots. You don't write sloppy code and argue that you can find bugs in the debugger, either, do you? Sloppy code vs. "slow" code are vastly different. I would much much rather have slow code, since 99% of the time it isn't even noticed by the user. Hence, it was worth not thinking about how to improve it. most high-performance items are not on the GPU. I agree that you want to take load off the GPU. However, CPU speed has not been the limiting factor in games for a LONG time. In fact, in conferences such as the GDC it is rarely discussed any longer, only in the context of using more CPU if available for non-engine tasks such as AI. And no, the 95%/5% rule does not hold true in game development. There is a big difference between what is essentially a graphics engine (you reference the Unreal engine) and a video game. If you look at modern games, do you think it is a waste that they are based on interpreted scripting languages? They can do this because as I said above, CPU isn't as limiting a factor as before.
- acg 17y agoIt always surprises me that some will seek to defend C++ when even its author seems to admit that it is flawed. Many famous programmers have pointed to the language's limitations. People use it because of pragmatism: it has what they need for the APIs they want to use. If you think C++ is not overly complicated, just what is a protected abstract virtual base pure virtual private destructor and when was the last time you needed one? Tom Cargill