Live data from Hacker News

Hell is a multi-threaded C++ program (2006)

codemines.blogspot.com

61–70 of 129 posts

Re: Hell is a multi-threaded C++ program (2006)

#61

I've been writing massively threaded C++ code since the 1990's, so I can comment with some serious experience here. My code used to run on 256CPU SGI's and had high cpu utilization across all of them. When designing C++ code for threading, you have to keep a few things in mind, but it's not particularly harder than on other languages with thread support. You generally want to create re-entrant functions which pass al…

technique wise, it's not too hard, but it only takes one little screw up to bring the whole thing crashing down. Back in an older version of MS C++ compiler/library their std::string had this bug where strings weren't thread safe as they relied on global state. That took a while to find.

Re: Hell is a multi-threaded C++ program (2006)

#62
post #15

There is no need to use raw pthreads in modern C++, it comes equipped with a much more convenient and capable standard library module. I prefer working with channels, which are still missing from the library; but they are trivial to add: https://github.com/andreas-gone-wild/blog/blob/master/diy_cp...

This is good advice and should never have attracted a downvote! I'm not actually sure that the C++ library does anything that pthreads doesn't - maybe std::lock, perhaps? - but it's more convenient to use (and it's not like pthreads is bad to start with...), and the pieces work well and fit together quite nicely. Some good use of move semantics. unique_lock is neat and you can do stuff like vector . The C++ thread li…

I was thinking about mostly about atomics, which aren't covered by pthreads; and I can't remember shared mutexes being supported either.

Re: Hell is a multi-threaded C++ program (2006)

#63

I've been writing massively threaded C++ code since the 1990's, so I can comment with some serious experience here. My code used to run on 256CPU SGI's and had high cpu utilization across all of them. When designing C++ code for threading, you have to keep a few things in mind, but it's not particularly harder than on other languages with thread support. You generally want to create re-entrant functions which pass al…

I am not as experienced as you are and I don't even know the term re-entrant functions but accidentally this is how I am now designing programs, mostly writing self contained functions that don't alter any global state. This approach has made my programming life hell because I am finding it incredibly hard to get rid of the old shared variables habit. What I have experienced is that shared data access is more or less inevitable and I design to restrict the shared data access at the database.

Re: Hell is a multi-threaded C++ program (2006)

#64

Earlier quoted context omitted.

> True C++ hell is taking a single threaded C++ program and making it multi-threaded after the fact, but that's hell in any language which allows you to have globals of any sort and doesn't perform function calls within some kind of closure. That's not true. In Rust you can have globals, but you have to declare the synchronization semantics for every global when you do. (That is, they have to be read only, or thread…

> you have to declare the synchronization semantics for every global when you do. (That is, they have to be read only, or thread local, or atomic, or protected by a lock.) That doesn't sound like it helps you determine which one of those is appropriate. So it seems rather like a system that will lead to unexpected bottlenecks when you parallelize. (Somebody deep inside the stack arbitrarily decided locking was the mo…

So much nicer to change than the type and chase compiler errors though. Reading the code and just being real smart is tough to get right.

Re: Hell is a multi-threaded C++ program (2006)

#65

There is no need to use raw pthreads in modern C++, it comes equipped with a much more convenient and capable standard library module. I prefer working with channels, which are still missing from the library; but they are trivial to add: https://github.com/andreas-gone-wild/blog/blob/master/diy_cp...

Channels like the one in the link are pretty easy to create. However that's more like Javas BlockingQueue than like Go channels. The power of Gos channels is in "select", which enables a lot more sophisticated synchronization mechansism then just getting data from thread A to B. E.g. waiting for events from multiple sources or cancellation support. Building channels with select functionality is a lot harder. An inter…

What is even more fundamental and useful than select is non-blocking reads (which the implementation I posted supports), once you have that you can write your own select. It's not magic, select has to traverse the list of channels just like any other code. The only reason it feels like magic in Go is that it's the only way to read a channel without blocking.

Re: Hell is a multi-threaded C++ program (2006)

#66
post #39

So to check, I googled C++11 threads memory model, and it[0] looks like his main complaint isn't helped by C++ 11 threads, as they essentially allow shared state too. They are also a thin veneer over the OS threads, for unix types, pthreads I imagine. [0] http://en.cppreference.com/w/cpp/language/memory_model

Honest question: If you aren't going to share data between/among threads, why use threads at all? Why not just fork-abd-exec?

Sharing (immutable) data between threads is fine, you just don't want to share (mutable) state or else you run the risk of threads stomping on each other. The ideal scenario is map/reduce: you take some large chunk of data, divide it up into smaller chunks, spin up a thread to process each smaller chunk and produce a result. Once the threads have all completed and the results are no longer changing, the main thread can pick them up and combine them to a single result.

It's possible to do that with fork-and-exec, but without a shared address space, marshalling costs make it expensive.

Re: Hell is a multi-threaded C++ program (2006)

#67
post #63

I've been writing massively threaded C++ code since the 1990's, so I can comment with some serious experience here. My code used to run on 256CPU SGI's and had high cpu utilization across all of them. When designing C++ code for threading, you have to keep a few things in mind, but it's not particularly harder than on other languages with thread support. You generally want to create re-entrant functions which pass al…

I am not as experienced as you are and I don't even know the term re-entrant functions but accidentally this is how I am now designing programs, mostly writing self contained functions that don't alter any global state. This approach has made my programming life hell because I am finding it incredibly hard to get rid of the old shared variables habit. What I have experienced is that shared data access is more or less…

a lot of the time you can restrict shared data access to at least being read-only, which helps a lot.

Re: Hell is a multi-threaded C++ program (2006)

#68

Earlier quoted context omitted.

Channels like the one in the link are pretty easy to create. However that's more like Javas BlockingQueue than like Go channels. The power of Gos channels is in "select", which enables a lot more sophisticated synchronization mechansism then just getting data from thread A to B. E.g. waiting for events from multiple sources or cancellation support. Building channels with select functionality is a lot harder. An inter…

What is even more fundamental and useful than select is non-blocking reads (which the implementation I posted supports), once you have that you can write your own select. It's not magic, select has to traverse the list of channels just like any other code. The only reason it feels like magic in Go is that it's the only way to read a channel without blocking.

Select is a kernel level interface. I don't know how it's implemented at the kernel, but there is no technical reason why it can't follow from an interruption to an array index and a file descriptor without ever passing through a list.

Besides, how do you block in user space when there is nothing to read?

Re: Hell is a multi-threaded C++ program (2006)

#69

I've been writing massively threaded C++ code since the 1990's, so I can comment with some serious experience here. My code used to run on 256CPU SGI's and had high cpu utilization across all of them. When designing C++ code for threading, you have to keep a few things in mind, but it's not particularly harder than on other languages with thread support. You generally want to create re-entrant functions which pass al…

Obviously it is possible to write threaded C++ programs. And there are experienced people like you who can do it well, with enough discipline, good design, and so on. But I think the point of the article is that for most programmers it is very easy to make mistakes and shoot themselves in the foot with threads and shared memory. It could be even something like using a 3rd party library where it's initialization context can't be shared but a mistake was made and it did end up being shared by accident, say passed to some workers threads.

> What leads to problems in threaded C++ programs is unprotected, shared state.

That's one of the main problem, but I don't think people start with saying "we'll just have this unprotected shared state and hope for the best", that shared state ends up being shared by accident or as a bug. I've seen enough of those and they not fun to debug (hardware environment is slightly different, say cache sizes are bit off, to make it more likely to happen at customer's site for example, on Wednesday evening at 9pm but never during QA testing).

Other things I've seen bugs in is mixing non-blocking (select / epoll / etc) based callbacks with threads. Pretty easy to get tangled there. Throw signal handlers in and now it is very easy to end up with a spaghetti mess.

Even worse, there is a difference between "we'll start with a clean, sane threaded design from the start" vs "we'll add a bit of threading here in this corner for extra performance". That second case is much worse and can result in subtle and tricky bugs. Sometimes it is not easy to determine if the code is re-entrant in a large code-base.

Interestingly and kind of tongue in cheek someone (I think Joe Armstrong, but I maybe wrong) said to try and think about your programming environment as an operating system. It is 2017, most sane operating systems have processes with isolated heaps, startup supervision (so services can be started / stopped as groups), preemption based concurrency (processes don't have to explicitly yield) and so on. So everyone agrees that's sane and normal and say putting their latest production release on a Windows 3.1 would not be a good idea. Why do we then do it with our programming environment? A bunch of C++ threads sharing memory are bit like that Windows 3.1 environment where the word processor crashes because the calculator or a game overwrote its memory.

Re: Hell is a multi-threaded C++ program (2006)

#70

I've been writing massively threaded C++ code since the 1990's, so I can comment with some serious experience here. My code used to run on 256CPU SGI's and had high cpu utilization across all of them. When designing C++ code for threading, you have to keep a few things in mind, but it's not particularly harder than on other languages with thread support. You generally want to create re-entrant functions which pass al…

So, functional programming then?

[deleted]
Post reply on HN