> The centralized CPU model is a historical acc...
# share-your-work
g
The centralized CPU model is a historical accident driven by 1950s economics. CPUs and memory were expensive, so we time-shared a single machine and faked parallelism. Hardware has since gotten cheap. We should stop architecting software as if it hasn't.
The Hardware Changed. The Software Didn't.
đź’Ż 1
b
is the question how to scale to use all available hardware? I guess I’m confused because i’d rather work on a single node shared-memory system than two nodes any day I agree that if you’re thinking about locks, you’re in trouble and working on the wrong abstraction level. I’ve written a bunch of parallel code in julia and didn’t have to worry about locks (maybe the libraries I used handled all that…presuming they were written correctly, my code should be deadlock free because it didn’t involve anything that could deadlock) I disagree that decentralization is a win. It adds the cognitive effort of moving data around efficiently: on a shared memory system, data movement is free—you pass a pointer. if data is decentralized, now you have to be clever. if the data you need isn’t where the CPU is, then it has to be copied over the wire somehow.

MPIâ–ľ

works nicely specifically because its API functions (broadcast, reduce) try to imitate synchronized shared-memory programming as much as possible (as much as it is called “Message Passing Interface” it is usually just a minimal data sync to pretend you are on a single machine) i never want to have to be clever. gimme 100 CPU cores on a die instead of 100 stand-alone machines. second best, give me MPI where I can assume all my machines are doing the same work in sync. but even MPI is strictly more cognitive load than a single machine
g
The way we build software - time-shared and centralized - is no longer appropriate for the problem domains of the 21st century, regardless of how convenient the synchronous, sequential, centralized paradigm seems. I calculate that the MPI abstraction costs about 30,600,000 LOC (MPI must be perched atop Linux or other even more bloated operating systems). We can do a lot better. Any code sitting atop Linux has a base tax of about 30,000,000 lines of code on top of CPU ICs that use many more transistors than what was needed to build Sketchpad, Visicalc, Turbo Pascal, Unix V1 (UNIX was originally written in assembler, not K&R C), etc. Continuing to build software the same way over and over again, expecting different results is a death march based on unwillingness to accurately assess the cost of doing so and based on the belief that there is only one purpose for "computers" (a better name might be RM - Reprogrammable Machines). Pure functional programming - modern [Sector Lisp]([https://justine.lol/sectorlisp2/](https://justine.lol/sectorlisp2/)) - costs about 354 lines of assembler. Most other programming languages pay a huge tax for stretching and generalizing the functional programming model. We need to think outside of the box of the synchronous, sequential, centralized model. Machine code and ICs can support many more kinds of models than just functional programming (K&R C may have been a convenient abstraction, but, subsequently, C has been distorted into a functional programming language (albeit flawed) replete with function-based calling conventions). Progress should result in less code. I don't believe that this is happening. Our current programming model makes us believe that issues - e.g. assignment, synchronous concurrency, asynchronous parallelism, thread safety, control flow - falling outside of the functional, synchronous, sequential model can be ignored or simply assimilated. Yet, Harel came up with a better notation for one of those issues some 40 years ago (Statecharts for reactive control flow). I think that we should be using models for programming that allow us to compose solutions using many paradigms, each with laser-focused languages to support them - specialization instead of generalization and the kitchen sink approach. Decentralization instead of centralization and assimilation. Eliminating problems instead of laboriously assimilating solutions to them. Switching to a decentralized model could eliminate the issue of thread safety. Simply eliminating thread safety concerns would noticeably reduce the size and complexity of programming languages. Since the main role of operating systems is to act as scaffolding to support the functional model, can something be done about that issue? Everything we've accomplished in the past 50 years fits inside just one of the bubbles on my diagram labelled "app". We haven't really addressed the rest of the stuff, other than by applying notation worship to assimilate each new gotcha, in a whack-a-mole manner, into the ball of stuff that we already have. Grace Hopper warned against this approach. Functions and math-like notation (mangled by 500 year-old Gutenberg technology) aren't the only way to program the things we call "computers". It is entirely reasonable to specialize and drill down onto a thin slice of programming. But, it must be remembered that this is not programming, it is just a thin slice of programming. Looking at all problems through a single lens has a cost.
o
love the idea, your post reminds me of Erlang, which I adore but never put enough effort to learn it properly. Thank you.
g
Thanks. Aside: I happen to be interested in snapping parts together like LEGO blocks. To do this, you need "topological blindness", something which Erlang doesn't insist on. A part must not literally send messages to other parts, you should only stuff your outputs into your own output queue. Something above you distributes / routes your outputs to the correct places - a parent part that I call a Container. Messages are key/value pairs
{port id, payload}
. Routing involves changing the sender's port id to the receiver's port id (which alleviates a lot of namespace issues). I call these kinds of messages mevents (message-events). A wire is a triple (or quintuple, depending on how you read it)
{direction, {sender, sender's port id}, {receiver, receiver's port id}}
, where direction is
[down | up | across | through]
. This kind of indirection was shunned by 1960's-thinking, but, is necessary to allow for fully isolated LEGO-like parts and allows for fan-out and fan-in.
Copy code
Q: Does Erlang preserve topology blindness?
A: Mostly — processes share no state, but each still needs a peer's PID to send a message.
Q: Does owning a peer's PID preserve topology blindness?
A: No — knowing who to talk to is already topology knowledge.
Q: So, again, does Erlang preserve the principle of topology blindness?
A: No — sending requires knowing the address, so full blindness is lost.
Q: If we place output on an output queue instead of sending it to a PID or calling a function, does that preserve topology blindness?
A: Yes — the producer doesn't know who reads it, or how many.
o
will the blackboard [common space for all messages] work then for you to achieve topology blindness? every module posts a message to a shared space and then every other module checks if this message can be processed by it. it is kind of similar to forums where every person posts an idea and all other read it and check if they can do anything with it.
g
Containers are "recursive" blackboards. There are 2 kinds of parts - Container Part, Leaf Part. Kind of like Lisp lists (list, atom). Containers contain parts, which can be other Containers or Leaves. Leaves contain only code and are not defined to be recursive. My (albeit weak) understanding of blackboards is that they are flat, not layered / nested / recursive. This is also true of my understanding of pub/sub, too. In essence Containers are collections of parts and a table of wires / routing-info-between-parts-in-the-collection. Leaf parts are bits of code with one steady-state entry point (the mevent handler). [I'm currently working on writing this up in book form ... so maybe my description here is inadvertantly jumping into the middle of something. Have I given enough context here?]