How does differential execution work?
Asked Answered
K

4

87

I've seen a few mentions of this on Stack Overflow, but staring at Wikipedia (the relevant page has since been deleted) and at an MFC dynamic dialog demo did nothing to enlighten me. Can someone please explain this? Learning a fundamentally different concept sounds nice.


Based on the answers: I think I'm getting a better feel for it. I guess I just didn't look at the source code carefully enough the first time. I have mixed feelings about differential execution at this point. On the one hand, it can make certain tasks considerably easier. On the other hand, getting it up and running (that is, setting it up in your language of choice) is not easy (I'm sure it would be if I understood it better)...though I guess the toolbox for it need only be made once, then expanded as necessary. I think in order to really understand it, I'll probably need to try implementing it in another language.

Kokanee answered 16/12, 2008 at 16:49 Comment(21)
Thanks for your interest Brian. To me, it is interesting that something simple seems disappointing. To me, the prettiest things are simple. Take care.Varix
I think I'm missing something important. Right now I'm thinking, "this is simple." If I really understood it, I think I'd be thinking, "This is simple. And really amazing and useful."Kokanee
I've done it so many times in so many languages that I gloss over the hard lessons I've learned along the way, like the importance of the P macro, and of DD_THROW. But when you get the hang of it, you can do so much it's addictive.Varix
... I still see people presenting MVC like it's the greatest thing, and I think I'd rather retire than have to do that again.Varix
In answer to your edit, I understand your mixed feelings, because it's a different way of thinking, and doing a tiny example yourself may be the best way to understand it. You're right, the toolbox need only be made once and expanded as necessary.Varix
I gave a second answer that gives a toy implementation thay you can step through. (I tried the code, so it works.)Varix
@Mike Dunlavey: is DE primarily intended as an alternative to MVC for data-presentation and manipulation? Reading your wiki and your entries here, I can't tell if DE applies to other uses. Thanks.Reiners
@Toybuilder: Dynamic Dialogs is an alternative to MVC, and it is one (the main) application of DE. I have also used DE as a way to detect changes in a small database, that does not rely on catching change events. I've also tried it for a general "undo" - by compressing the FIFO.Varix
... for "undo" you serialize/deserialize the data, and spin off a file which is the XOR of the two, which is mostly zero so easily compressed. Use that to recover the prior data. Now generalize to arbitrary data structure.Varix
@MikeDunlavey, please share more of your examples of this! I mean in the sense of library or source code. Also I noted your MFC Dynamic Dialog demo is GPL, which of course is your choice to make, but it's kind of inconvenient for me. :)Tadzhik
@Prof.Falken: I'm really ignorant about licenses. If you can suggest a better one, and maybe how to change it from what it is, I'll be glad to. As for examples, I have to think of what I can share. I did a personal app in C++ for closeout of charity auctions. It does a lot of this. Maybe that would be a good one.Varix
@Prof.Falken: Maybe you've already seen these, but there are a couple of videos on my YouTube.Varix
@MikeDunlavey, that app sounds great, 'cause it's doing something real. A really simple license both to understand and to use in all kinds of applications including for-profit-closed-source-proprietary is the MIT license: opensource.org/licenses/MIT (GPLv2 is also good in that it is easy to understand but limits what you can do with the code.) If you are the only substantial author, you can just change the license at any time to MIT or any other license - you are the copyright holder. Did not see the videos, will check them out later.Tadzhik
@Prof.Falken: OK, I'm trying to do this. They gave me a new W7 64-bit machine, and upgraded me to VS 2013, so I had to re-install MySQL and I'm struggling to rebuild the app. I'll try to get it done today, and then post it on SF. Thanks for your interest.Varix
Don't want to add to your workload, @MikeDunlavey, but if you missed it, Source Forge fell from grace with questionable biz practices. Github.com is where the cool kids hang nowadays. They have a really nice Windows client for W7 at desktop.github.comTadzhik
@Prof.Falken: I just made my first posting to github, under my name and AucUI. Hope I did it right :)Varix
@MikeDunlavey, looks good to me! I starred you. :) You might want to add a LICENSE.txtTadzhik
@Prof.Falken: There's yet another explanation here.Varix
@Prof.Falken: Any luck with that? I hunted for your email to ask you that, but got the wrong person.Varix
@MikeDunlavey I got kind of stuck with COM+ problems in the mean time. :)Tadzhik
@Prof.Falken: There's an initial minimal implementation in Javascript in GitHub, under my name, and called DiffExeInJS.Varix
V
98

Gee, Brian, I wish I had seen your question sooner. Since it's pretty much my "invention" (for better or worse), I might be able to help.

Inserted: The shortest possible explanation I can make is that if normal execution is like throwing a ball in the air and catching it, then differential execution is like juggling.

@windfinder's explanation is different from mine, and that's OK. This technique is not easy to wrap one's head around, and it's taken me some 20 years (off and on) to find explanations that work. Let me give it another shot here:

  • What is it?

We all understand the simple idea of a computer stepping along through a program, taking conditional branches based on the input data, and doing things. (Assume we are dealing only with simple structured goto-less, return-less code.) That code contains sequences of statements, basic structured conditionals, simple loops, and subroutine calls. (Forget about functions returning values for now.)

Now imagine two computers executing that same code in lock-step with each other, and able to compare notes. Computer 1 runs with input data A, and Computer 2 runs with input data B. They run step-by-step side by side. If they come to a conditional statement like IF(test) .... ENDIF, and if they have a difference of opinion on whether the test is true, then the one who says the test if false skips to the ENDIF and waits around for its sister to catch up. (This is why the code is structured, so we know the sister will eventually get to the ENDIF.)

Since the two computers can talk to each other, they can compare notes and give a detailed explanation of how the two sets of input data, and execution histories, are different.

Of course, in differential execution (DE) it is done with one computer, simulating two.

NOW, suppose you only have one set of input data, but you want to see how it has changed from time 1 to time 2. Suppose the program you're executing is a serializer/deserializer. As you execute, you both serialize (write out) the current data and deserialize (read in) the past data (which was written the last time you did this). Now you can easily see what the differences are between what the data was last time, and what it is this time.

The file you are writing to, and the old file you are reading from, taken together constitute a queue or FIFO (first-in-first-out), but that's not a very deep concept.

  • What is it good for?

It occurred to me while I was working on a graphics project, where the user could construct little display-processor routines called "symbols" that could be assembled into larger routines to paint things like diagrams of pipes, tanks, valves, stuff like that. We wanted to have the diagrams be "dynamic" in the sense that they could incrementally update themselves without having to redraw the entire diagram. (The hardware was slow by today's standards.) I realized that (for example) a routine to draw a bar of a bar-chart could remember its old height and just incrementally update itself.

This sounds like OOP, doesn't it? However, rather than "make" an "object", I could take advantage of the predictability of the execution sequence of the diagram procedure. I could write the bar's height in a sequential byte-stream. Then to update the image, I could just run the procedure in a mode where it sequentially reads its old parameters while it writes the new parameters so as to be ready for the next update pass.

This seems stupidly obvious and would seem to break as soon as the procedure contains a conditional, because then the new stream and the old stream would get out of sync. But then it dawned on me that if they also serialized the boolean value of the conditional test, they could get back in sync. It took a while to convince myself, and then to prove, that this would always work, provided a simple rule (the "erase mode rule") is followed.

The net result is that the user could design these "dynamic symbols" and assemble them into larger diagrams, without ever having to worry about how they would dynamically update, no matter how complex or structurally variable the display would be.

In those days, I did have to worry about interference between visual objects, so that erasing one would not damage others. However, now I use the technique with Windows controls, and I let Windows take care of rendering issues.

So what does it achieve? It means I can build a dialog by writing a procedure to paint the controls, and I do not have to worry about actually remembering the control objects or dealing with incrementally updating them, or making them appear/disappear/move as conditions warrant. The result is much smaller and simpler dialog source code, by about an order of magnitude, and things like dynamic layout or altering the number of controls or having arrays or grids of controls are trivial. In addition, a control such as an Edit field can be trivially bound to the application data it is editing, and it will always be provably correct, and I never have to deal with its events. Putting in an edit field for an application string variable is a one-line edit.

  • Why is it hard to understand?

What I have found hardest to explain is that it requires thinking differently about software. Programmers are so firmly wedded to the object-action view of software that they want to know what are the objects, what are the classes, how do they "build" the display, and how do they handle the events, that it takes a cherry bomb to blast them out of it. What I try to convey is that what really matters is what do you need to say? Imagine you are building a domain-specific language (DSL) where all you need to do is tell it "I want to edit variable A here, variable B there, and variable C down there" and it would magically take care of it for you. For example, in Win32 there is this "resource language" for defining dialogs. It is a perfectly good DSL, except it doesn't go far enough. It doesn't "live in" the main procedural language, or handle events for you, or contain loops/conditionals/subroutines. But it means well, and Dynamic Dialogs tries to finish the job.

So, the different mode of thinking is: to write a program, you first find (or invent) an appropriate DSL, and code as much of your program in that as possible. Let it deal with all the objects and actions that only exist for implementation's sake.

If you want to really understand differential execution and use it, there are a couple of tricky issues that can trip you up. I once coded it in Lisp macros, where these tricky bits could be handled for you, but in "normal" languages it requires some programmer discipline to avoid the pitfalls.

Sorry to be so long-winded. If I haven't made sense, I'd appreciate it if you'd point it out and I can try and fix it.

Added:

In Java Swing, there is an example program called TextInputDemo. It is a static dialog, taking 270 lines (not counting the list of 50 states). In Dynamic Dialogs (in MFC) it is about 60 lines:

#define NSTATE (sizeof(states)/sizeof(states[0]))
CString sStreet;
CString sCity;
int iState;
CString sZip;
CString sWholeAddress;

void SetAddress(){
    CString sTemp = states[iState];
    int len = sTemp.GetLength();
    sWholeAddress.Format("%s\r\n%s %s %s", sStreet, sCity, sTemp.Mid(len-3, 2), sZip);
}

void ClearAddress(){
    sWholeAddress = sStreet = sCity = sZip = "";
}

void CDDDemoDlg::deContentsTextInputDemo(){
    int gy0 = P(gy);
    P(www = Width()*2/3);
    deStartHorizontal();
    deStatic(100, 20, "Street Address:");
    deEdit(www - 100, 20, &sStreet);
    deEndHorizontal(20);
    deStartHorizontal();
    deStatic(100, 20, "City:");
    deEdit(www - 100, 20, &sCity);
    deEndHorizontal(20);
    deStartHorizontal();
    deStatic(100, 20, "State:");
    deStatic(www - 100 - 20 - 20, 20, states[iState]);
    if (deButton(20, 20, "<")){
        iState = (iState+NSTATE - 1) % NSTATE;
        DD_THROW;
    }
    if (deButton(20, 20, ">")){
        iState = (iState+NSTATE + 1) % NSTATE;
        DD_THROW;
    }
    deEndHorizontal(20);
    deStartHorizontal();
    deStatic(100, 20, "Zip:");
    deEdit(www - 100, 20, &sZip);
    deEndHorizontal(20);
    deStartHorizontal();
    P(gx += 100);
    if (deButton((www-100)/2, 20, "Set Address")){
        SetAddress();
        DD_THROW;
    }
    if (deButton((www-100)/2, 20, "Clear Address")){
        ClearAddress();
        DD_THROW;
    }
    deEndHorizontal(20);
    P((gx = www, gy = gy0));
    deStatic(P(Width() - gx), 20*5, (sWholeAddress != "" ? sWholeAddress : "No address set."));
}

Added:

Here's example code to edit an array of hospital patients in about 40 lines of code. Lines 1-6 define the "database". Lines 10-23 define the overall contents of the UI. Lines 30-48 define the controls for editing a single patient's record. Note the form of the program takes almost no notice of events in time, as if all it had to do was create the display once. Then, if subjects are added or removed or other structural changes take place, it is simply re-executed, as if it were being re-created from scratch, except that DE causes incremental update to take place instead. The advantage is that you the programmer do not have to give any attention or write any code to make the incremental updates of the UI happen, and they are guaranteed correct. It might seem that this re-execution would be a performance problem, but it is not, since updating controls that do not need to be changed takes on the order of tens of nanoseconds.

1  class Patient {public:
2    String name;
3    double age;
4    bool smoker; // smoker only relevant if age >= 50
5  };
6  vector< Patient* > patients;

10 void deContents(){ int i;
11   // First, have a label
12   deLabel(200, 20, “Patient name, age, smoker:”);
13   // For each patient, have a row of controls
14   FOR(i=0, i<patients.Count(), i++)
15     deEditOnePatient( P( patients[i] ) );
16   END
17   // Have a button to add a patient
18   if (deButton(50, 20, “Add”)){
19     // When the button is clicked add the patient
20     patients.Add(new Patient);
21     DD_THROW;
22   }
23 }

30 void deEditOnePatient(Patient* p){
31   // Determine field widths
32   int w = (Width()-50)/3;
33   // Controls are laid out horizontally
34   deStartHorizontal();
35     // Have a button to remove this patient
36     if (deButton(50, 20, “Remove”)){
37       patients.Remove(p);
37       DD_THROW;
39     }
40     // Edit fields for name and age
41     deEdit(w, 20, P(&p->name));
42     deEdit(w, 20, P(&p->age));
43     // If age >= 50 have a checkbox for smoker boolean
44     IF(p->age >= 50)
45       deCheckBox(w, 20, “Smoker?”, P(&p->smoker));
46     END
47   deEndHorizontal(20);
48 }

Added: Brian asked a good question, and I thought the answer belonged in the main text here:

@Mike: I'm not clear on what the "if (deButton(50, 20, “Add”)){" statement is actually doing. What does the deButton function do? Also, are your FOR/END loops using some sort of macro or something? – Brian.

@Brian: Yes, the FOR/END and IF statements are macros. The SourceForge project has a complete implementation. deButton maintains a button control. When any user input action takes place, the code is run in "control event" mode, in which deButton detects that it was pressed and signifies that it was pressed by returning TRUE. Thus, the "if(deButton(...)){... action code ...} is a way of attaching action code to the button, without having to create a closure or write an event handler. The DD_THROW is a way of terminating the pass when the action is taken because the action may have modified application data, so it is invalid to continue the "control event" pass through the routine. If you compare this to writing event handlers, it saves you writing those, and it lets you have any number of controls.

Added: Sorry, I should explain what I mean by the word "maintains". When the procedure is first executed (in SHOW mode), deButton creates a button control and remembers its id in the FIFO. On subsequent passes (in UPDATE mode), deButton gets the id from the FIFO, modifies it if necessary, and puts it back in the FIFO. In ERASE mode, it reads it from the FIFO, destroys it, and does not put it back, thereby "garbage collecting" it. So the deButton call manages the entire lifetime of the control, keeping it in agreement with application data, which is why I say it "maintains" it.

The fourth mode is EVENT (or CONTROL). When the user types a character or clicks a button, that event is caught and recorded, and then the deContents procedure is executed in EVENT mode. deButton gets the id of its button control from the FIFO and askes if this is the control that was clicked. If it was, it returns TRUE so the action code can be executed. If not, it just returns FALSE. On the other hand, deEdit(..., &myStringVar) detects if the event was meant for it, and if so passes it to the edit control, and then copies the contents of the edit control to myStringVar. Between this and normal UPDATE processing, myStringVar always equals the contents of the edit control. That is how "binding" is done. The same idea applies to scroll bars, list boxes, combo boxes, any kind of control that lets you edit application data.

Here's a link to my Wikipedia edit: http://en.wikipedia.org/wiki/User:MikeDunlavey/Difex_Article

Varix answered 16/12, 2008 at 16:50 Comment(22)
I'm having trouble with the last paragraph of "What is it good for" ("so what does it achieve..."). I'm understanding what it is saying but lacking the ephiphany moment where it all comes together. Maybe a pair of code samples with and without it would be clearer?Kokanee
I'll see if I can work something up. In the meantime, the dynamic dialog demo gives an example of a structurally varying UI. For a simpler static dialog, in DD it is 5 times less code than in Java Swing.Varix
@Mike do you have any references that you can add here: papers, projects, discussions...Adjoint
@Mike: I'm not clear on what the "if (deButton(50, 20, “Add”)){" statement is actually doing. What does the deButton function do? Also, are your FOR/END loops using some sort of macro or something?Kokanee
@Brian: Good question, and I put my answer in the main text above.Varix
You should really link these people to the paper! It's very easy to understand the way you explain it there. It's all about modularization and temporal granularity. Flight control is an example, where you have to update huge displays with huge resolutions accurately and quickly, but redrawing it all is a problem. You don't want to code-up monitor-specific differentials (only update changing pixels), instead you want a solution that is expandable to the general problem - a system-independent way of updating fragments of the datastream that are changing over time - 2009.. I'm a bit late sorryUnchancy
I think this is fascinating as it feels almost like pipelining at the language-level. I think this is a very important concept and points to the absolute question of: what do you want it to do in the ideal case and how can we get there without changing hardware? Since if we knew the exact states in their exact order we can just run our machine [timed] and everything is perfect, but how do we make it just-as-fast or really close to just-as-fast without knowing the future states? Without wasting time on "objects" since most of that "data" is discarded/unnecessary in the drawing process.Unchancy
And sorry to plague you with answers but if I understand this correctly, you are basically moving your calculations closer and closer to the processor and away from the output hardware. This is an incredible insight as we invest a lot of stock into the idea that by programming in objects and variables, they are pretty easily translated into the best machine code to achieve the same output, which is certainly not at all the case! Although we can optimize code when compiling, it's not possible to optimize time-dependent actions. Refuse time-dependence and make primitives do the work!Unchancy
@sova: When I first stumbled on this, the benefit was obvious to me, but not to anyone else. It took me a few years to understand what it was about, in ways that I could explain (and publish). But the thinking you are going through sounds very much like what I did.Varix
@Mike, have you worked with WPF at all? This concept sounds like databinding, where change notifications are observed 'under the hood' instead of defining observations in each implementation. Action binding is also supported. (I'm aware many technologies have binding capabilities). Am I way off the mark?Progression
@Josh: I haven't gotten into WPF, but as you say databinding is not new with WPF. Basically, I've learned to shun notification-style programming, where there are redundant parallel data structures (the control, the data, the gadget that sits between them) that try to keep updated with tight message-passing. What I try to do here is have as little data structure as possible, and tolerate inconsistency until an update pass resolves it. That way, structural modifications to the data are much easier to manage.Varix
@Josh: ... Also with action binding, I think what matters is the form of the source code. I like the code that handles the event to be written in direct proximity to the code that maintains the control, not scattered about the class, so that it doesn't have to be named & have to know how to access the relevant data. Now in some languages there are closures / lambda expressions to do this, but still I think this method is cleaner at capturing context, and it doesn't create more data structure.Varix
Is differential execution in any way related to coroutines? If I'm not mistaken, coroutines can be thought of as threads, but only one thread runs at a time (removing the major hassles of multithreaded programming, namely deadlocking and nondeterministic behavior). This means you can have a producer (e.g. a for loop grabbing characters from a file) and a consumer (e.g. a for loop counting the characters it's getting) running lock-step. Since each thread gets its own stack, you don't have to graft all your control structures into one big ugly loop.Tubb
@Joey: Not really, but I certainly had early fascination with co-routines, so maybe it was an unconscious inspiration. Those are good to think about. My mental images are more like a knitting machine, or like a zipper that can get out of register, or like a crystal with dislocations, or gears with missing teeth. (I was a mech. engr.)Varix
@Joey: Now that you mention it, the idea of a control structure that runs off a FIFO, and parallel co-routines running off a job queue, there is a lot in common there.Varix
@IntermediateHacker: Sorry about that. To me it's super-simple, but when I try to explain it, it just keeps going.Varix
I wonder how close Differential Execution is to the approach used by the react.js library.Kokanee
@Brian: From skimming the info, react.js uses a diff function to send incremental updates to the browser. I can't tell if the diff function is actually as capable as differential execution. Like can it handle arbitrary changes, and it claims to simplify binding. Whether it's done it to the same degree I don't know. Anyway, I think it's on the right track. Couple videos here.Varix
So this basically allows you to make an immediate-mode GUI API that controls a retained-mode GUI API. Pretty cool. GUIs are much easier to write over an immediate-mode API (as you said it yourself) given that they follow the natural flow of code and you don't have to manage widget handles.Vicenta
@cap: I'm not familiar with the terms. I like to think a DSL means: if the user doesn't need to care, neither should the programmer. As soon as a program has to "manage" anything, it's not working in the actual domain. So if the user couldn't care less about handles, scroll bar states, data binding, etc, then chances are there is a DSL where the programmer doesn't have to deal with them either.Varix
@Cthutu: I wasn't aware of IMGUI. Thanks. It has some of the same motivations - reducing lines of code by a big factor, making it easy to dynamically change the UI. IMHO, things have to move in that direction. Differences seem to be that IMGUI likes a high-bandwidth display, where DE was originally built for low-bandwidth displays. Also, DE is language-agnostic, first done in C. FWIW, I've got a couple videos about it on my UTube channelVarix
@MikeDunlavey, I write my tools with a combination of OpenGL/IMGUI and reactive programming over Model, Model-View and View layers. I will never go back to the old style now. Thanks for the links of your videos.Crackbrained
C
13

Differential execution is a strategy for changing the flow of your code based on external events. This is usually done by manipulating a data structure of some kind to chronicle the changes. This is mostly used in graphical user interfaces, but is also used for things like serialization, where you are merging changes into an existing "state."

The basic flow is as follows:

Start loop:
for each element in the datastructure: 
    if element has changed from oldDatastructure:
        copy element from datastructure to oldDatastructure
        execute corresponding subroutine (display the new button in your GUI, for example)
End loop:
Allow the states of the datastructure to change (such as having the user do some input in the GUI)

The advantages of this are a few. One, it is separation of the execution of your changes, and the actual manipulation of the supporting data. Which is nice for multiple processors. Two, it provides a low bandwidth method of communicating changes in your program.

Cupronickel answered 19/12, 2008 at 18:53 Comment(0)
U
12

Think of how a monitor works:

It is updated at 60 Hz -- 60 times a second. Flicker flicker flicker 60 times, but your eyes are slow and can't really tell. The monitor shows whatever is in the output buffer; it just drags this data out every 1/60th of a second no matter what you do.

Now why would you want your program to update the whole buffer 60 times a second if the image shouldn't change that often? What if you only change one pixel of the image, should you rewrite the entire buffer?


This is an abstraction of the basic idea: you want to change the output buffer based on what information you want displayed on the screen. You want to save as much CPU time and buffer write time as possible, so you don't edit parts of the buffer that need not be changed for the next screen pull.

The monitor is separate from your computer and logic (programs). It reads from the output buffer at whatever rate it updates the screen. We want our computer to stop synchronizing and redrawing unnecessarily. We can solve this by changing how we work with the buffer, which can be done in a variety of ways. His technique implements a FIFO queue that is on delay -- it holds what we just sent to the buffer. The delayed FIFO queue does not hold pixel data, it holds "shape primitives" (which might be pixels in your application, but it could also be lines, rectangles, easy-to-draw things because they are just shapes, no unnecessary data is allowed).

So you want to draw/erase things from the screen? No problem. Based on the contents of the FIFO queue I know what the monitor looks like at the moment. I compare my desired output (to erase or draw new primitives) with the FIFO queue and only change values that need to be changed/updated. This is the step which gives it the name Differential Evaluation.

Two distinct ways in which I appreciate this:

The First: Mike Dunlavey uses a conditional-statement extension. The FIFO queue contains a lot of information (the "previous state" or the current stuff on monitor or time-based polling device). All you have to add to this is the state you want to appear on screen next.

A conditional bit is added to every slot that can hold a primitive in the FIFO queue.

0 means erase
1 means draw

However, we have previous state:

Was 0, now 0: don't do anything;
Was 0, now 1: add it to the buffer (draw it);
Was 1, now 1: don't do anything;
Was 1, now 0: erase it from the buffer (erase it from the screen);

This is elegant, because when you update something you really only need to know what primitives you want to draw to the screen -- this comparison will find out if it should erase a primitive or add/keep it to/in the buffer.

The Second: This is just one example, and I think that what Mike is really getting at is something that should be fundamental in design for all projects: Reduce the (computational) complexity of design by writing your most computationally intense operations as computerbrain-food or as close as you can get. Respect the natural timing of devices.

A redraw method to draw the entire screen is incredibly costly, and there are other applications where this insight is incredibly valuable.

We are never "moving" objects around the screen. "Moving" is a costly operation if we are going to mimic the physical action of "moving" when we design code for something like a computer monitor. Instead, objects basically just flicker on and off with the monitor. Every time an object moves, it's now a new set of primitives and the old set of primitives flickers off.

Every time the monitor pulls from the buffer we have entries that look like

Draw bit    primitive_description
0           Rect(0,0,5,5);
1           Circ(0,0,2);
1           Line(0,1,2,5);

Never does an object interact with the screen (or time-sensitive polling device). We can handle it more intelligently than an object will when it greedily asks to update the whole screen just to show a change specific to only itself.

Say we have a list of all possible graphical primitives our program is capable of generating, and that we tie each primitive to a set of conditional statements

if (iWantGreenCircle && iWantBigCircle && iWantOutlineOnMyCircle) ...

Of course, this is an abstraction and, really, the set of conditionals that represents a particular primitive being on/off could be large (perhaps hundreds of flags that must all evaluate to true).

If we run the program, we can draw to the screen at essentially the same rate at which we can evaluate all these conditionals. (Worst case: how long it takes to evaluate the largest set of conditional statements.)

Now, for any state in the program, we can simply evaluate all the conditionals and output to the screen lightning-quick! (We know our shape primitives and their dependent if-statements.)

This would be like buying a graphically-intense game. Only instead of installing it to your HDD and running it through your processor, you buy a brand-new board that holds the entirety of the game and takes as input: mouse, keyboard, and takes as output: monitor. Incredibly condensed conditional evaluation (as the most fundamental form of a conditional is logic gates on circuit boards). This would, naturally, be very responsive, but it offers almost no support in fixing bugs, as the whole board design changes when you make a tiny design change (because the "design" is so far-removed from the nature of the circuit board). At the expense of flexibility and clarity in how we represent data internally we have gained significant "responsiveness" because we are no longer doing "thinking" in the computer; it is all just reflex for the circuit board based on the inputs.

The lesson, as I understand it, is to divide labor such that you give each part of the system (not necessarily just computer and monitor) something it can do well. The "computer thinking" can be done in terms of concepts like objects... The computer brain will gladly try and think this all through for you, but you can simplify the task a great deal if you are able to let the computer think in terms of data_update and conditional_evals. Our human abstractions of concepts into code are idealistic, and in the case of internal program draw methods a little overly idealistic. When all you want is a result (array of pixels with correct color values) and you have a machine that can easily spit out an array that big every 1/60th of a second, try and eliminate as much flowery thinking from the computer brain as possible so that you can focus on what you really want: to synchronize your graphical updates with your (fast) inputs and the natural behavior of the monitor.

How does this map to other applications? I'd like to hear of other examples, but I'm sure there are many. I think anything that provides a real-time "window" into the state of your information (variable state or something like a database... a monitor is just a window into your display buffer) can benefit from these insights.

Unchancy answered 10/11, 2010 at 11:8 Comment(5)
++ I appreciate your take on it. For me, initially it was an attempt to do program-described displays on slow devices (think 9600-baud remote text terminals), where it would basically do an automated diff & transmit minimal updates. Then I was pressed on why not just code this by brute force. The answer: because if the surface form of the code is as if it's a simple paint, it's shorter, nearly bug-less, therefore done in a fraction of the development time. (That's what I think of as the benefit of a DSL.)Varix
... The development effort that is freed up can then be re-invested in more sophisticated and dynamic displays, that users find responsive and pleasing. So you get more UI bang for the developer buck.Varix
... Example: this app from about 10 years ago: pharsight.com/products/prod_pts_using_dme.phpVarix
This made me understand ... when you talked about computer games. Actually, many games are coded like how Mike does user interface. An update list which is cycled through with every frame.Tadzhik
A seemingly related example to some of what you said is detecting whether a key/button is being held down, or was just released. It's easy to know if a button is pressed or not. That's a true/false value from your low level API. To know if a key is being held down, you have to know what state it was previously in. If it's from 0->1 then it's just been pressed. if it's 1->1 it's being held down, if it's from 1->0 then you've just released.Ladida
F
3

I find this concept very similar to the state machines of classic digital electronics. Specially the ones which remember their previous output.

A machine whose next output depends on current input and previous output according to (YOUR CODE HERE). This current input is nothing but previous output + (USER, INTERACT HERE).

Fill up a surface with such machines, and it will be user interactive and at the same time represent a layer of changeable data. But at this stage it will still be dumb, just reflecting user interaction to underlying data.

Next, interconnect the machines on your surface, let them share notes, according to (YOUR CODE HERE), and now we make it intelligent. It will become an interactive computing system.

So you just have to provide your logic at two places in the above model; the rest is taken care of by the machine design itself. That's what good about it.

Fite answered 17/7, 2012 at 7:26 Comment(1)
I seem to remember I did have a hardware model in mind when this occurred to me.Varix

© 2022 - 2024 — McMap. All rights reserved.