4 ms·
> I guess you just don't have the data. Basically every piece of performance-critical open-source infrastructure (ie. what we can actually all see and evaluate
by haberman 13y ago
> I guess you just don't have the data.
Basically every piece of performance-critical open-source infrastructure (ie. what we can actually all see and evaluate) is written in C or C++. That is empirical data.
You say that long-running servers that don't have startup cost constraints will perform better in Java, particularly when there is concurrency. Why then, is the entire web served by web servers written in C/C++ (Apache, IIS, nginx, LiteSpeed), mail servers written in C/C++ (Exim, Postfix, qmail, Sendmail, Microsoft), DNS servers written in C/C++ (BIND, PowerDNS), and database servers written in C/C++ (MySQL, PostgreSQL, Microsoft SQL Server, MongoDB, Riak)?
You attempt to explain all this away, as if OS's, VMs, web browsers, etc (which you admit are better off written in C or C++) are somehow different from the applications you work on, and that somehow the Java you write in your applications would be hard to beat in C++.
The only "data" you have offered is your own personal experience of working in the defense industry. A couple problems though. First of all you never write the "same" 2+ MLOC system twice, you'll always use the lessons gained from the first go at the second design. Something tells me you wrote the C++ version first. Your experience is also something that we can't see for ourselves, and involves lots of variables that we can't evaluate. It seems likely that your company didn't have a strong culture of best practices in C++, or maybe just weren't that good at it. Your C++ design might have had lots of opportunities for improvement that good C++ people would immediately recognize.
It's not hard to write good and high-performance C++ when you have a good set of guidelines like the Google C++ style guide (http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml http://google-styleguide.googlecode.com/svn/trunk/cppguide.x...) and a solid set of core libraries, but without these it's easier to go astray. There's no doubt that Java's reasonable set of "batteries included" libraries makes this harder to screw up.
I can say from my brief stint in defense that I was not super impressed. Everything was based around extremely heavy infrastructure like CORBA. There are lots of decisions like this that can skew the results you were getting.
- pron 13y ago> Basically every piece of performance-critical open-source infrastructure (ie. what we can actually all see and evaluate) is written in C or C++. That is empirical data. Beside the fact that this is an appeal to an irrelevant authority, it is also false. MongoDB is about as far from being performant as you can be; Riak is not written in C++, but in Erlang; PostgreSQL and MySQL predate Java (and predate Java 1.4 – when it started getting performant – by many years), as do Exim, Postfix, qmail, Sendmail and BIND (don't know about PowerDNS); Microsoft has been an enemy of Java for political reasons. On the other hand, recent Apache projects are all Java: Tomcat, ZooKeeper and the list is long. Cassandra and Voldemort are Java; Twitter Storm; most benchmarks show Java web-servers (of non-static content) to outperform or match C and C++ servers (http://www.techempower.com/benchmarks/#section=data-r6&hw=i7&test=plaintext http://www.techempower.com/benchmarks/#section=data-r6&hw=i7...). > It's not hard to write good and high-performance C++ when you have a good set of guidelines like the Google C++ style guide (http://google-styleguide.googlecode.com/svn/trunk/cppguide.x... http://google-styleguide.googlecode.com/svn/trunk/cppguide.x...) and a solid set of core libraries, but without these it's easier to go astray. This may have been true 5 or 6 years ago. But modern multicore hardware requires a high level of concurrency, and a high level of concurrency requires low contention, and low contention requires non-blocking data structures, and nonblocking data structures require either a GC, or some other method of automatic memory reclamation that's extremely hard to achieve in C++ (or any other language, for that matter). Even C++'s natural way of pointer sharing (reference counting) is a huge contention point and a performance killer. Not to mention the fact that until C++11, C++ didn't even have a memory model (which, BTW, was designed by Java people). So while it's not too hard to make single-threaded C++ code run a little faster than Java, it's extremely hard to make multithreaded C++ code outperform Java, even for experts (and it was almost impossible prior to C++11).
- haberman 13y ago> most benchmarks show Java web-servers (of non-static content) to outperform or match C and C++ servers (http://www.techempower.com/benchmarks/#section=data-r6&hw=i7... http://www.techempower.com/benchmarks/#section=data-r6&hw=i7...). Umm, there are no C or C++ servers in that benchmark, except for one extremely slow one I've never heard of. > and a high level of concurrency requires low contention, and low contention requires non-blocking data structures That really does not follow. Lock-free data structures are not about reducing contention, they are about eliminating blocking and improving performance when contention does occur. Contention occurs whenever multiple threads attempt to access the same resource, regardless of whether access to that resource is blocking or non-blocking. Lock-free data structures can still experience contention, and while they do not block, their performance does degrade under contention. Indeed later in your message you say that reference counting is a "huge contention point" -- refcounting a la C++'s shared_ptr is a lock-free algorithm. So while lock-free algorithms don't affect the amount of contention in the system, they do exhibit better performance under high contention, for the (small) subset of data structures that can be made lock-free. But this is only an issue when contention exists to begin with. Many highly parallel systems exhibit very low contention; for example, MapReduce. Even in systems with inherent contention, there are many strategies for reducing it, like partitioning and affinity schemes. > nonblocking data structures require either a GC, or some other method of automatic memory reclamation that's extremely hard to achieve in C++ (or any other language, for that matter) I'm not sure what is so hard about Hazard Pointers (http://www.research.ibm.com/people/m/michael/ieeetpds-2004.pdf http://www.research.ibm.com/people/m/michael/ieeetpds-2004.p...). Maged Michael is a smart guy who figured this stuff out 10 years ago. But you don't even have to do it yourself. Non-blocking algorithms are extremely subtle by their nature (I hope you don't implement them yourself in Java; that would be a waste of effort). Just use an existing library like Thread Building Blocks, Grand Central Dispatch, etc. that are specifically designed to achieve maximum multi-threaded performance in C++.
- pron 13y agoContention is not binary, or, rather, its effects aren't binary. A lock that is held for 10 microseconds potentially causes a lot more contention than a CAS that takes a few nanos. OTOH, thousands of short contentions are as bad as, or worse, than few long contentions (there's a whole great talk by Doug Lea about how Java's fork/join tricks modern CPU out of crazy power-saving optimizations to reduce thread wakeup time). Intel's TBB were modeled after java.util.concurrent, BTW. Later, the Java memory model inspired the C++11 memory model (and the Java people were called to help with that, too). When it comes to concurrency, Java is usually at least 5 years ahead of C++ (perhaps with the exception of fork/join, which was inspired by Cilk, but has since come to surpass it). > I'm not sure what is so hard about Hazard Pointers Hazard pointers are expensive without RCU (the hazards need to be visible to other threads, which means store-load barriers), and RCU requires kernel cooperation, and besides this entire class of techniques is either very limited or incredibly complicated (what do you do if you have thousands of threads?). A good GC vs hazard pointers with or without RCU is like driving down a highway vs stumbling in a swamp when it comes to lock-free data structures. Here's Herb Sutter on the issue (source: http://herbsutter.com/2011/10/25/garbage-collection-synopsis-and-c/ http://herbsutter.com/2011/10/25/garbage-collection-synopsis...): All existing C++ garbage collectors, notably the Boehm collector, aren’t enough for many interesting uses for several reasons, the main one being that they’re conservative, not accurate. See Hans Boehm’s page Advantages and Disadvantages of Conservative Garbage Collection for more discussion of tradeoffs. As usual, there’s something to standardize iff we want to enable portability. So in this case the question is whether it’s interesting to enable portable programs that rely on GC — and note that typically if they rely on GC, they rely specifically on accurate GC, not conservative “maybe-GC.” For example, C++11 atomics enable writing many kinds of portable lock-free code, but not all; we need a standard for #2 if we want to enable people to write portable lock-free singly-linked lists, where the only known practical algorithms require automatic (non-refcount) GC and so they cannot be implemented in portable C++ today without some nonportable platform-specific code underneath. Again, appeals to hazard pointers fail because the obscure convolutions and limitations of hazard pointers only expose the limits of even the most heroic efforts to work around the lack of true automatic GC. The main contribution of hazard pointers, unfortunately, is that they thoroughly motivate why direct support for automatic GC is needed.