Overview
Features
Download
Documentation
Community
Add-Ons & Services
The POCO C++ Libraries Blog

Does C++ Suck?

Filed under: Uncategorized by alex at 18:57

Here’s a blog on how C++ sucks and committee members are an ignorant bunch having little or no clue what they talk about when it comes to threading. I find it interesting to see over and over how people sharply and firmly focus on a single aspect of [insert your favorite issue here, from Iraq war to garbage-collection] and elaborate it to a great extent to prove a point without even bothering to look at the big picture, historical context and value of optimal solution morphed by all the forces acting upon and constraining the issue at hand. Often they have a point in the narrow sense, but they also blatantly disregard any other concern. I guess the world should reboot now, deleting all the code written so far and starting it over in a language so pure and perfect that we’d think we’re in heaven. Before we embark on that journey, someone points us to No Silver Bullet, please. Mixing that kind of “analysis” with arrogance is also often a case. For the intrigued, comp.lang.c++.moderated has discussed the issue, too. I haven’t learned much from that discusion thread.
But wait, here’s the solution to the problem, blogs another expert. It’s called “the Erlang way”. Without pretending to be an expert on the issue, let me take a minute here to share what I have (prompted by the mentioned blogs) recently learned about the Erlang way of concurrency. It’s based on the Actor model. Erlang processes (not to be confused with OS processes) are single threads (not to be confused with OS threads) of execution that share nothing, which makes them safe in a concurrent scenario. They are not operating system processes and are very lightweight, hence cheap to spawn, making Erlang applications readily concurrent in an agile way and very scalable. The Erlang poster-boy, Yaws web server beats Apache hands down in both performance and scalability. However, the important point is that the problem is not in Apache or C. The culprit is actually the operating system and the way it implements concurrency. C (or C++) simply uses the OS API to achieve concurrency. And I’m not sure whether there’s any room for Lin-Win fight in that arena – they seem to be about equally bad, give or take. But hey, worry you not, prudent software professional, that too is about to be resolved by the upcoming world reboot alongside with the operating system rewrite in The Perfect One-and-Only Language. I could actually make a good use of a sabatical decade, and this seems like a perfect opportunity.

Joking aside, who wouldn’t like to see a good solution for concurrency – it’s like an 18-wheeler coming down the road at us. However, there are fundamental reasons preventing true Erlang-like functionality in C++. A significant aspect of Erlang processes is their lightweighted-ness, which comes from the fact that they are spawned with minimal stack size which can later be resized if needed, which, as it turns out, is impossible to achieve without keeping track of the pointers to allocated memory. In plain words – you need garbage collection. For the curious reader, it is explained well in this post. Clearly, nothing comes free, so Erlang pays for this in performance currency – it is slower than C++ when it comes to computing efficiency. So, unless someone can convince the C++ standards committee to sacrifice performance and turn C++ into garbage-collected language running on a VM and have its own (OS independent) threading implementation, the whole discussion is a waste of time. I’ve been thinking long and hard of a “microkernel-like” design for my servlet container – I’d love to load a shared library (or some sort of a lightweight “process”) really quickly and not share my memory with it (i.e. not be vulnerable to anything bad going inside the library). Any way one looks at it – you can’t have it all. There are always trade-offs and that’s the bottom line.

So, back to the original question – does C++ suck? If one looks at some aspects of the language in isolation, a compelling “C++ sucks” case can easily be built. However, when one reads through Design and Evolution of C++, many a reason for language features becomes clear and obvious. Mistakes have been (vector<bool> :-\ anyone?) and likely will be made. However, on average, I’d say that C++ crowd comes across as a pretty sharp bunch. I’ve personally heard Stroustrup say that running multiple threads in shared memory space is simply not a good thing to do. Imagine that, bloggers – even he is aware of it. Unfortunately, we live in an imperfect world and write our imperfect applications in imperfect languages to run them on imperfect machines being controlled by imperfect operating systems. Theo de Raadt puts it well in one of his interviews: “everything we build is turds, we just move them around or shine them or have a different view on which way they should be rolled”.

Concurrency shall be addressed in the upcoming standard. There are some interesting proposals emerging that could make concurrency easier to digest in C++, such as lock-free techniques as presented by A. Alexandrescu and B. Stroustrup. It is important to stay alert, think before you code and expect no miracles. Short of some lunatic pressing the nuke button, the world is not rebooting any time soon. So, looking at it from a broad perspective, C++ does not suck. It is the most succesful general-purpose computer programing language in history. And, for better or worse, warts and all, it still rocks.

18 Comments »
  1. ‘unless someone can convince the C++ standards committee to sacrifice performance and turn C++ into garbage-collected language running on a VM and have its own (OS independent) threading implementation’

    Looking at http://herbsutter.spaces.live.com/blog/cns!2D4327CC297151BB!159.entry, the garbage collected part of that is possible/likely for C++0x…

    That page also talks about C++ concurrency libraries, but I don’t think that’ll end up being an independent threading solution.

    The other main difference between Erlang and C++ that makes concurrency easier with Erlang is the lack of shared state (except for the Erlang database, Mnesia). If you were to say (when using C++) ‘right, all inter-thread messages will carry copies of data with them rather than data references and will return copies of that data’, then the need for locks is reduced massively – at the expense of copying all that data (and incurring lock penalties in the heap manager, I’m sure….).

    Oh – and I totally agree with your last few sentences :-)

    Comment by Stuart Dootson on March 1, 2007, 11:02

  2. When you say that “the culprit is actually the operating system and the way it implements concurrency. C (or C++) simply uses the OS API to achieve concurrency”, you’re only talking about performance. Sure, Erlang has a major advantage over C++ in concurrency performance, but its real gains are its shared-nothing actor model. It’s orders of magnitudes more difficult to run into deadlock problems with message-passing concurrency than it is with traditional shared-state concurrency. And with respect to performance, look to ejabberd, not yaws, as the shining example: it currently handles 600,000 Jabber simultaneous connections on a single machine. Not bad, huh?

    Erlang’s other big advantage is the OTP (Open Telecoms Platform) libraries that are used to build massive applications on it such as ejabberd. That’s the real key to writing stupendously robust, scalable applications, and that’s something you don’t realise unless you’ve used Erlang for quite a while building mid-sized applications.

    As for computing efficiency, Erlang also has a native-code compiler named HiPE that you can use for performance-critical code, and it’s pretty easy to call out to C when needed via a feature named ports.

    Haskell is probably the best bet to combine Erlang-style lightweight concurrency with C++-like performance. It already has Erlang’s lightweight threads, and you can coax performance as good as C++ if you know what you’re doing with it. It’ll be something to watch in 5 years.

    In the meantime, check out http://www.algorithm.com.au/talks/concurrency-erlang/ for some resources on how do make concurrency in C++ a bit easier, including some libraries that do lightweight (userspace) threading and give you Erlang-style message-passing.

    (This post is by someone who’s lucky enough to use both Erlang and C++ every day professionally — and loves both languages. No Haskell in my dayjob yet, however…)

    Comment by Andre Pang on March 1, 2007, 15:32

  3. Oh, and I should say thank you very much for POCO: I use some of your classes daily, and boy it makes life nicer! :)

    Comment by Andre Pang on March 1, 2007, 15:33

  4. One can decide to turn C++ into Erlang by creating a framework that will mandate GC, share-nothing (i.e. copy everyting) and run on a VM having its own machine independent threading/process model. Whether something like that should become standard is another issue. I’m not really familiar with it, but I think Java green threads attempt to do something like that but have severe limitations, such as running on only one CPU and having to sprinkle your code with yields (“cooperative threading”).
    There is a 1999 Red Herring article by Stroustrup. The title says it al: ‘Why no single programming language can solve every need”. Quote:
    “we should resist the temptation to solve all programming problems once and for all by standardizing a single solution”.
    Thanks for your comment.

    Comment by alex on March 1, 2007, 15:37

  5. Andre,

    Looking into Erlang recently has definitely been a very educational experience for me. However, back to my original motivation for the post, with all the excellent Erlang concurrency features, I still do not see how those features in their entirety could be justified for C++ standardization. Erlang is just another language that does certain things very well.
    Considering your favorable comment about Poco, would you mind posting it in forums under POCO Praise and Success Stories which is sortof “official place for patting hard working Poco folks on the shoulder ;-)

    Thanks for your comments.

    Comment by alex on March 1, 2007, 15:51

  6. I agree that Erlang’s features — and thus the requirements of the language runtime that they’re based on — would not make it into C++ at all, nor would I want them to. I’m very happy building my own message-passing layer on top of C++, and it’s kinda nice that C++ is powerful enough that I can do this, even if it is more difficult than I’d like.

    That said, the original blog article you referred to has a very valid point: threading in C++ is hard. The real problem is that shared state concurrency is hard, whether it’s in C, C++, Java (which has a lot more threading primitives than many other languages), Python, or whatever. It is just a very wrong programming abstraction to use, and 20 years of it being the mainstream software engineering practice has shown that almost every single programmer who’s had to do heavily multithreaded code simply hates it. Can you even imagine trying to write something as complex as a game engine, where you have 1000+ objects all updating, with each object depending on other objects? And then trying to make that scale and perform well and scale to 8 processors? 32? 512?

    Read the article again and perhaps you’ll see it in a different perspective now. It’s not attacking C++, it wants to see C++ not take the same route that Java did and advocate using shared-state concurrency as the mainstream concurrency technique. I’ll be _so_ happy the day that there’s a Boost library to able to do mesage-passing between threads: this idea really isn’t much more than a thread-safe boost::signals sending signals to a thread’s runloop. We need to start advocating a better programming model than using mutexes and locks, and message-passing is an excellent solution. Message passing might not be the be-all-and-end-all, but just about anything is better than mutex hell! Erlang is just not about another language that does certain things very well; it’s proof that having a better programming model and abstraction can make the very hardest problems in C++ almost trivial. C++ would do well to promote an Actor model.

    For more background reading, see Tim Sweeney’s invited talk at PoPL about “The Next Mainstream Programming Language” http://lambda-the-ultimate.org/node/1277, and Edward A. Lee’s paper “The Problem with Threads” http://www.algorithm.com.au/blog/files/the-problem-with-threads.html

    P.S. I find Stroustrup’s quote about no single programming language solving every need a little ironic, considering that C++ is arguably the best example of a kitchen-sink language ;-).

    Comment by Andre Pang on March 2, 2007, 02:23

  7. Oh, and I haven’t actually shipped anything with POCO (yet!), which is the only reason I haven’t added it to the POCO success stories page. Rest assured I will the day it gets used in an actual released version of our products!

    Comment by Andre Pang on March 2, 2007, 02:25

  8. Andre,

    I don’t think I’ve read it wrong. Here’s from his other blog:
    “why the heck do we need C++? What does C++ give us that good ol’ fasioned C doesn’t? Objects? C has structures of function pointers. … Templates? Macros. Exceptions? Setjmp/longjmp. Etc.”
    Doesn’t sound exactly like a C++ fan.
    Anyway, I understand well that there are many problems with C++ and threading. I also understand that shared state and locks are messy and Erlang does well in that area due to no-sharing philosophy.
    But there are trade-offs always and in everything. Looking at things from a single point of view does not do justice. Neither does criticizing without proposing viable alternatives. Criticizing is easy, but what I’d like to see is someone actually make a concrete proposal that is viable. Instead, Brian knows in advance that his “help would not be appreciated”. Furthermore, he also thinks that C++ is “not fixable as a language”. Which is fine, he is, like the rest of us, entitled to his opinion.
    From what I read, Erlang does its “magic” via VM, having its own lightweight process mechanism. And the whole thing is done in a clever way, so it is capable of taking advantage from multiple CPUs. I’m not sure what happens if one uses HiPE. Could you clarify that? And, I’m still not clear how could that be done in C++ on the language level without using OS threads. I’d like to see it, but nobody was able to explain how exactly it could/should be done. If it’s just about message passing and no sharing, anyone is free to decide to do that and pay any associated performance penalties.
    At any rate, this is a very interesting discussion. I’ve learned tons about Erlang and threading ;-).

    Comment by alex on March 2, 2007, 04:36

  9. I just wanted to note that with POCO you’ll also get many high-level multi-threading abstractions. For example, with the NotificationQueue class it’s easy to build multi-threaded applications that use message passing – no need to share data (or use mutexes) here. ActiveMethod is another abstraction that also allows you to work without shared state.

    The nice thing (or problem – depends on your viewpoint) about C++ is that C++ does not force upon you a certain design. All the power to the programmer. With great power comes great responsibility, though…

    Comment by guenter on March 2, 2007, 10:48

  10. > All the power to the programmer.
    The power comes from freedom. We are undoubtedly all entitled to freedom as human beings so we should be granted the same as programmers.

    > I find Stroustrup’s quote about no single programming language solving every need a little ironic, considering that C++ is arguably the best example of a kitchen-sink language.

    I don’t find it ironic at all. I don’t think “no single programming languge can solve every need” means a general-purpose programming language should not attempt to solve as many needs as possible.

    Comment by alex on March 7, 2007, 18:10

  11. It’s not possible to implement Erlang-style concurrency in C++. Erlang is a safe language: i.e. if an Erlang process “crashes”, the runtime will detect this and throw an exception, and other Erlang processes will continue to run as normal. If you were to use a userspace threading library in C++, or even use OS threads, you don’t have this guarantee; you’d have to resort to OS processes to get proper isolation. Erlang’s HiPE system preserves the same safety semantics that its normal bytecode compiler uses, so there’s no problem there.

    _If_ OS processes were really cheap, using a separate OS process would be no problem. However, OS processes aren’t cheap precisely because it must protect itself against undefined semantics in C/C++ programs; you have to protect against a NULL pointer dereference somehow.

    It is possible to safety-check every single operation in C/C++ to give it the same safety properties as Erlang (via an alternative compiler+runtime system), similar to C interpreters like eic. That will give you Erlang’s safety semantics. Add a userspace threading library in for ultra-cheap thread scheduling, and then you’d only have one problem left, which is dealing with I/O calls that block the process :). Erlang gets around this by using a dedicated OS threads to service only I/O requests, and funnels all I/O requests to those threads.

    Sure, Erlang is not a general-purpose programming language like C++. The problem is that as concurrency becomes a more important topic with the multi-core future looming, getting concurrency right in C++ will become a very important topic, and is a hard thing to get right — Erlang does it get right. We need C++ programmers to be more educated and move toward a better programming model, rather than sticking with locks and semaphores and condition variables, which are miserable building blocks for building complex concurrent systems.

    Comment by Andre Pang on March 13, 2007, 04:01

  12. > It’s not possible to implement Erlang-style concurrency in C++.

    I agree. This was exactly my original reason for the post.

    > It is possible to safety-check every single operation in C/C++ to give it the same safety properties as Erlang

    I agree. This is not likely to make it into C++ standard, though.

    > We need C++ programmers to be more educated …

    Once again, I wholeheartedly agree. There is also a need for education about void* and macro perils. However, the C++ approach is usually to advise about these kind of things rather than impose constraints and/or break existing code.

    BTW, concerning HiPE, my qustion was related to lightweight feature of Erlang process, (i.e. resizable stack, if I understand it properly) – is that aspect preserved with HiPE?

    Comment by alex on March 13, 2007, 18:51

  13. HiPE just preserves the semantics required by the Erlang virtual machine when it compiles Erlang to native code. You can turn HiPE on/off at a per-module level (either by a compiler switch or a pragma in the source code), so it’s no problem to mix and match HiPE code with normal Erlang bytecode. One reason you might do this is the HiPE compiler takes a _lot_ longer than the bytecode compiler, and you don’t usually see very much performance gain on typical Erlang applications since most of them are I/O-bound (network or disk). If you have a couple of CPU-intensive Erlang modules, you might just try compiling just those specific modules with HiPE before resorting to calling out to C: you can leave the other Erlang modules as normal bytecode files.

    Theoretically, a buggy HiPE compiler could take down the entire VM (by attempting to read memory at 0×0 or whatever); the VM has no proof that the compilation is correct. However, there really aren’t many provable compilers around either ;-). You’re more-or-less placing the same trust in the HiPE compiler that you are with any usual native-code compiler. Hopefully that answers your question.

    One other cool feature I thought I’d throw in is that the Erlang compiler+runtime also supports encryption of bytecode-compiled modules. I don’t know the specifics about this, but if you’re worried about other people reverse-engineering your bytecode, encryption sounds like it’d be a great solution.

    Comment by Andre Pang on March 14, 2007, 04:02

  14. BTW, I just found this little blog post comparing HiPE performance vs Python/C performance:

    http://bob.pythonmac.org/archives/2006/09/21/erlang-binary-performance/

    I haven’t toyed that much with HiPE myself, but I’ll be impressed if that result’s typical of what can be achieved with it. I’m sure Googling around with give you much more data to judge HiPE’s speed!

    Comment by Andre Pang on March 14, 2007, 04:05

  15. If that benchmark is valid, then Python is to blame for it. As this benchmark shows, HiPE outperforms C (or C++ for that matter) only in concurrency.

    Comment by alex on March 14, 2007, 12:55

  16. I didn’t mean to imply that HiPE was faster than C/C++ — as you well know, very few things are faster than C++ for anything. Again, HiPE is an excellent solution for those selected bits of code that are performance-critical: just recompile the offending modules with HiPE on and try it again. If that doesn’t cut it, fall back to C/C+ in those crucial parts and let Erlang handle the concurrency.

    I also emphasise again that most Erlang programs are not CPU-bound: jabber.org runs ejabberd and maintains 10000+ simultaneous connections ever day, with multiple Erlang processes per connection, and its load average sits under 1.0 for most of the day (http://status.jabber.org/). If you’re writing highly scalable network servers — which is Erlang’s intended problem domain — CPU is usually not the issue.

    Comment by Andre Pang on March 15, 2007, 15:41

  17. This whole thing has intrigued me, so I’ve been playing with those benchmarks. It seems that coroutine-type concurrency is much less CPU intensive than pthreads. Which makes me wonder whether it is worth considering as “lightweight threading” implementation (win32? fibers? or something like this attempt to implement *context() calls). One would certainly have to take into account the fact that *context() calls are not present on all systems and, additionally, have been obsoleted in favor of pthreads. Also, the presence of much dreaded variadic function does not help. However, the performance gain looks appealing. The whole thing is well covered (at least for POSIX platforms) in this paper. And things are better now with availability of *context calls than was the case when the mentioned paper was published. GNU pth described in the paper includes additional (setjmp/longjmp-based) solution making it fully portable across POSIX systems.
    Something to think about.

    Comment by alex on March 16, 2007, 22:16

  18. Coroutines are a great basis for lightweight threads, although most of the coroutine libraries I’ve seen are designed to hop specifically between only two points of control, as opposed to scheduling multiple execution contexts. If you visit my Erlang talk webpage at my website, there’s a couple of different implementations of userspace threads there, all with different ups and downs.

    Comment by Andre Pang on March 19, 2007, 17:00

RSS RSS feed for comments on this post. TrackBack URI

Leave a comment