Why Skin-Deep Correctness -- Isn't, and Foundations Matter.
Among the advertised features of Apple's latest OS update, three in particular caught my attention: "auto-save", which claims to wipe out the abomination of volatile-by-default documents; "versioning", which claims to introduce document version-control into the Mac's normal operations; and "resume", which promises to re-load a user's work-state whenever an application is re-started.
On the surface, these new features appear to bring in two of what I believe to be the defining attributes of a non-user-hostile computer system:
the Second Law of Sane Personal Computing:
Information which entered the machine through deliberate operator action shall never be destroyed or otherwise rendered inaccessible except as a result of deliberate operator action to that end. No operator action shall lead to the destruction of information unless said destruction is the explicit and sole purpose of the action. If all non-volatile storage space that could hold full undo-information for operator-initiated actions is exhausted, the operator shall be informed immediately and given the opportunity to explicitly erase unwanted data or connect additional storage devices, thus preventing unintentional information loss.
and the Third:
Volatile storage devices (i.e. RAM) shall serve exclusively as read/write cache for non-volatile storage devices. From the perspective of all software except for the operating system, the machine must present a single address space which can be considered non-volatile. No computer system obeys this law which takes longer to fully recover its state from a disruption of its power source than an electric lamp would.
But... not so fast. By all means, open your checkbooks, buy the updates. But before you also buy an icon of Mr. Jobs to kiss, take a moment to think.
As expected, Apple's promotional material breathes not one word about how these features are implemented. But the fact that they are advertised as separate features is just about a dead giveaway of how they aren't implemented. That is to say: correctly, as a unified whole; in some way which is not, at its heart, a sham, a cheap trick, a fuffle. But how can we be so certain that we are being fooled?
It is because a system architecture having orthogonal persistence [1] would give you "auto-save" and "resume" for free. Auto-versioning would follow just as readily from a relatively-uncomplicated mechanism laid on top of an orthogonally-persistent address space. [2] Apple's OS update clearly has not removed and replaced the system's UNIX foundation with something sane, and therefore orthogonal persistence is not to be found in Mac OS 10.7. It follows trivially that Apple's auto-save and all related features are implemented by means of demanding ever more pervasive use of proprietary document-specific API calls from programmers. There is ample precedent: consider Apple's much-hyped "reinvention" of parallel programming. It might seem like manna from heaven to a thread-addled C/C++/Java programmer, but compared to even the lamest proposals for dataflow-based architectures it is the innermost circle of hell.
Persistence implemented correctly is an architectural simplification, rather than yet another proprietary knob dumped on top of a tall, stinking heap of the same. With true persistence, user and programmer alike are completely relieved of the burden of thinking about RAM volatility. It simply vanishes as a concern. Why, then, won't Mr. Jobs sell you a computer which has this marvelous property? Is it out of malice, out of a desire to watch you squirm? No: it is because he simply cannot. While Apple remains the undisputed champion of seamless architecture-hopping, moving between competing instruction sets is nothing compared to even the smallest movement between paradigms. And that is precisely what a move to orthogonal persistence would be. The UNIX loader and just about everything connected with the act of manually shuttling data between different forms of storage would have to vanish. The difference between asking developers to port code (what happened after each of Apple's three CPU swap-outs) and asking developers to junk all code ever written by anyone is, well, a serious one. Don't expect this kind of suicidal courage from Apple or from any other commercial entity. Or from any mainstream organization led by respectable people, for that matter.
All you will ever get from Apple is a "Worse Is Better", taxidermic imitation of orthogonal persistence. The same goes for the First Law. As for the others, just forget it. Apple's products shit [4] on the Fourth, Fifth, Sixth, and Seventh Laws [3] enthusiastically and malignantly. [5] And if you think that this is merely a story about the antisocial behavior of a large American company, you are not seeing the big picture. Apple's notions of how to build a personal computing environment are already finding their way into university classrooms. Not only Tetris-playing accountants, but now so-called academics are eagerly sucking them up. In the classrooms they will be taught as the best, perhaps the only reasonable notions. This is when the sun will truly set on the personal computer's potential as a civilization-level game changer.
Foundations matter. Always and forever. Regardless of domain. Even if you meticulously plug all abstraction leaks, the lowest-level concepts on which a system is built will mercilessly limit the heights to which its high-level "payload" can rise. For it is the bedrock abstractions of a system which create its overall flavor. They are the ultimate constraints on the range of thinkable thoughts for designer and user alike. Ideas which flow naturally out of the bedrock abstractions will be thought of as trivial, and will be deemed useful and necessary. Those which do not will be dismissed as impractical frills -- or will vanish from the intellectual landscape entirely. Line by line, the electronic shanty town grows. Mere difficulties harden into hard limits. The merely arduous turns into the impossible, and then finally into the unthinkable.
The typical MS Windows user may never read or write a single line of C++, C#, or any other programming language. Nevertheless, Windows is ultimately a product of the way C++ built environments; Unix, of the way C does; the Lisp Machines, of Lisp's way. This holds true not because of some yet-undiscovered law of mathematics, but rather due to the limitations of the human mind. No matter who you are, regardless of your intelligence, endurance, motivation, or other qualities, your capabilities are still finite. Thus, a "mental CPU cycle" which you spent on manual memory management (or decisions regarding static types, and other drudge work) is one which can no longer be spent on something more interesting. Any given conceptual foundation sets up a kind of current, against which those who build on that foundation swim at their peril. To continue with this analogy in reference to modern computing, what has once been a fast-moving stream has now become a high-pressure steel pipe beneath city streets. A sewer main, to be exact.
Bedrock abstraction is destiny. This cruel law of nature applies to all aspects of computing, both above and below the level of the programming language. The Von Neumann Bottleneck is known to many, and is sure to become a dinner-talk phrase among ever less intellectually-inclined programmers as Moore's Law breathes its last. But it is not the only conceptual flaw in the foundations of the modern computer. The pervasive use of synchronous logic circuits is another, far more serious one. The basic principles of asynchronous digital logic design have been known for more than half a century. Yet engineers continue to be taught only synchronous design, popular CAD tools remain incapable of asynchronous synthesis, and through the familiar vicious chicken-and-egg circle the tradition persists. On the rare occasion when someone bothers to design an asynchronous CPU, it invariably turns out to be a mindless adaptation of the tired old crippled Von Neumann paradigm, sans clock. We can do better than that. But that is a story for another time.
There are those who tell us that any choice from among theoretically-equivalent alternatives is merely a question of taste. These are the people who bring up the Strong Church-Turing Thesis in discussions of programming languages meant for use by humans. They are malicious idiots. The only punishment which could stand a chance at reforming these miscreants into decent people would be a year or two at hard labor. And not just any kind of hard labor: specifically, carrying out long division using Roman numerals. A merciful tyrant would give these wretches the option of a firing squad. Those among these criminals against mathematics who prove unrepentant in their final hours would be asked to prove the Turing-equivalence of a spoon to a shovel as they dig their graves.
The ancient Romans could not know that their number system got in the way of developing reasonably efficient methods of arithmetic calculation, and they knew nothing of the kind of technological paths (i.e. deep-water navigation) which were thus closed to them. Who knows what marvels we are denied, lacking the true computer, the true intelligence amplifier, the true labor-saver? But unlike the Romans, we have some clues regarding the ways in which our foundational concepts are lacking. Let's consider alternatives. Just... consider them. I ask here for neither respect nor money, but merely that people think. Pretend that you are doing it only for sport, if you must do so to save face. But do think.
[1] If you try to research the meaning of the phrase "orthogonal persistence", you will drown in an ocean of pseudo-intellectual babble. The original, true idea behind it was that of creating the equivalent of non-volatile RAM, and restricting all of the machine's user-accessible storage to said RAM. This is something that once stretched the bounds of the technologically-plausible, but is trivially accomplished today -- if you enthusiastically piss on backwards-compatibility.
[2] "...that this margin is too narrow to contain."
[3] Comments re: the Seven Laws which sum up to "who do you think you are, what, a Euclid, you wanker" will be silently deleted unless they contain an honest attempt at deriving a different, more perfect set of such Laws.
[4] For the idiots who would like to paint me as a paranoid lunatic, a reminder: Apple shits on the principles of sane personal computer design not out of a sadistic desire to torment users, or from a lack of awareness of said principles, but simply because doing so is immensely, irresistibly lucrative.
[5] This is not the place to summarize exactly why, but it suffices to say that Apple is
- In bed with copyright racketeers
- About as interested in building -- or even permitting to exist -- the cheap, legal, easily-user-programmable personal computer as Boeing or Airbus are in the cheap, legal, and easy-to-fly private airplane. The destruction of HyperCard alone is proof of this.
[...] http://www.loper-os.org/?p=448 [...]
This is not true, due to the upgrade problem.
It is easy to underestimate the difficulty of achieving truly orthogonal persistence, whereby we intend an orthogonal relationship to maintenance and upgrade, deprecation and service transitions, concurrency, distribution, mobility, disruption and partitioning, pluggable architecture and service discovery, security, garbage collection, and other challenging conditions.
I agree that persistence implemented correctly promises significant architectural simplification. The trick is analyzing what 'correct' actually means, then implementing it.
Your presumptions are wrong, and thus your conclusions, no matter how logically they follow from your presumptions (which I don't think they do) are also wrong.
Apple implemented this in a way that is quite elegant.
I'm guessing you're not familiar with the way Apple does things.
They do things better at the API level than they do at the UI level.
Put another way, the Mac has had orthogonal persistence for quite some time. You just didn't' recognize it when you saw it.
Dear Engineer,
> Your presumptions are wrong, and thus your conclusions... ...are also wrong.
Would you care to elaborate?
> I’m guessing you’re not familiar with the way Apple does things.
You guess wrong. I spent four months of my life -- four long months which I will never get back -- writing an iPad app. It was a truly nauseating process. Not compared to Win32 or Web app development, to be sure, but compared to what programming can be like.
> the Mac has had orthogonal persistence for quite some time. You just didn’t’ recognize it when you saw it.
Words and phrases have meanings. You don't get to re-define them at will. "Orthogonal persistence" actually means something. If I write a value into an arbitrary location in memory, proceed to cut the power, and it isn't there any more when the machine comes back up, the system does not have orthogonal persistence.
The idea behind orthogonal persistence is to avoid any programmer or user involvement in the process of marshaling bits into and out of volatile storage. That is the "orthogonal" aspect of it: no one save the original designer actually has to think about the process.
No files. No need for lead-acid battery banks. Just your data staying written after you write it, like pencil marks on a sheet of paper.
Yours,
-Stanislav
So you want the OS to periodically save the memory state of a process to disk? I don't think this is a very useful or desirable feature:
* Program failures are far more common than either power failures or OS crashes. If a program gets itself into a bad state and subsequently crashes or gets stuck in an infinite loop as a result, I could reload that bad state from disk, whereupon it would crash or hang again. Yay? I still have to save my work the traditional way, unless I want to risk going through the ugliness of sifting through a memory dump and extracting my work from it (maybe possible if it's just text - I've done that before - but probably not worth it otherwise).
* A memory dump is not something that could be loaded into a different program (or even a later version of the same program). My compiler wants to see source code as plain text, not a text editor's internal data structures. If I'm uploading an image to the Web, it's going to have to be in some common format like .gif/.jpg/.png, not an image editor's internal data structures. And so on.
* I don't want my hard disk worn out by constant writes - your phrase "If I write a value into an arbitrary location in memory, proceed to cut the power" seems to suggest you have in mind an interval of seconds, rather than minutes.
* To dump a process, it has to be paused, or else the dump will be inconsistent. Combine this with the fact that programs today use a lot of memory, and that's going to lead to a pretty awful user experience: Right now, my firefox process is at 148MB memory usage. My hard disk can write about 30MB per second. I get annoyed when a program stops responding for just 1 second, let alone 5.
Korath,
You are getting to tied up in the implemenation. how this happens is of no consequence to us. But to solve some of your questions, all data writes could be done to a non-volatile part of memory (this memory only need to be as big as the the throughput rate in bytes per second multiplied by the processing time (in seconds) it takes to write said data to more permanent storage. There are challenges there, but they are solvable. A periodic use of the processors time to save this data to "disk" would need to be employed at some point (but only to flush the NVRAM to disk to allow more NVRAM writing).
Part of this would be a divergence from the Von Neumann architechure entirely... and maybe something slightly different than Harvard too. What about a system that uses fast cache and RAM for all read operations (manipulating large datasets, but any changes are saved to a different nonvolatile area. It's totally possible, just not widely thought about.
1- Program instability is greatly increased by the absence of proper abstraction, such as orthogonal persistence. Nevertheless, you are right that programs will always be less stable than the OS, and that orthogonal persistence does increase the pressure on stability of software. Yet, solutions are by and large known: monitoring processes, killing and restarting them, using virtual machines and backups, having supervisor modes in various numbers of known working states, that you seldom invoke, even more seldom modify, etc. Not only is it not rocket science, for any case that you would have to use them when the system helped you support orthogonal persistence, you would have had to use them anyway when it didn't.
2- Indeed, orthogonal persistence means that by default people should not be using low-level languages and bit-level compatibility, unless specifically twiddling the optimized implementation of the system. This is precisely our point.
3- If you care about your precious disk writes (e.g. using SSD), then you may use compression, increase latency to persistence (batching them so that they use up fewer transactions and/or possibly compensate each other), use optimistic evaluation (assume this has already persisted, though it hasn't yet), etc. See the report on Eumel.
4- A word you might never have heard of, judging by your comment: "incremental".
The scenario I had envisioned is that a program state gets screwed up but the effect isn't apparent immediately so even if you can go back to old states you'll have to lose work to go back to a good one. Or consider memory leaks (and yes, they can still exist even with GC; whether an allocated object will ever be used again is undecidable, so all real-life GCs fudge it and delete only unreachable objects) - the longer you work on a file, the more memory the program takes up... With good old non-orthogonal persistence, usually all you have to do is save the file, restart the program, and re-open the file; problem solved.
And there's nothing "low-level" about the need to get data from one program into another. Orthogonal persistence only works if a program is a self-contained monolith such that absolutely everything you'll ever need to do in that problem domain is in there. This was already unrealistic decades ago, but now that the Internet exists it's just absurd. I need to trade files with other people who use different software; Photoshop will never be able to read a GIMP memory dump or vice versa... but, non-orthogonal persistence to the rescue, they can both read and write .png just fine.
Many uses of state is inherently volatile or can be easily regenerated - iterators, cursors, sessions, buffers, caches, memoization, connections, subscriptions.
It seems to me that OS-layer orthogonal persistence will never be very precise or efficient, because it cannot easily distinguish between state that must be persisted, state where persistence might offer performance, and state that might even be useless or harmful.
If a computer spends a lot of time persisting stuff that doesn't really need to be persistent, the computer may become slow. The persistence mechanism itself is at risk of becoming a form of bloatware.
What we want is the ability to persist just what is needed - i.e. non-regenerable state, especially state directly influenced by human input (text, forms, views, clipboards, etc.). Preferably this is achieved without polluting our apps with extra serialization and database code.
Given an appropriate language - i.e. one that can analytically distinguish between buffer-state, cache-state, and user-input histories - we could achieve orthogonal persistence very efficiently. But this would probably mean rejecting pervasively Turing-complete languages.
A sensible option is for each app to provide a set of abstract registers for the data that must be persisted. This would grant most of the benefits of orthogonal persistence, such as preserving type and structure in those registers.
Dear dmbarbour,
It would be cleaner to persist by default and specially mark volatile values as such, rather than the reverse. At the same time one would specify default values for said data.
Writes, to anything other than mechanical disk - especially to battery-backed RAM (why the hell isn't all of it?) are cheap enough.
And there are plenty of idle bus cycles, any and all of which ought to be used for letting data trickle from faster and more volatile to slower and less-volatile storage.
Yours,
-Stanislav
The 'trickle' approach can work well, but typically sacrifices precision and consistency - i.e. you might lose an arbitrary amount of information that hasn't had time to 'trickle' to the persistent storage, and you might lose part of a operation that touches multiple pages. I do not like making this sort of tradeoff.
If I constrain myself to a strong guarantee that persistent state is consistent, and place a hard real-time bound on how much information can be lost, then the performance and language integration concerns become more significant.
Even with battery-backed RAM, I would be concerned about consistency of RAM relative to L1 cache.
Also, idle bus cycles seem like the sort of resource likely to disappear on you the moment you most need them - i.e. during user-input, the programs start gathering a lot more resources and working with big frame-buffers and running GCs. This might be a scenario where a real-time guarantee is appropriate.
I believe orthogonal persistence is feasible, perhaps even simple, but doing it right takes is not casual using today's languages.
I agree that persistence-by-default is favorable, though I'd prefer the system mostly infer volatility (relative to other resources).
Hello Stanislav,
where are "we" with the well interesting loper-OS ? About 3 years from the last tagged post about it.
regards,
Aurelien
Dear Aurelien,
Here.
However, for the past year I have been working on entirely unrelated things so that I could eat.
Yours,
-Stanislav
This blog's awesome, I read every single post yesterday. Thank you!
[...] Loper OS » Why Skin-Deep Correctness — Isn’t, and Foundations Matter.All you will ever get from Apple is a “Worse Is Better“, taxidermic imitation of orthogonal persistence. [...]
I know how you feel about Clojure, but I think it has potentially brought some good ideas to this space. Particularly, the idea of favoring pure functions wherever possible and using explicit reference types with STM.
Orthogonal persistence would be pretty easy with a system that handles state like that.