C++ and Beyond 2011: Herb Sutter - Why C++? (video)
channel9.msdn.com
channel9.msdn.com
No-compromise performance still happens with C and C++. Secondarily Fortran and Ada for their respective industries. I doubt that'll change anytime soon as I'm not seeing any languages that are choosing the same priorities. Go isn't a systems language by any measure or estimation in design or implementation, and Rust is a type-safety-centric compromise that will likely end up somewhere around Java in performance. I'm not seeing much indication from the mailing list that safety-off all-go performance is a priority for the Rust devs because they seemed to favor avoiding the quagmire of a large C++ codebase.
I think realistically a "clean start" of C++ with the best of C++11 and earlier standards but removing a lot of the less worthwhile features would go a long way. Won't happen though. A lot of people avoid those problems by just writing C. Missing out on the cool reference counted pointers and compile-time stuff though.
Any language that wants to fully replace C/C++ has to embrace "pay only for what you use". Mandatory anything at runtime at the language design level is untenable.
"Pay only for what you use" sounds like a really great principle. But ...
Every user of C++ pays the cost (in convenience and reduced bugs) of not having automatic memory management, even if they don't need its benefits in memory consumption and avoiding GC pauses.
Many users of C++ pay a performance cost for the language's lack of automatic memory management, because getting the best performance from an automatic memory management system requires tight language integration and you can't have that with C++. (So they fall back on refcounting or Boehm-Weiser-like conservative GC, and pay the cost.)
Every user of C++ pays the cost (in ugly syntax and long compile times because of the included-header model) of backward compatibility with C, even if they don't really need that.
Many users of C++ pay a performance cost for this, because the C++ compilation model means that template-heavy code tends to end up with a lot of more or less duplicate object code, which means more cache misses and more time spent paging code in from disc.
Every user of C++ pays the cost (in overflow bugs and the like) of using integral types tied closely to the underlying processor architecture, even if they don't need the performance advantages it brings.
Some C++ users pay a performance cost for this, because if an application actually needs bignums then it'll typically be implemented using some general-purpose bignum system like GMP, which will typically perform very well for really large numbers but may be totally unsuited for smaller ones -- and sometimes most of the numbers you're computing with are in fact the small ones.
C++ uses "pay only for what you use" as a slogan, but it applies that slogan only in a very narrow domain. C++ users are paying, all the time, for lots of things they don't use. Quite a lot of C++ users are paying in performance for things they don't use. The slogan is misleading because it directs your attention to the obvious, explicit costs of features like automatic memory management and integer types that overflow gracefully, but there are plenty of costs that aren't so obvious.
(Lest I be misunderstood: For many applications C++ is a great choice. For some it's the only realistic choice. I am not, for instance, arguing that actually everyone should be programming in Common Lisp or something. I just think "you don't pay for what you don't use" is a really misleading slogan.)
Now that's just empty rhetoric. What about 'users of Java pay a performance cost for not having a stack, and creating every single object in the heap'?
I don't pay for unneeded garbage collection, and you can't use your rhetoric around it.
It is. We are counting every cycle in the JS<->Rust bridge, for example, when compared against C++ Gecko and WebKit.
As a general policy, Mozilla does not take performance regressions. Servo is a total non-starter if it is a performance regression over the existing C++ browser engines.
That said, Rust does some things by default that C++ doesn't do by default, such as stack overflow checks and array bounds checks. This is because we believe that, for most apps, they're the right defaults when weighed against the dire security consequences. It's planned for both of these to be able to be switched off in the future. (It's already possible to switch off array bounds checks, and switching off stack growth will be needed for competitive JS<->Rust interoperability in Servo.)
I really want real numbers, not idle conjecture. Gosling was fond of speculating that Java would eventually be faster than C and C++ because of $COOL_FEATURE_X, but nothing ever materialized or made enough of a difference.
Concurrent GC didn't make up the difference. JIT didn't make up the difference. Why would I believe anybody else bringing the same message?
This cyclical "we're gunna be faster and here is the cool compiler research saying so!" is why the programming language community has thoroughly alienated my colleagues that work in systems.
They can't afford to build their systems on speculation, it HAS to work. They believe nothing anybody says about programming languages now.
What's the most impactful territory to conquer that could help make up the difference?
Rust's design allows for a subset of valid programs that could be constructed in C++. It's the same kind of thinking that Haskell utilizes. The type system is useful in Haskell because it disallows potentially invalid programs giving you a smaller space to reason about. (cf. Total programming)
However, that subsetting means avenues and opportunities for optimization are eliminated. Optimization sometimes means doing something batshit.
A high-level 6502 emulator a friend wrote in x86 asm comes to mind.
Certainly, if you need something to be shipped soon, and you need it to be performance-competitive with C++, don't use Rust. Despite the fact that it's backed with LLVM, the compiler is quite immature.
I don't think at this point that any significant avenues for optimization are closed off in the Rust language design relative to C++. The safety checks are all designed to be optional.
Uhm, I'd say Java already is faster than C++ for all intents and purposes. If your codebase is >50KLOC and the number of developers on your team is more than, say, 4 or 5, then some crazy optimizations that are possible in C++ for small applications are out of the question, and the JIT plus the GC more than make up the remaining difference. Also, modern performance-critical software requires good concurrency, and Java has stuff like fork-join, that I'm not sure is available in standard C++ (it is, of course, available in Cilk).
So, even though it's hard to provide hard evidence (because most benchmarks are <50KLOC and written by one or two developers at most), I'd say from experience that Java already is faster than C++ in practically all circumstances (except, maybe, scientific-computation applications), not counting startup time. The only missing piece in the Java performance puzzle is value types and arrays-of-structs, and those, I believe are coming, and should make scientific-computation faster in Java as well.
50KLOC and 5 people is _tiny_. C++ developers work at projects about 100 times that size and still beat the performance of Java easily. (Linux comes to mind. Firefox, Safari, Chrome. The various JITs are smaller, but still well beyond 50KLOC. Clang. Python.)
Sorry, Java has many advantages. Speed isn't one of them.
Java has many advantages, and speed is chief among them, but memory footprint certainly isn't.
And I'm not sure how you can say that the software you mention beats Java (let alone easily). You have nothing to compare it to. Those programs' requirements preclude Java before any speed considerations are taken. If you can't spare lots of RAM, and don't have the time to wait for the JIT to profile and compile your code, Java isn't for you. But if you just want speed for an always-online application - especially if it requires serious concurrency - I'd say Java would be a better choice than C++, performance-wise, 9 times out of 10.
If I were to pick an example to try and disprove my statement, I would actually choose Oracle or MySQL. But I don't know what requirements make them a better fit for C/C++ if, in fact, they are at all.
This is part of my problem, very few who don't have to confront the challenges or disadvantages of their technology of choice seem willing to acknowledge them.
That is a dire problem in what is ostensibly an engineering discipline.
We don't get to say concrete has higher tensile strength than steel just because we think concrete is the best thing ever.
That programmers can't even agree on basic notions of what's true does a lot to discredit our field.
Linux is written in C, not C++.
I'm pretty dubious, given that Google extensively utilizes C++ in their more performance sensitive portions of their architecture. Their custom web server, for example, is written in C++. Lets not forget the rather large HPC codebases out there in Fortran.
Can you prove any of this?
I don't know why Google uses C++, but I'd guess that it's for predictability. A web server would rather always have a reasonably low latency than somewhat lower latencies in most cases with a chance for an occasional latency spike.
Also, small, well-contained C++ program would probably perform better than Java, and I don't know how big is each of Google's C++ apps.
And like I said, this is very hard to prove; it's just been my experience after replacing a couple of legacy C++ apps with Java ones. But I think it could be "theoretically proven" (if that even means anything for such an empirical claim), by analyzing why and where C++ has any performance advantages vs. where Java has them, and showing that the larger the codebase and the team, the C++ advantages must disappear, while Java's become more significant.
Just to give two examples of why that should be: in a large application, the domain model might require some class methods to be virtual. The JVM can detect at runtime that in practice only one or two implementations are present, and would inline them. This is not possible for the C++ compiler. The other example is memory management. Java's memory allocation is so much faster than malloc (it's just a pointer bump), allows for better concurrency, and automatically reduces fragmentation. The way to get good memory management in C++ is by forsaking malloc for manual management (say, object pools), but that becomes increasingly difficult to maintain in a large, ever shifting codebase with many developers involved. Also, Java has better concurrency support which is, as you've said, important for throughput.
The only counter example is arrays of structs that are simply not supported on the JVM, and where C++ would have obvious cache-miss advantages.
Quite possibly, though not necessarily better. And you still won't have the memory management advantages. But let me revise my statement: if you have unconstrained development resources, you could probably make large C++ software a little faster than a Java counterpart in many cases, but that little improvement would come at a very big development cost in almost all cases.
And just to make sure: whatever improvement you get will be very costly. We're talking manual control of memory fences, NUMA memory allocation and placement, lock biasing and more. All stuff the JVM does a very good with little effort for the Java programmer.
NOT!
It would've been more constructive for D to focus less on using GC and more on compromises and intermediate stages between fully static memory management and raw malloc/free.
Compromises like reference-counting pointers with various semantics available in Boost.
C++ didn't kill D, Java did. D was never going to replace C++.
Rust is doing a better job of making things like that available (unique pointer, borrowed pointer, etc).
That said, D didn't embrace "pay for only what you use" at a serious level because shutting off the GC basically left you in the wilderness and there weren't many accommodations made for making non-GC operation as good as possible. Rust doesn't seem to embracing that either for the sake of prioritizing safety.
Also, the GC in Rust is not for safety (as the language is memory-safe regardless of whether you use the GC); rather it's for convenience when you want it.
We can't ship a competitive touch-responsive browser engine if the compositor has GC pauses, so making the GC optional is as critical for us as it is for everybody else.
I mean, it's good that the GC is optional and I think Rust is well-designed but you get what I'm saying about "pay for only what you use". GC being optional alone doesn't constitute embracing that mindset.
It's a far-reaching sentiment that explains a lot of the stranger design decisions in C++. I'm sure Rust could end up being great for Servo, but there's more to how C and C++ get used than that.
It takes a lot of unintuitive design compromises to make a language that's just as suitable in a 4kb RAM embedded device as it is in an HPC cluster. Only a committee of people from very diverse backgrounds is generally going to be able to account for that sort of thing.
For better or worse, C and C++'s design-by-committee did result in languages that were capable of coping with a vast variety of extremes, even if it meant giving up a lot of 'niceness' and never really taking safety seriously beyond tooling/testing.
"Pay only for what you use" means safety optional, GC optional, dynamic allocation optional, everything. I know you're a very well read and highly experienced programmer and I have a lot of confidence in the Rust-dev team, but you are not replacing C++.
You're replacing C++ for Mozilla.
I don't feel that we're replacing C++ for everybody, of course. C++ is a very nice language for what it does, and there's a huge amount of infrastructure and an ecosystem built around it. I don't think that we're at an optimization/performance disadvantage relative to C++ in terms of the language design, however. (Compiler toolchain immaturity is another issue, though; currently we aren't really using LLVM to its full potential in various ways.)
C++ doesn't assume the existence of a GC, because it's designed to make manual or semi-automatic memory management as performant or accessible as possible that mode of operation is better accommodated when the time for performance arises.
D let you shut off the GC too, except now you were "on your own".
What can't be shut-off that happens at runtime?
In C++, "pay only for what you use" isn't really a compiler flag (although some things are), it's a core design principle of the language and it was designed from the ground-up for that.
Adding a few compiler flags for a mode of operation that wasn't designed for from the beginning isn't really the same thing, no?
Further, Rust doesn't accommodate as much variety or control over the concurrency semantics for the sake of promoting the use of tasks. That is unconscionably unacceptable for systems programming where you need to be able to use virtually any approach to concurrency depending on the nature of the problem being solved.
The reason why GC pointers are built into the language is that GC really requires compiler support to do well (with stack maps) and, in particular, to interoperate with the parts of the language that don't require GC in a safe way. You can't just add GC to a language that wasn't designed for it and have it work as well; the best you can do at that point is conservative GC. If you want good GC, that's a decision you need to make early on, and you need to bake it into your language to some extent. (That doesn't mean you need to rely on it though; that's exactly what Rust is trying to prove!)
There is very little that is not planned to be able to be shut off, relative to C++. One of the biggest is failure; if your functions fail, then you have to arrange to run all destructors on the stack while your thread exits. (This is similar to C++ exceptions, and are in fact implemented with C++ exceptions under the hood right now, although we plan to change this.) I suppose we could also have a mode whereby we just leak everything on failure to avoid this overhead (basically like -fno-exceptions in GCC; this is pretty much exactly the same tradeoff as C++ offers). I don't think it'd be popular though :)
What about building up arbitrary concurrency mechanisms from scratch such as is possible in C and C++? The impression I had from the IRC channel was that very little beyond tasks and a couple more lower-level tools for concurrency would be accommodated.
Other interesting parts of the runtime that are built using Rust are task spawning (lots of locks): https://github.com/mozilla/rust/blob/master/src/libcore/task...
And implementations of mutexes: https://github.com/mozilla/rust/blob/master/src/libcore/priv...
The current plan is to implement the entire runtime in Rust itself. That includes the userland scheduler. So we will need all the low-level OS primitive mutexes, condition variables, pthread infrastructure, etc. There's no reason we can foresee why this would be particularly difficult; the only reason why it wasn't done this way from the start is for bootstrapping reasons.
I'm sure the big players are in standard C++ for the long haul. Though, the thing you mention, wouldn't that make a good (though ambitious) community project? Make a hot-rod non-standard C++ implementation using Clang as a starting point.
Getting consensus on what to take out would be difficult, considering a lot of the features aren't there to incrementally improve the lives of 'everyone', but rather to cope with an edge-case that could be a fatal problem for something specific. That's how a lot of committee work goes.
I wouldn't mind seeing such a thing. I find C++ intimidating, but I don't think the answer is to completely forgo utilizing it. Realistically my solution is to only use features I understand in relation to how something similar would look in C.
Absolutely true. I don't understand people trying to use every single language feature in every single program. And I'm thinking in general terms, not just C++.
List goes on...
Concurrency primitives in C: it's a library issue. In userspace, use libatomic-ops. In kernel space, you've got atomic_t and it works well.
static_assert: the kernel has BUILD_BUG_ON. You can easily roll your own in userspace in a line or two. It should get added to C at some point, but it's not a huge issue.
type inference via "auto": C++ only needs this because it adopted the "stutter" syntax 30 years ago. "Foo foo = new Foo()" is not something you see in C.
nullptr: C++ only needed this because it had function overloading. Since NULL is actually just a macro for 0, it easily matches functions where it "shouldn't". C doesn't need this hack. Note that you still have the "pointer to bool" conversion path in C++-- have fun with that. (C doesn't have bools.)
lambdas: gcc has supported nested functions in C for a while. Not too many people use them because honestly, they are not that useful in C, as good as they may be in other languages.
regexp: there's a ton of libraries out there for doing regular expressions in C. PCRE is a well-known one. Or even just regex.h. However, if you find yourself using this a lot, it might be a warning that you're using the wrong language for the job.
parametric polymorphism: now we're getting closer to the heart of the issue, aren't we? A lot could be written about this, but let me just say this: the C++/Java type system leans heavily on inheritance, which is an antipattern. C tends to encourage you to use composition, which is the right way to do things anyway. You can create vtables of function pointers in C when you really need to, which isn't as often as a lot of Java/C++ programmers have been trained to think.
In C++: "Foo *foo = new Foo()"
In C: "Foo *foo; foo = malloc(sizeof(*foo));"
C stutters even more, unless you want to use macros to 'patch' the stutter, but that applies to both languages equally.You're right that there's no need for flamewars. I agree with most C programmers in style, as I am a C programmer at heart. I program C++ code that looks like simpler, cleaner C code.
Why does it look simpler and cleaner? Because I don't have to typedef my structs or stutter "struct" everywhere. Because I have templates to reduce that "necessary code duplication" where it matters for performance. I have constructors and destructors for the cases where I need compiler-enforced init/destroy function calls within a {} scope. I have references so I can almost entirely eliminate the worry of null pointer values in my code.
The point is that C is not the subset that most people want. Being a smaller subset of C++, it has a lot of appeal to me. But ultimately, there are genuinely useful C++ syntax features that can make your life much easier in producing reliable, clean code.
Absolutely C++ can be abused (i.e. inheritance for anything but interfaces), and absolutely C++ has some annoying quirks. That doesn't bother me too much, because at the end of the day C++ leads to cleaner code and higher productivity for me due to these relatively "syntactic sugar" features that I use.
typedef'ing structs is an anti-pattern in C. See http://www.kernel.org/doc/Documentation/CodingStyle. Basically the rationale is that the fact something is a struct is useful information which should be reflected in the type name.
So you use constructors and destructors. Good job, but how to you report errors from those constructors and destructors? Even if you're willing to use exceptions (and most experienced C++ users, like Google, Mozilla, etc are not), you can't safely throw one from a destructor, because of the risk of triggering std::terminate. So you usually end up writing your own init() and shutdown() functions for most non-trivial classes anyway. But you still have to define the useless constructor anyway, because hmm, C++ doesn't see fit to zero your class fields of POD type. Now don't try using memset-- you know better than that. Better specify what you want to initialize each one to individually in a long assignment list. And let's not forget to define a copy constructor and an assignment operator, or else you'll get screwed when someone copies your class bit-wise and does a double free.
Sound familiar? This is all adding up to a lot of boilerplate that you wouldn't have to deal with in C. Syntactic shackles is more like.
And what are those features, according to you?
Also, any C++ programmer making fun of Java should go and compare the syntax for HashMap<T> versus unordered_map(). Hash maps are probably one of the most widely used data structures and the C++11 implementation is much clunkier than Java's.
Many in the HPC community think anything that is not compiled is a joke.
As someone who works with a lot of Python/NumPy/SciPy, I can see how they'd come to that conclusion. Python's a great way to prototype, but a production runtime for high performance applications it is not.
Also, the intersection really good C++ programmers + domain specific knowledge is very small in general. I don't think that's where the world is going in general, honestly, except for some niches or areas where skilled human factor is cheap (e.g. academia).
My experience with repeatedly seeing claims like this in different contexts is that I'm not sure they've ever exceeded the performance of static binaries in pretty much anything.
They used to claim it for Lisp, then Java. I'm done waiting, I'd rather just use something I know can be made fast than hoping the PyPy guys do magic.
My point is that in certain scenarios where you need performance but aren't willing to pay the C++ grief, you can get away with better languages without significantly sacrificing.
1. My first high-level language was Common Lisp (clisp).
>My point is that in certain scenarios where you need performance but aren't willing to pay the C++ grief, you can get away with better languages without significantly sacrificing.
2. And? I expressly said that when you need to be able to squeeze every last ounce and have total control over the behavior of the program and that's when this discussion became relevant. Close only counts in hand grenades and horseshoes. I'm talking about systems work.