Posted Mar 29, 2013 16:12 UTC (Fri) by simlo (subscriber, #10866)
Parent article: A look at C++14, part 1
As a C++ developer I am not too happy hearing about extensions. I like having priority queues or heaps. I had to implement my own. I also like that std::function is part of the standeard now as well as lambda expressions and variadic templates.
But i dislike the all the talk about multithreading. I know it is needed to make programs scale, but 95% of the programs do not need that. The only reason people use threads are due to blocking APIs! To make threads work correctly more low level APIs are often needed.
Instead I do it the UNIX way: split the problem in processes using pipes/sockets, each being single threaded. That way I do not have to worry about locks, and it is far easier to make a deterministic test system.
Posted Mar 30, 2013 3:36 UTC (Sat) by elanthis (guest, #6227)
[Link]
Threads - when invisible - are super important. No, most programs don't _need_ them, but I rather enjoy when the program I'm running on my modern 8-core desktop does its operations nearly instantly rather than making me wait because at most it's using ~12% of my CPU.
A look at C++14, part 1
Posted Mar 30, 2013 10:38 UTC (Sat) by simlo (subscriber, #10866)
[Link]
Most likely the program is not waiting to get CPU - it is waiting for blocking IO. If you have 8 cores, the OS will schedule the other programs to the other CPUs.
Threading is most of the time a hack around blocking API calls. It is very rare that a normal desktop program actually have CPU work for more than one CPU.
And then you forget the cost in programming time and runtime of locking overhead: A single threaded program does not need atomic operations, which can be quite expensive on a multi cored platform. If, for instance, a program uses reference counting (std::shared_ptr), these operations have to be atomic. If you know the program is single threaded, that overhead is not needed. That kind of overhead might make the single threaded program run faster than the multi threaded, which very often can not use all 8 CPUs anyway due to lock contention.
A look at C++14, part 1
Posted Mar 30, 2013 8:44 UTC (Sat) by alankila (subscriber, #47141)
[Link]
I suggest that you can't somehow smuggle in multiprocessing without encountering at least some of the hardships you suggest are exclusive to using threads. For instance, if you want to do multiprocessing and communicate using sockets or pipes, you could just as well use a producer-consumer pattern with threads and maintain the level of discipline in your threads that you know they don't need locking because all the resources they need for their work are local to the thread or just immutable.
Multiprocessing has one giant downside that always hits me in practice: the need to serialize and unserialize the datastructures that represent the work items and the results. This is pure overhead. In general the fact that threads can communicate with each other directly is their blessing and curse, but so far the blessings have been much larger to me, so I prefer code based on threads over the other means of achieving concurrency.
A look at C++14, part 1
Posted Mar 30, 2013 11:18 UTC (Sat) by simlo (subscriber, #10866)
[Link]
As I see it there are 3 levels of concurrency
1) Use of explicit locks (as in the Linux kernel)
2) Message queues between threads, where data structures are owned by one thread avoiding the need for locks.
3) Messages queues in the form of pipes and sockets between processes.
1) is clearly the hardest to code. In principle it scales well of an unlimited amount of CPUs but it will often it ends up in lock congestion, where every thread waits for a specific resource. There is also a huge runtime overhead in managing the locks and logic around it.
2) and 3) are almost the same. 3) has the downside as you say that everything has to be serialized, which is a (huge) overhead. The upside is that individual processes can crash independently making it a lot easier to debug. You can also use sockets as a debug output, such the system can be inspected while running. 3) also have the possibility to move some of the system to a another machine.
But with a well written framework, where the communication channels are abstracted, 3) can be made into 2) without changing the core application code. No matter if you use 2) or 3), you have to use the same state machines in the application to handle the concurrency problems.
But once the application is written with explicit locks as under 1), there is no way back.
My advice is thus: Start by using 3). With templates and well a well written framework it is really not that hard to avoid threads. Then if the overhead of serialization is too high, merge the processes as in 2).
What I oppose to, is having a C++ runtime assuming it is multi threaded, having the runtime system using atomic operations, when I want to stick to single threaded programs. And what I argue for, is that most people ought to stick to single threaded programs anyway!
A look at C++14, part 1
Posted Mar 30, 2013 17:55 UTC (Sat) by jwakely (subscriber, #60262)
[Link]
> What I oppose to, is having a C++ runtime assuming it is multi threaded, having the runtime system using atomic operations, when I want to stick to single threaded programs.
Use a better C++ runtime implementation then, don't argue the rest of us should always have to do things your way.
GCC can be configure with --disable-threads in which case there are no atomic operations used in the runtime. When configured with --enable-threads=posix (on a supported OS) the runtime still doesn't use atomic operations unless the program is linked with libpthread.so so single-threaded programs don't use atomic operations. So I don't really see what you're opposed to.
A look at C++14, part 1
Posted Mar 31, 2013 9:31 UTC (Sun) by simlo (subscriber, #10866)
[Link]
What I am opposed to is multi threading!
My experience have shown me that it is the most misunderstood and amused feature in programming. Everybody learn about it in school and think they have to use it and impose it on everybody else. Once a program, framework or API relies on multithreading it is impossible to avoid. But in 95% of the cases the multi threading was complete unneeded if just the API were available in non-blocking versions.
Take Java: when you implement a TCP protocol you have a thread for receive, a thread for transmit and a timer to detect inactivity. Using select() you can do this in one thread avoiding a lot potential race conditions. I have seen instabilities and odd problems popping up in old Java programs due to this.
When using this model in C++ you get into further problems due to missing garbage collector.
In the beginning UNIX user space did not have multi threading. It is mostly a bad habit coming from other OS'es lacking proper processes.
I am not totally against multi threading: realtime system and system with heavy scale able algorithms. But 95% of programs only becomes unstable and much harder to maintain due to threads.
A look at C++14, part 1
Posted Mar 31, 2013 13:59 UTC (Sun) by Cyberax (✭ supporter ✭, #52523)
[Link]
I haven't seen real Java apps using separate threads to receive and send data. That's simply stupid in most cases. However, I did see asynchronous servers die under a high load with all but one CPU completely idle.
A look at C++14, part 1
Posted Mar 31, 2013 21:08 UTC (Sun) by simlo (subscriber, #10866)
[Link]
Well, as I work with _real_ Java programs (written before select() came to Java), I know it is needed.
For instance: I have some data I have to send to different clients via TCP sockets. Naive solution: Loop through the clients and perform write() on each socket. Problem: If one client is not taking data the write() operation will block halting data for any other client as well.
Therefore I have to make a thread in which to execute write() for each client. And a message queue for each client to get data from the data producing thread(s) to the write thread.
Similarly, the read option is blocking so I have to have a receive thread per client.
And all this is hard to make work in all cases where clients can close the connection, the link is lost or what ever where the different thread are in different states.
On a proper OS (i.e. UNIX) in C++, I can use select() and therefore contain everything in one thread and have a clear picture of my possible states.
A look at C++14, part 1
Posted Apr 2, 2013 4:28 UTC (Tue) by akeane (subscriber, #85436)
[Link]
>What I am opposed to is multi threading!
Hooray, finally, a sane voice in the wilderness!
>the most misunderstood and _amused_ feature in programming.
This may be a typo, but yet it is brilliant somehow, I have always regarded pthreads as some evil entity laughing maliciously as some hapless programmer stumbles into it's wake...
If only I had a dollar for every sleep() I have seen to try to push that pesky race condition further into the sunset, and the amount of times I have seen assumptions being made about thread startup time and executions.
Or the nested locks, Oh God, the nested locks!
But back to pipelines in C++, surely the UNIX way would be to write some kind of general program which you could tell the system to take the output of one program and "pipe" it into another. I would call it:
/bin/M*A*S*H
Merry Easter
A look at C++14, part 1
Posted Apr 2, 2013 8:25 UTC (Tue) by jwakely (subscriber, #60262)
[Link]
> But back to pipelines in C++, surely the UNIX way would be to write some kind of general program which you could tell the system to take the output of one program and "pipe" it into another.
Posted Mar 31, 2013 14:04 UTC (Sun) by nix (subscriber, #2304)
[Link]
Well, sort of. An awful lot of libraries pull in libpthread just in case they are used with threaded programs. Nonthreaded programs then pull in libpthread transitively, and suddenly get hit with pointless locking overhead even though they will never have more than one thread.
Hell, on my system even ls(1) is linked with libpthread (though that is via librt because of a use of clock_gettime() and will presumably go away when it is rebuilt against glibc 2.17+.)
A look at C++14, part 1
Posted Mar 31, 2013 18:46 UTC (Sun) by jwakely (subscriber, #60262)
[Link]
> An awful lot of libraries pull in libpthread just in case they are used with threaded programs.
This is not the C++ runtime's fault though, libstdc++ goes to a lot of effort to avoid locking in non-threaded programs, but sufficiently motivated fools^Wprogrammers can defeat that effort.
If you really need to skin that cat you can build GCC with --disable-threads and use that libstdc++.so
A look at C++14, part 1
Posted Apr 1, 2013 23:03 UTC (Mon) by nix (subscriber, #2304)
[Link]
Indeed. Unfortunately, a lot of programmers don't realize they can use pthread locking functions even without linking to libpthread, getting stubs unless something else has loaded libpthread. The total lack of glibc libpthread documentation might explain this :)
(actually, I can't remember if they're stubs or weak symbols. Stubs, I think, so you don't even need to wrap their uses in null checks.)
A look at C++14, part 1
Posted Mar 30, 2013 17:46 UTC (Sat) by jwakely (subscriber, #60262)
[Link]
So you like new stuff, but only the stuff you want, and you don't like anything else even if other people want it. Got it.
If you don't like the new threading features don't use them. Sounds like you have no need for them, so it shouldn't be a problem for you.
A look at C++14, part 1
Posted Mar 31, 2013 14:06 UTC (Sun) by nix (subscriber, #2304)
[Link]
So you like new stuff, but only the stuff you want, and you don't like anything else even if other people want it. Got it.
This should be called "the user's lament" or something: it is universal. Note that since users often don't know if they'll like something before they try it, predicting this reliably requires not only telepathy but also precognition.
A look at C++14, part 1
Posted Mar 31, 2013 20:58 UTC (Sun) by simlo (subscriber, #10866)
[Link]
Before the newest C++ standeard the compiler didn't know about threading. That is it could optimize away as it wanted. Only the OS knew about threading. Now the compiler have to lay out code, which can work in a multi-threaded environment. So unless, I use non-standeard options on the compiler I can't avoid it. Of course you can say the same for exceptions.
I am not against features in libraries: Those I can simply avoid.
I do have as I mentioned above (https://lwn.net/Articles/545352/) a thing against multi threading in general. And now C++ tries to jump the wagon, too.
The good thing about C++ is that it doesn't do much opposite for instance Java. There is no standeard for GUI either. STL is a library of containers and types you can use anywhere on any OS and without an OS (except for file IO).
Of course, to make portable multi threaded programs, wrappers around the OS specific implementations where needed. But then why not wrappers for sockets, serial ports etc?
Or put it another way: Was POSIX not a good enough wrapper?
Before STL and C++ was really not about portability. There weren't even a standeard like int32_t in C++. With threading, atomic types, chrono etc. STL have been given the task of supplying portability.
The thing about threads:
People learn to use threads all over the place. The excuse these days are scale-ability. But very rarely your program can scale anyway. The core reason for using threads is "that is what I am used to" and due to not being used to work with non-blocking IO.
What we need in general (not only C++) is _not_ more support for threading, but non-blocking APIs and a good framework for writing applications using non-blocking IO.
A look at C++14, part 1
Posted Apr 1, 2013 11:43 UTC (Mon) by jwakely (subscriber, #60262)
[Link]
> Before the newest C++ standeard the compiler didn't know about threading.
Nonsense, people have been using Pthreads with C++ for decades. The *standard* didn't mention threads, but compilers live in the real world and had to deal with what programmers actually do.
You're making a silly leap from "some people don't use threads correctly" to "I wish noone was allowed to use threads." Luckily you're not actually involved in the standardisation process, you're just whining on the internet.
> What we need in general (not only C++) is _not_ more support for threading, but non-blocking APIs and a good framework for writing applications using non-blocking IO.
There are proposals to add such things to the next C++ standard, because the committee realises not everyone programs the same way and that different tools are suitable for different tasks. Maybe you would benefit from the same realisation.
A look at C++14, part 1
Posted Apr 1, 2013 15:41 UTC (Mon) by tjc (subscriber, #137)
[Link]
Thanks for starting the sub-thread on multithreading. I'm not sure if I agree with you or not--I have to think about this more--but it was very interesting.
A look at C++14, part 1
Posted Apr 1, 2013 23:05 UTC (Mon) by nix (subscriber, #2304)
[Link]
Before the newest C++ standeard the compiler didn't know about threading. That is it could optimize away as it wanted. Only the OS knew about threading. Now the compiler have to lay out code, which can work in a multi-threaded environment.