13 ms·
I started doing massively parallel programming on SGI systems around the time this paper was published. SGI's at the time could have 64 CPUs in a single system
by oppositelock 7y ago
I started doing massively parallel programming on SGI systems around the time this paper was published. SGI's at the time could have 64 CPUs in a single system image, which was very novel. Sun was working on its early multi core workstations, and companies like Cray were pushing different models of distributed computation.
This paper came at a time when threads were really painful to work with. POSIX threads were still new and mostly unsupported, so you were stuck with whatever your OS exposed. On IRIX, you would do threads yourself by forking and setting up a shared memory pool, on Solaris, you had the best early pthread support, in Java, you used the native Thread classes which only really worked on Solaris at the time. It was a mess!
This mess is now solved. Pthreads are everywhere, C++ has std::thread, Java threads work everywhere, and we've had many new language some out which handle parallelism beautifully - for example, the consumer/producer model built into channels in Go is very elegant. The odd one out is Win32, but it's close enough to pthreads for the same concepts to apply.
Event driven programming has also become threaded, this is how the whole reactive family of frameworks, node.js, etc, handle parallelism.
As someone who's been doing this for a long time, what I find confusing is higher level constructs that try to hide the notion of asynchronous operations, such as futures, promises. As a concept, they're fine, but they're difficult to debug because most developer tools seem not to care about making debugging threads easier.
- gdubs 7y agoCurious what kind of work your were doing? I’ve always been fascinated by the golden days of SGI.
- oppositelock 7y agoAll sorts of real time graphics stuff; flight simulators, oil and gas visualization of voxel maps, general industrial visualization. These were the early days of hardware accelerated 3D and everyone thought that visualization would change the world to a greater extent than it really did. It was great fun to write code which drove 8 displays using 8 graphics pipes, with roughly 8 cores working in concert with each pipe. All this work for something that runs faster on the latest iphone using a single thread... I went on to work at SGI for a few years, and it was still my favorite job ever. It was pure R&D, graphics and realtime systems for their own sake. Today, this doesn't exist. 3D graphics are an applied technology that's part of an app, but not a product research area of its own.
- zeusk 7y ago> It was pure R&D, graphics and realtime systems for their own sake. Today, this doesn't exist. 3D graphics are an applied technology that's part of an app, but not a product research area of its own. I'm quite certain there are teams here at Microsoft that do just this, and their counterparts in the GPU industry (AMD/Nvidia/Intel - although Nvidia seems to be the dominant one in research)
- Psyladine 7y ago>and everyone thought that visualization would change the world to a greater extent than it really did. c.f.: Data Scientists
- dijit 7y agoYour comment about threading in win32 being lackluster is surprising. It was my understanding that while the OS doesn't support fork(), that it /does/ support pthreads, and that as soon as you go outside of the process itself, Windows has "better for developers" system calls. I'm speaking specifically about I/O Completion ports. Working with epoll or select/poll is really nasty when dealing with "some data" that's coming on a socket, where as IOCP allows you to tell the kernel "as soon as you see some data destined for my thread, just populate it in the memory here, tell me when something is there and then tell me how much is there"; for high performance C++ programmers this is basically invaluable and as far as I understand there's no direct competitor in UNIX and unix-like land. (although; according to the C++ devs where I work, "kqueues are less braindead than epoll")
- PaulDavisThe1st 7y agothe thing that is missing (or was missing - not 100% of the current state of things right now) in nix land is the generalized concept of "waitForSomething" where "something" could include any* event/data that should/could wake a thread. kqueues help a bit, but I think it is still harder than it should be on *nix (even Linux) to write code whose semantics are "put this thread to sleep until anything happens". If the thread is waiting on a specific set of objects (e.g. file descriptors, kqueues), then this can be done easily, but it's still not quite as simple as it has been under Windows (and by the way, IOCP is built on top of the underlying mechanism that makes this possible on Windows; they are not the mechanism itself)
- zozbot234 7y agoWith io_uring this can be done on Linux as well.
- StillBored 7y agoNot yet, read the LWN article and look at the todo list. There are a number of things still missing, and of course there are things like semaphores and generic eventing that still aren't part of io_uring. io_uring is "different" and definitely has some advantages due to its shared ring buffer. but NT has a core/generic set of async completion/wait/etc functions that can be applied to basically the entire API surface.
- brlewis 7y ago> Java threads work everywhere That depends how you define "work". Java has a decent concurrency library such that if you have a limited-scope project implemented by 1 or 2 highly-skilled programmers, you stand a good chance of avoiding thread safety issues. But as the project grows, the probability of thread safety issues approaches 1. > Event driven programming has also become threaded, this is how the whole reactive family of frameworks, node.js, etc, handle parallelism. Event-driven programming is way less painful than Java threads. With threads, in every place you mutate anything you have to ask yourself, "what happens if my thread is preempted here?" With event-driven programming you only do that sort of thinking in places where you see the `await` keyword.
- trhway 7y ago>Event-driven programming is way less painful than Java threads. With threads, in every place you mutate anything you have to ask yourself, "what happens if my thread is preempted here?" in 23 years programming Java (and in the all places where i've worked having been recognized as local expert on threads and synchronization, incl. in the current very large C++ platform project) i have never asked myself that question in the context of the mutation of data.
- plapetomain 7y agoWell you either did ask that question and answered it by using appropriate locking and concurrent data structures (which is basically implicitly asking and addressing that question..) or you wrote a lot of crash prone shitty software. Which one was it?
- trhway 7y agoLocking isn't about preemption. You can have a system without preemption and still have to use the locks and other concurrent primitives.
- deleted 7y ago[deleted]
- stareatgoats 7y ago> futures, promises. As a concept, they're fine, but they're difficult to debug Might as well forget debugging in promise-land. I think this is partly because they are conceptually problematic as well, apparent once beyond the simplest use-cases, given the number of bloggers that still seek to explain it - and then are forced to edit their post because of errors. And the need to list common mistakes on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#Common_mistakes https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guid...
- mc3 7y agoI don't think it is that bad, once you learn those mistakes (maybe a linter could help to?) and more importantly bed down the mental model I think promises are a fine way to do things up to a point. I don't do intense concurrency in the browser, but I'll do non trivial stuff, like collating responses from a server then sending a request once I have all that information. I think promises are fine for this and it is possible for most programmers to write clean, mostly mistake-free promise based code. As for difficult to debug? I've never had that issue, neither in JS (browser/node) or C# with the similar but different Task<>. For example in JS, I can put breakpoints at any point of the promise chain and see what is going on. If they are network requests I'll also look at the network tab. If everything is happening real fast I might use console.log statements, but that is rare. I think promises are OK for a lot of situations that most of us will encounter developing business software - this might be a reflection on the simplicity of the problems I end up solving. Obviously if you are creating a multi-threaded high-frequency market making trading thing then this might not apply.
- int_19h 7y agoIt's all about tooling. There's no reason why a debugger can't make sense of "spaghetti" stacks that result from future-based async code, and some debuggers do just that (e.g. VS does it for C#).
- pdimitar 7y ago> This mess is now solved. If you say so. I can't count the dollars I've made fixing other people's poorly written multithreaded code in my entire career, including in the last 3 years. Thread support is [more or less] standardised in all major OS-es now, sure. Doesn't change the fact that it's an extremely bad fit for the human brain to think about parallelism. Stuff like actors with a message inbox (Erlang/Elixir's preemptive green threads) or parallel iterators transparently multiplexing work on all CPU cores (Rust's `rayon` comes to mind) are much better abstractions for us the poor humans to think in terms with. Golang's goroutines are... okay. Far from amazing. Still a big improvement over multithreaded code as you pointed out though, I fully agree with that. I might be projecting here and please accept my sincere apologies if so, but it seems to me you are a bit elitistic in your comment. Multithreaded programming is still one of the most problematic activities even for senior programmers, to this day. Multithreading bugs get written and fixed every day. At this point I believe we should just move to hardware-enabled preemptive actors where message passing is optimised on the hardware level, and just end all parallelism disputes forever since they are an eternal distraction. (The overhead when utilising message passing today is of course not acceptable in no small amount of projects. Hence the hardware suggestion.)
- travisgriggs 7y ago> Doesn't change the fact that it's an extremely bad fit for the human brain to think about parallelism. I'm curious why you feel this way? Certainly anything with multiple (more than one) thread is a lot harder than single threaded. Given. But I think the human experience is rich with real world counterparts that can make multithreaded programming "natural" (not necessarily easy, there's a difference). Basically any process in real life you do that involves collaborating with multiple people is a collaborative multi-threaded process. When forced to grapple with multi-threaded solutions, I often ask myself, "if I had a room full of people working on this problem, what would have to be in place to make the operation flow smoothly?" For me, this anthropomorophisation of the process makes it a "good fit" for how my brain is used to solving lots of real world problems. On the flip side, I haven't had a lot of luck finding real world experiences that I can model coroutines/async/etc on. So they may be ultimately easier, but if our rubrik for "fit for the human brain to think about" is what the wealth of human experience has evolved our brain to handle well, I'm less convinced. But I like learning new things. Maybe I just haven't seen the lightbulb yet. Help me see your point of view?
- dirtydroog 7y agoWhere did futures/promises come from? They just seemed to appear out of nowhere. I'm not particularly a fan of them either. C++'s current version of std::future is particulary useless.
- mc3 7y agoReading on wikipedia, it sounds like the 1980s this stuff came out, with support in a particular lisp released in 1980. The promise pipelining technique (using futures to overcome latency) was invented by Barbara Liskov and Liuba Shrira in 1988. Yes B. Liskov is the L in "SOLID", if you are wondering! Happy 40th birthday promises! :-) I wouldn't be surprised if mathematicians were thinking of this before transistors were invented though. I think it takes time for things to trickle down. Maybe it takes someone from an academic background (or a curious paper reader) to be forced to use C++ or Javascript for something, but along the lines of "progress relies on unreasonable men/women" they get annoyed and write their own library so they can use the nice Lisp/Haskell/etc. feature they are used to, but often in a more limited way but better than nothing. Then from there it might get hyped to the shithouse, die, or just get used by insiders.
- dboreham 7y agoThey're the lesser of two evils: before came callback hell. Async/await has also mostly replaced bare Promises. The result almost gives you something as usable as threads. Fwiw I find it amusing the async is promoted as the pinnacle of elegance in concurrent programming when in reality it exists because the JS interpreter wasn't thread safe.
- kragen 7y agoThere were thread-safe JS interpreters, in early versions of Opera for example. But without locks it was going to be impossible to write thread-safe JS. The arguments of Ousterhout and Miller may or may not have been an influence in 2000 but certainly they were in 2010. You might be interested in Miller's dissertation.
- abecedarius 7y agoAFAIK the E language http://erights.org/index.html http://erights.org/index.html influenced several other people and projects like Twisted Python and Midori, which influenced the now-popular deployments like Javascript. (I followed E in the 90s but not so much the other projects.) There's a sketch of E's history at http://erights.org/history/index.html http://erights.org/history/index.html but it's mostly stubs there. They apparently invented promises independently of Liskov, while working on Xanadu: http://erights.org/elib/distrib/pipeline.html http://erights.org/elib/distrib/pipeline.html
- newnewpdro 7y agoI came along a bit after you did, arriving with Pthreads books already in bookstores, but on Linux we were just getting LinuxThreads and not yet even NPTL, so things were still pretty flaky. I remember trying to write threaded programs that used SVGAlib, and breaking my console multiple times a day because LinuxThreads used SIGUSR[12] internally and so did SVGAlib, that year magic-sysrq became my best friend. My impression at the time was that developers had a flawed impression of threading complexity and bugginess because they were bolting threads onto existing single-process programs, and their existing hygiene was the problem, nothing inherent to threading. If you look at the single-process C programs of the era, global variables and global state in general were extremely common. When you start adding threads to such programs to try take advantage of SMP, trying to wrap locks around heaps of unnecessarily shared, poorly encapsulated state, of course you produce a lot of buggy programs. Then everyone starts saying "multi-threading is too hard, not worth it", instead of admitting their programs are a complete mess. Using modern standards of hygiene, even with threads-naive languages like C and good old Pthreads, I don't find it particularly challenging at all to write threaded programs. Like you mentioned, I also struggle with the higher level abstractions attempting to make threading easier. Pthreads makes sense to me, I find threads, mutexes, condition variables, rwlocks, all very intuitive and ergonomic to use, but it's probably because I spent a lot of time using that API at a young age. As I started taking swe jobs in silicon valley in the early-mid 2000s, it surprised me how few people had experience with Pthreads. It blew my mind, four different startups doing C programming with experienced C programmers and nobody was ever familiar enough with Pthreads to quickly review my code without having to rtfm. It's like there was such a stigma surrounding threads being "too hard" a lot of people never even attempted it. But my being a kid learning linux and just excited about new features in my unix, when LinuxThreads arrived and then NPTL, I spent years playing with Pthreads in C and getting my hands on SMP systems just to program them with Pthreads. Those years of playing paid off unexpectedly well in silicon valley, SMP was everywhere and C was still heavily in use on Linux. I still get a bit of happy nostalgia when the opportunity arises to write some code like: pthread_mutex_lock(&foo->lock); while (!foo->ready) pthread_cond_wait(&foo->cond, &foo->lock); /* consume from foo */ pthread_mutex_unlock(&foo->lock);
- rovolo 7y ago
- BlueTemplar 7y agoYeah, as someone using JavaScript for the first time (and not being an experienced developer either), I gave a brief look at promises, recognized that I hardly understood any of it, and quickly decided to use the bleeding edge async instead ! (And haven't regretted it.)
- adrianmonk 7y ago> Sun was working on its early multi core workstations This article is from 1995, and they were out a bit earlier than that. Sun had been shipping 4-core workstations with thread support since 1993. The SPARCstation 10 came out in 1992 and had two MBus slots. Each slot could take a CPU card with 1 or 2 CPUs on it. Although SunOS 4.x had very limited multi-CPU support, SunOS 5.x had support for multiple CPUs. SunOS 5.1 (Solaris 2.1) came out in 1992 and supported SMP, and SunOS 5.2 (Solaris 2.2) came out in 1993 and introduced a thread API. By 1995, they had also introduced the SPARCstation 20 and the 64-bit Ultra 2. They also had some server systems (like the SPARCcenter 2000) that had a backplane with several boards that could themselves have CPU cards. I don't know the maximum number of CPUs, probably something like 20. Info from my memory and: http://mbus.sunhelp.org/index.htm http://mbus.sunhelp.org/index.htm https://en.wikipedia.org/wiki/SPARCstation_10 https://en.wikipedia.org/wiki/SPARCstation_10 https://en.wikipedia.org/wiki/Solaris_(operating_system) https://en.wikipedia.org/wiki/Solaris_(operating_system)
- badsectoracula 7y ago> The odd one out is Win32, but it's close enough to pthreads for the same concepts to apply. One thing that i find very convenient in Win32 that is lacking in other platforms (at least using pthreads) is that every thread has its own message queue and you can have threads communicating with each other simply via PostMessage/GetMessage (which also handles sleeping). You can implement something similar over pthreads, but it is nice that the OS provides that out of the box.
- dfox 7y agoThe message queue is user-space abstraction relevant to the GUI subsystem and not something that is inherent to Win32 threading model. And in fact its implicit existence is source of completely unique Win32 class of hard to debug threading bugs. Edit: on the other hand Win32 has one really nice feature: you can WaitForMultipleObjects() on essentially anything that has kernel handle, which includes most of IPC primitives. On the other hand this causes the native Win32 IPC to have significant overhead and is the reason why game developers often resort to userspace spin-locks and why Windows 10 had introduced NPTL/Linux-style lightweight futex-based mutexes...
- int_19h 7y agoWin32 message queues are not only relevant to the GUI subsystem. Note how PostThreadMessage doesn't even deal with HWNDs anywhere. And COM, for example, uses those same message queues for cross-apartment calls - even if it's one GUI-less service calling another one, there's a message pump in there somewhere.
- dfox 7y agoI somewhat believe that the sole reason for existence of PostThreadMessage and friends is to make COM cross-apartment calls work for threads that do not own any windows. You can probably devise other uses for that, but such uses still somewhat boil down to implementing your own COM-like bidirectional IPC mechanism (GIMP's libwire comes to mind, which does essentially the same thing on top of “anything that behaves like SOCK_STREAM socket” and shared temporary directory)
- pvg 7y agoOn IRIX, you would do threads yourself by forking and setting up a shared memory pool IRIX also had that weird 'sproc' API that I think was semi-lifted from something in Sequent's DYNIX.
- dfox 7y agoBy the way this is how threads work on Linux to this day. LinuxThreads were exactly this and NPTL is mostly about doing magic with RT signals and shadowing libc symbols such that stuff that changes global process state works reliably. Along the way the kernel got some awareness of userspace playing such tricks and got futex(), but still it is mostly implemented as bunch of userspace magic that makes essentially unrelated processes look like threads of same process. One nice consequence of this is that all the PTHREAD_WHATEVER_PSHARED stuff simply works (in contrast to BSD derivates, where you have to choose whether you want to use IPC primitive across threads or across processed and when you want both you get -ENOSYS, -EINVAL or somewhat hilariously -ENOMEM with manpage containing rationale along the lines “POSIX says that this has to be possible and that there can be global limit on number of such objects. It does not say that the limit should be larger than zero, so this always fails with -ENOMEM”)
- zelly 7y agoThe big problem with native threads is its API. You have to make a callback aka function pointer. All of a sudden you have to keep track of parallel execution of these function pointers. This level of abstraction has no representation in code, only existing in the developer's mind. This doesn't scale. There is an impedence mismatch between how code is written (sequential, top-to-bottom, left-to-right text) and how parallel code behaves. That's why people use idioms like actors or coroutines. I think to actually solve this you need a new format for writing code, like a graphical function call graph instead of a text editor.
- StillBored 7y agoYour win32 comment is odd. I was developing on NT during the 3.1 betas in ~1992 and we chose to go all in on multithreading. Our product launched in 1993 with the release of NT. I don't ever remember hitting an OS level thread/etc bug (fair share of footguns though). More than a year later we were trying to port much of the codebase to solaris and it was a nightmare. Pretty much nothing worked right so we ended up bolting on the fork/mmap abstraction on top of much of it and building our own lock wrapper. While we sorta got it working, the solaris port died for internal political reasons and the NT version took off and we never looked back. NT was designed out of the gate for heavily threaded/async workloads, that stuff wasn't bolted on like pretty much every unix clone in existence.
- rcurry 7y agoI worked for a major day trading software company back in 1999 or so and we built everything around NT. I had come from a Unix background, but to be honest NT was the perfect choice at the time for building the back end of a large-scale retail trading platform. And IO completion ports were amazing compared to what was available on other platforms.