Summary:
The C++ standardization committee is hard at work standardizing threads for the next version of C++. Some members recently met to discuss the issues, and The C++ Source was there. Read on to learn what the world’s leading experts on concurrency are planning for C++0x.
The ability to add new comments in this discussion is temporarily disabled.
Most recent reply: July 4, 2017 6:05 AM by
|
> You claimed that Java is no longer slow and then you were > provided with contemporary examples of slow Java > applications which you then dismissed.
I have no problems with any of these applications on my computer. What kind of computer with what kind of graphics card do you run your Java applications with?
> So give us some examples of contemporary java apps that > perform as well or better than C++ apps.
It's not just about performant apps. It's about programming productivity and application stability. I have very performant Java applications that bang away 24/7.
The applications that were pointed out, all have something in common. They are desktop/GUI apps. The Swing implementation, currently, utilizes basic drawing primatives to render all components. This means that its rendering is more like a 2D/3D game's requirements than a "windows desktop app". So, it's no wonder that people without powerful graphics cards have poor results with Swing based applications. Eclipse/SWT can do better than Swing because it uses native drawing. But, in JDK-6, support for VISTA and Sun's realization that their rendering pipeline was not correct/optimal for windows, means that things are going to change, and I believe, for the better.
Unfortunately, the masses are still all keyed in on CPU performance and don't understand the time they are wasting on writing complex code, debugging memory issues, and porting their software to a new OS/CPU combination.
The design of Java is what enables it to be portable. The JDK1.5 memory model changes are what finally made it possible to write dependable multi-thread code. My largest applications which run with 100's of threads, will run equally well on Windows, Linux and MAC OS-X, without a single bit of porting work on the software base.
The work on putting threads into C++ and the lack of GC are revealing the deficiencies it really has in its overall design. Applications with huge amounts of complex data structures are very difficult to get right. C++ makes it even harder, because you have to deal with memory management more explicitly either through finding the right library, or creating the memory model you need for yourself.
C++ was never designed for a multi-threaded world, and it never will be able to really get there, with any dependable portability and operation, without a memory-model and then the standardized threading support that uses that memory-model. That's the effort that's underway now, and this report shows the issues that are going to have to be dealt with both technically, practically and religously.
There are vendors today, providing OS based solutions, where they have control/advanced knowledge of the issues to deal with, including inparting a memory model in their compilers that makes it possible for them to support threading in some form.
Thread designs that I saw in this report, based on library and templates, are very risky, because they utilize one of the more complex parts of the language specification.
I'm anxious to see how things evolve toward a workable standard. But, in the end, I'm not at all sure it will happen without a "virtual machine" designed into the mix that provides the right level of abstraction that vendors can plug into.
|
|
|
> It's not just about performant apps. It's about > programming productivity and application stability. I > have very performant Java applications that bang away > 24/7. >
I'm not sure why my post is quoted at the top of yours since what you said has little to do with what I said.
|
|
|
> The fundamental problem with Java is that it consumes "too > much" memory, and that inevitably slows down the system.
Another problem is that C++ has a memory model which allows faster implementations; stack-based allocation can really speed up programs; and it is not the same as gc linear allocation, because the gc has to be locked for multiple thread access.
The difference in performance grows exponentially with the size of the data set; handling a big Word document with Java would be quite slower than with C++.
|
|
|
> Joking aside, the fact is that a majority of developers > don't want to use GC with C++. How many real life projects > are there where C++ is used with GC?
You are mistaking the effect for the cause. Real-life projects don't use GC with C++, because C++ does not have GC, libraries don't support GC, the STL does not support GC.
I forgot to add to the reply to your previous statement that Eclipse, NetBeans etc are considerably slower than VS8, and VS8 is considerably slower than VS6...and this is because Java apps and VS8 are .NET applications, whereas VS6 is purely C++.
|
|
|
> The difference in performance grows exponentially with the > size of the data set; handling a big Word document with > Java would be quite slower than with C++. I can't imagine anyone using a garbage collector with cpu cost exponential in the size of the data. As for large documents, I wrote a little demonstration which was able to handle 100MB documents with ease. It is just a matter of using a suitable implementation for the Document. I routinely have applications with 300MB heaps.
|
|
|
> > It's not just about performant apps. It's about > > programming productivity and application stability. I > > have very performant Java applications that bang away > > 24/7. > > > > I'm not sure why my post is quoted at the top of yours > since what you said has little to do with what I said.
You asked about performance and Mark's response to your questions. Without trying to answer for Mark, I was trying to find out more about your experience and perspective. I enumerated the issues and perspective that I'm familiar/experienced with.
Java provides a platform that simplifies software development. In doing that, it generalizes certain things in that platform to make them more portable or more predictable. The result is that some things take more instructions to execute than if you coded them in assembly language. The difference is that you are trading some degree of performance for portability and speed of development.
As James Gosling said, he enjoys write software in Java because it's possible to write correct software in Java, must faster and get better quality software, if you use the platform in the intended way.
I'd still like to know what Java software you have practicle experience with in development and operations so that I can get a better understanding of your experiences and perspective. It's hard to really understand those things from your posts so far.
I'd like to really discuss the issues and not throw around pointless arguments.
|
|
|
> You claimed that Java is no longer slow and then you were > provided with contemporary examples of slow Java > applications which you then dismissed. > > So give us some examples of contemporary java apps that > perform as well or better than C++ apps.
Directly comparable applications written in two different languages are rather rare. For a fair comparison they also need to have been written at about the same time (otherwise the more recent app is likely to have been infected with the prevailing "memory is free" mentality).
My own work is in vehicle routing --- optimising the routes and loading of vehicles. Computational performance is important here and yet I'm not bothered by doing it in Java. The previous major version of our system was written in C++ and we still have some C++ code in the current system (for historical not performance reasons). We are working to eliminate all of the remaining C++.
At various stages during the development I have written benchmarks reflecting the type of computation we do and compare results in both Java and C++. Performance differences were in every case modest. I don't bother with testing against C++ anymore; I am confident I can achieve the required results and performance at least for the kind of work that I do.
One part of the code is responsible for computing shortest paths between locations. On a network containing around 3 million arcs the Java version is around 10 times FASTER than the previous C++ code and is more flexible as well. Of course this difference is largely a result of an improved algorithm. The choice or development of algorithms are much more important than the choice of language. In this case the C++ code was a good implementation of an established algorithm, the Java code uses a completely new approach.
|
|
|
> You asked about performance and Mark's response to your > questions.
Actually, I never asked about Java performance, that was somebody else. I just observed that Mark's response when presented with examples of well-known contemporary Java applications that ran slower than comparable apps written in C/C++ seemed to me to be evasive.
I don't deny that Java can sometimes allow faster development than C++, but that has nothing to do with run-time performance.
Of course, we're all a bit-off topic anyway since the story was about standardizing threads in C++, not whether C++ is better than Java.
|
|
|
> Nobody is trying to "turn C++ into Java." The committee is > interested in standardizing existing practice. We should > standardize threads for the simple reason that lots of > people are writing multi-threaded C++ programs today. To > not put threads in the standard would be to ingore the > needs of real C++ programmers. I picked this message to reply to more or less randomly. I just wanted to thank Eric on a most useful piece of reporting. All too often people ignore the standards process, only to loudly complain when the result doesn't meet their needs - or they think the standard doesn't meet their needs. The committee consists of volunteers - often very dedicated and hard-working volunteers, who devote significant time to explain what's going on and to make information available (Can you search for "WG21" on the web? If you do, you'll find all the proposals - more than you even wanted to know). The C++0x effort are trying to address what many experienced people see as the key problems for the C++ community. It is trying to address them responsibly, with very few resources (i.e. people, money, and time), and in a timely manner. Doing all three is *hard*. See my papers on the direction of C++ (on my publications page: http://www.research.att.com/~bs/papers.html). It is now pretty certain that C++0x will (1) address the serious issues about machine model raised by modern hardware - the standard will address these issues exactly so that "the average programmer" will not have to understand these issues - the language will (at the user level) present a reasonable model of the machine. (2) provide a reasonably simple, portable, and conventional model of threading. It may provide "futures" as a mechanism to save many users from explicit locking. I wish it would also provide some form of message queues, but that is almost certainly not going to happen in this time frame. The committee uses Technical Reports (TRs) to address issues beyong the C++0x timeframe. Threading is a pretty awful way of dealing with concurrency, but it is so widely used that C++0x must address the issue. Purely library-based approaches can't handle real concurrency (e.g., as presented by multi-cores), so there will be some code language support (e.g. for thread local storage and initialization). (3) Programmer-controlled garbage collection, as first suggested by me in 1994 or so, primarily based on the work on Hans Boehm (available open source and and used industrially with C++ for about 15 years now). If you don't like it, you turn it off (actually, the default is "off", so it's more correct to say "if you want it, you turn it on"). (4) Lots more. More than I can mention here - again see my publications page, "WG21", and my C++ page. -- Bjarne Stroustrup: http://www.research.att.com/~bsPS. Try to read Eric's article (again) and other articles. See what's actually written and proposed, rather than just imagining. And please do remember that you couldn't possible get all you could wish for.
|
|
|
... > And please do remember that you couldn't > possible get all you could wish for.
That is one of the big advantages of C++ -- if the standard libs don't do what you want, the low- level capabilities are there to implement precisely (and often portably and efficiently) what you do want. So, for me, the key questions about threading/concurrency-enabled memory models are low-level, like "can I implement Lamport's Bakery Algorithm portably for multiple cores?".
|
|
|
> The logical place to signal that something went > wrong is when joining the terminated thread.
I'm not at all sure that I agree with that.
It seems to me that the best place to deal with an exception would be within the thread itself (within function_that_throws() for example). I think it would be better to prohibit thread entry functions from throwing altogether (as Java does), or else just terminate the entire application if an uncaught exception escapes the top stack frame of a given thread.
Passing the Exception back through join() seems needlessly complicated, and seems to needlessly complicate programs which use threads.
What if join() is called after the thread terminates? This implies that we need the ability to "save" the exception until join() is called. What if join() is never called? Would the saved exception just be quietly lost? The Thread which threw the exception obviously didn't deal with it, so this implies we will likely leak some memory (at a minimum, the Thread object itself, in which our saved Exception is presumably stored). I think I would prefer to know what of my Threads had just met an untimely demise than to have it quietly depart, without the necessity for me to figure out a way to call "join()" on each and every Thread I create.
I suspect with such a scheme, common practice would be to do something such as:
try { // blah blah } catch (...) { cerr << "Uncaught exception" << endl; exit(1); }
in every single thread, which obviates the need for this complex exception passing scheme.
Alternatively, you would have to write some sort of "thread manager", which kept references to all the threads in your system, continuously calling "isRunning()" to monitor each of them, and then calling "join()" to reap exceptions and free the memory associated with each thread. This hardly sounds like something I want to write in every program I ever write.
Actually, perhaps the best solution would be to let the user decide what is to be done. Again, to borrow slightly from Java; Associate with each thread an "ExceptionHandler" method or class, which specifies the behavior the Thread should exhibit, with a default which does something pleasantly terminal, such as killing the entire application, or which prints an error to stderr and then kills the Thread.
|
|
|
My concern about the memory model proposal describe in the article is that instances need to have a special type in order to be sharable between threads. The sharing of volatile instances between threads (when done correctly) is highly portable to many C++ implementations already. But it isn't practical (for many reasons) to have a volatile STL map shared by multiple threads (protected by a mutex). It seems to me that the discussed memory model would have this issue as well.
This is why I think sharing mechanisms based primarily on special operations rather than special types is a better direction. My hope is that this would make sharable STL containers easy: member function templates to do "first_read" and "last_write" could be added to the Allocator interface. They would be noops for the default allocator. For the "sharing-enabled allocator" they would (respectively) force a non-reordable read from/write to common memory, using the standard-defined special operations.
|
|
|
> But almost all C++ versions in almost all O/Ses provide > threads, critical sections, mutexes and semaphores. > Therefore I do not see where is the problem for the 99% of > the cases.
Most if not all C++ compilers are unable to do optimizations that go beyond the scope of a single source file. This means the compiler has to generate code to write out all (changed) register-mapped variables before calling an external function, and the compiler has to re-load from memory all variables after calling an external function. It's also generally true that external functions are called to take and realease whatever type of mutex is protecting access to shared data structures. I've been told that, on x86 processors this combination is enough to be safe. This is because the actual order of loads and stores is always the same as the order in which the load/store instructions were executed.
But on other processors (MIPS, PowerPC, SPARC I think) loads and stores may not happen in the order of execution (as long as accesses of the same location are not reordered). Special instructions need to be added to prevent load/store reordering. Of course, this is only necessary when the threads are running on different CPU cores.
I think the urgency with the new memory model is the possibility that multi-core processors may become the norm way before C++1x. And I've already seen one linker that appeared to do some tweaking of the object code.
|
|
|
> > Joking aside, the fact is that a majority of developers > > don't want to use GC with C++.
Perhaps not. I guess it all depends on what you do. > > How many real life > projects > > are there where C++ is used with GC?
There are quite a few, I believe.
> > You are mistaking the effect for the cause. Real-life > projects don't use GC with C++, because C++ does not have > GC, libraries don't support GC, the STL does not support > GC.
This is factually wrong. Nothing prevents you to rely on garbage-collection with C++. It requires that you observe a simple rule - namely to not "hide" any pointers (by e.g. writing them to disk). Although nothing in the C++ documentation prohibits this behaviour, I have not seen any standard library that performs such foul tricks so you can use that one with no problems at all.
/Peter
|
|
|
> > This goes beyond a particular OS's implementation of > > threading, and right down to the hardware. > > Perhaps I missing something, but wouldn't implementing > threading at the hardware level rather than through the OS > have the potential to bring the OS crashing down? How can > two entities be manipulating the same hardware registers > to different ends?
I think that a virtual abstraction of threading with no particular hardware or OS as its target provides the best opportunity for portability. The more features you demand at the virtual layer, the harder it is to port and the more dependent on certain behaviors an application can become.
This is where languages like java run circles around native languages in portability. As soon as you add threading though, you have to have a memory model that dictates exactly how concurrency is mediated by the language. Otherwise portability goes right out the window and your back to defining 30 macros that represent the hardware requirements for cache synchronization, spin locks, OS locks etc. And suddenly things get ugly.
|
|