Tuesday, October 06, 2009

An Image Problem

Imagine a library, say in Java, that provided a universal facility for saving the exact runtime state and configuration of any application. It would essentially capture the heap, all threads and their stacks, and save them to a file.

You could then load this file from disk and reconstitute your application just as it was when it was saved: For example, windows would re-open just where they were (adjusting their location if the screen size differed). The system would cope with open file handles and sockets in a reasonable fashion, re-opening them if they were available. All this would work irrespective of the underlying platform, and it would be fast as well.

The facility would be transparent to the programmer - you wouldn’t need to change your program in any way, beyond calling the library function. Pretty neat.

I don’t know of such a facility in Java or .Net. However, Smalltalk has had such a beast for over 30 years. In Smalltalk parlance, it’s called an image. Most Smalltalks are inseparably coupled to the notion of such an image.

To be sure, the image is a very powerful mechanism, and has significant advantages. For example, in the context of the IDE, it gives the ability to save and share debugging sessions.

One advantage of images is rapid start-up. If your program builds up a lot of state as it starts, it can be intolerably slow coming up. This is one the problems that killed Java on the client. One should avoid computing that state at startup; one easy way to do this is to precompute most of it and store it in an image. It is typically much faster to read in an image than to try and compute its contents from scratch.

The problem begins when one relies exclusively on the image mechanism. It becomes very difficult to disentangle one’s program from the state of the ongoing computation.

If your program misbehaves (and they always do) and corrupts the state of the process, things get tedious. Say you have a test that somehow failed to clean up properly. In a conventional setting, you fix the problem with your code, and run the test again. With an image, you have the added burden of cleaning up the mess your program made.

In some cases it is even more critical to separate your code from the computation. You often save your image just to save your program. It may take you a while to find out that your image has been corrupted. Now you need to go back to a correct image, and yet you need to extract your code safely from the corrupt image. To be sure, Smalltalk IDEs provide a variety of tools that can help you with that, but I have never been really happy with them.

Tangent: this is where irate Smalltalkers berate me about change sets and logs and Envy and Monticello etc. etc. Sorry, I don’t think it’s good enough.

In general, Smalltalk makes it hard to modularize one's code, and especially to separate the application from the IDE. The exclusive reliance on the image model greatly aggravates these difficulties.

Traditional development tools, primitive as they often are, naturally provide a persistent, stateless representation of the program. In fact they provide two: the source code, in a text file, and a binary.

Semi-tangent: source code seems the most obvious thing in the world; but traditional Smalltalk’s have no real syntax above the method level! Classes are defined via the evaluation of reflective expressions, which rely on the reflective API. This is very problematic: the API often varies from one implementation to another. By the way, this is one of the ways Newspeak differs from almost every Smalltalk (the late, great Resilient being the only exception I can recall). Newspeak has a true syntax. Furthermore, because Newspeak module declarations are fully parametric in all their external dependencies, they can be compiled at any time in any order - unlike code in most languages (say Java packages) where there are numerous constraints on compilation order (e.g., imports must be defined).

A binary is a stateless representation of the program code, but one that does not require a compiler to decode it. Smalltalk doesn’t usually have a binary form. Code is embedded in the image. There are exceptions, and some Smalltalk flavors have ways of producing executables, but the classic approach ties code and computation together in the image and makes it very hard to pry them apart.

None of this means you can’t have an image as well as a binary format. What is important is that you do not have just an image. Ideally, you have images and a binary format. This is one of my goals with Newspeak, and we are pretty close.

In Newspeak, serialized top level classes can serve as a binary format. I will expand on how serialization can serve as a binary format in an upcoming post. At the same time, we continue to use images, though I hope they will become much less central to our practice as time goes by.

Clamato is a Smalltalk that has "real syntax" and an embodiment in text files. (Gnu Smalltalk also, I think). Of course, Clamato usually is operating without an image, but it does run nicely on V8 which has a snapshot facility.

I think images are potentially dangerous while developing, for the reasons you state - having the IDE in the same process as the running program is great for a few hours at a time (fix and continue etc), but in general you should be committing source code, not saving your image; rebuild the image frequently from source instead. People tend to conflate these two aspects of the Smalltalk environment, but they're really quite separate (and Clamato, for example, generally has the live development but not the image).

On the other hand, they're great for deployment: uploading a single, carefully built image to a production server is a lot nicer than updating tens or hundreds of source files (a Java .war file isn't bad either, but that's not the world I usually play in).

I think serialization of program state is great and it should be in all languages.

I'm interested in what you think of the following problem:

Suppose you have a program P and somewhere during the computation it is saved to state S.

However, what happens if you change your program P to P+1? Suppose you find a bug, and you add another line of code somewhere. If you now reopen your state S, should it be interpreted in the old way? Or in the new way? This is a problem that always seems to come up, and I haven't found a good solution to deal with it.

Yes, I agree with Avi (I am the GNU Smalltalk maintainer). The image can be useful to deploy a system (after all it is just like a statically linked binary), and it can be used to leave aside the work you're doing if you're not ready to commit it.

But it is difficult to manage during development for various reasons. It tends to amplify the presence of bugs in the IDE, and it is too easy to forget an initialization and to find out too late, spending hours debugging just before deployment.

This is luckily a less common scenario nowadays that tools like Store or Monticello are in wide use, so that you can rebuild the image frequently. But an environment like GNU Smalltalk would encourage this even more, because rebuilding an image is literally one command away (like "gst -I my-image.im -i")---I say would because to close the circle gst would need better tools to integrate version control (svn/git/CVS) with the IDE.

This has parallels with the development of relational databases. In most RDBMSs, the schema, constraints, encapsulated logic (which amount to system rules) and the data (which is basically state information) all coexist in one place - the database. DB developers can mutate the schema and the logic even as the state in the database's tables is changing. A backup of the database is analagous to an image.

The challenges of controlling database development - rigorously scripting out schema changes and encoding the changes that are needed in database state to accompany logic or schema alterations - seem similar.

Try googling - this has existed for Java for years (in various research projects). The feature you want to look for is called orthogonal persistence. For example PJama allows you to pause a program and then later restart it - restoring threads, objects, etc. as they were at the time of pause.

Thanks to all who commented. I am largely in agreement. I'm not familiar with Clamato - I'll take a look. And, yes, I should have noted that V8 supports snapshots.

Paolo: Your comment on source control resonates. We've had some integration with svn in Newspeak for a while, and are moving toward mercurial. Overall, source control is also a problematic area, and I intend to post on it soon as well.

I am well aware of orthogonal persistence; it is true that the image can be viewed as a form of that. But OP is more general, and lets you control what subset of the system gets persisted (by determining what roots are persistent). As such, your program may need to change. This is one reason why PJama isn't exactly an image based system.

I am familiar with PJama, and know several people who worked on it. It was research project and was never deployed in production by Sun (not entirely the researcher's fault - Sun management had a gift for recognizing cool technology).

So when you say "this feature has existed in Java for years" - it's not the same feature, and we have very different ideas of what "exists" means.

There have in fact been other research efforts that provided an image for Java. But nothing that a developer can actually utilize in practice.

The JVM specification simply does not provide the necessary primitives to implement this service as a library. You have to get into the VM implementation to do it, and might easily run awry of the Java test suites if you did (Sun historically did not allow super-setting). So it hasn't been possible for commercial versions to be marketed.

It took me quite a long time before I realized that it's better to use one image for only one project and treat the image something like an art work that my work will be continued on for some time. It's not for source management it's for play with things I'm making. So I learned to have a copy of it when I'm to do something uncertain. It's useful to save a lot of living things. So it can be useful in net-age, say an image for a profile on the server.

Very interesting, as usual. On a "tangent" note, there is a trend to add full class syntax to Smalltalks. In additions to those already noted here, I know about one for F-Script (see example here), one for GNU Smalltalk (example) and one in development for Pharo (called Coral, see example).

Persisting data without the code that created it is problematic. In OO, they naturally go together: you save an object, you save its class with it (this is partly why Java serialization is broken).

The problem you describe should not arise wrt persistence, but can arise, say, when you modify a live program. The state data is now associated with code that differs from the code that created it. What does such a program mean? It's a deep issue. I don't know of a good answer.

Thans for the pointers. Of course, each of these syntaxes is different. The absence of a standard syntax is a problem, and introducing different syntxaxes in different dialects has the potential to make things worse.

F-Script, like Newspeak, is not exactly Smalltalk, so having a unique syntax for it is understandable.

The GNU syntax is pretty reasonable.Why Pharo needs to re-invent this is beyond me; but then, the rationale for Pharo as a whole eludes me as well.

What are your thoughts on OODBs, in the case where all you have is an image which contains your data/behaviour and is performing computation? You have an scm tool for code which you can save out to, but largely you can't scrap your image and restart since all of your data (objects) is contained in it.

The image is same idea now realized by VMs (vmware and others) but different scale. Today, VMs are base technology for deploy, virtualization, application migration …It always wonders me – how Smalltalk invented these models and ideas but in the mainstream they go by Oss (different scale)

And today's virtualization technologies extend some kind of version control over these image snapshots. In VMWare workstation you can create and name snapshots of a VM. You can revert to any snapshot and if you like you can fork the snapshot tree. It's also possible to delete intermediate snapshots.

I don't know of any lisp or smalltalk image management tools that can do that (and efficiently store the differences between snapshots).

Very good points. Especially (3) - but there remains the question of whether all state is persistent, or some is transient. The experience of orthogonal persistence points toward the latter. So what you want is orthogonal synchronization - the persistent subset of the heap gets sync'ed with the server.

Of course "images" can be shared live - witness Croquet. Google's Wave allows for a limited version of this, which is why I think it is very significant.

On your last point: Modeling evolution over time via inheritance has intrigued me for many years. On the other hand, maybe it is never exposed, and the diffs are just an implementation issue, as in a version control system.