I have spent a fair amount of time over the last year doing web development. This has been variously interesting, fascinating and downright frustrating.
My musings on the subject…
Conventional code development has a well-characterised target environment. Issues arise when that environment changes (porting to a new compiler, a new OS, a new version of an OS, a new chip). This is however mitigated by the fact that the new target environment is at least a stable target once known.
For web development, the ‘target’ consists of the server infrastructure and the clients. The server infrastructure is (largely) under control allowing conventional prediction of code development and planned porting activities when required.
On the client side however, a certain controlled amount of hell breaks loose. Most browsers agree on HTML rendering – mostly. There is a good degree of confidence that HTML presented in FF3 and IE8 will look basically the same. In javascript and DOM programming, there are subtle differences that vary from rendering issues to annoying-to-find functional issues.
One bought to my attention yesterday was that programming the contents of a text box in FF3 and IE8 does not call the elements’ ‘onchange’ method, however in Safari, it does. This is annoying as perfectly functional code suddenly had an infinite loop on a new client. Solvable, but not very predictable. Combine this with the fact that debugging tools for some browsers barely exist. Note – FF3 has some great debugging tools and is my browser of choice, however if the bug does not show up in this browser – these do not help you.
Another example; re-rendering a form in IE keeps the state of check boxes intact from the previous display. So for consistency the body ‘onload’ method needs to clear these out. Note that it does not matter what the DOM programming says before the render is complete – the browser ignores this information.
There are more.
On the other side of things, in web applications – especially database-based systems, there is very significant underlying functional complexity. The standard solutions to managing this complexity; strong architectural design and careful functional specification are needed to ensure interactions are understood, explored and tamed before the development team codes itself into a corner.
Unfortunately the ‘gee, the UI is working – it must be finished!’ aspect of web development makes it difficult for the developer to put their foot down and insist that these essential tools are given due deference – and not just become ‘deferred’.
The last issue which makes things hard is regression testing at the UI. For form-based systems, you stand a chance and there is the infrastructure to help build test harnesses. However (of course) the effort in providing the test harness for a page is significant and requires insistence to ensure it is factored in and prioritized. On a site where there is a significant graphical component – regression testing is hard to achieve except through a human agent.
The last aspect of development that is interesting is the use of interpreted, dynamically typed languages (javascript, PERL etc…) for web applications. The dynamic typing allows for the generation of run-time type errors and their resultant fall-out. This makes thorough testing all the more important. To mitigate this, programmer discipline is essential to ensure that interfaces behave as expected.
So – in short; there are dragons and it is a significant challenge. Web development is not a black art, but there are aspects of it that are. Experience, discipline and attention to the basics really help. Experience to help know what browsers are going to do, discipline to architect systems and use code structure and language capabilities to minimize the chance of runtime error, and testing the c*** out of the result.
Sounds like a challenge…
I was talking with a friend this morning (Hi Geoff) about software development, gigs I am currently involved in and plans he has… And observations around some really cool software that is emerging into world in a big way. Things like X-Plane and Reaper to name but two (www.x-plane.com, www.reaper.fm)…
The old software development model; large heavily managed teams of developers pouring man-years of effort into producing products.
Many of these products started with small development teams and conceptual integrity. Then something slipped and it became a ‘Business’ (it was a business already…). Something happened and the development/maintenance became more and more cumbersome. Then the product started to look more like a porcupine than a well-crafted sculpture…
Small teams are making an impact. Whether this can be sustained long term will be interesting to watch. Will the keep the lean/mean approach with simple communications and fantastic productivity, or will something happen along the way?
Combine this with open source and the ability for small teams to leverage a huge amount of prior investment.
And further combine this with the increasing trend in the embedded world for manufacturers to provide significant amounts of library support for new parts (check out what TI, NXP and ST offer in the way of support for their Cortex-M3 based ARM MCUs) – small teams can do big things. Quickly.
The acronym? Might need work – ‘Small Teams of Killer Coders’.
I know the 2 or 3 people I would work with to build a new system fast by choice. I know from bitter experience how hard it is to build a team when the pieces just do not fit.
Basically; I do not want to be a ‘manager’ in the conventional sense. I want to build consensus and run hard. It is so much more fun…
Just an idle thought whilst I am musing about debugging…
Sometimes problems seem to make no sense. Sometimes it takes a lot of time, blood, sweat, tears etc… to find the solution to a difficult issue.
At times like this, something that I find comforting is a simple ritual.
‘Sacrificing the mental chicken’.
Oftentimes, when you need one, a suitable piece of livestock is not available to offer to the Gods of code – so instead you can call to mind a suitably succulent bird and offer it as you start the next round of tests, hoping this time you have nailed it and your hypothesis holds…
There is something in this idea though… I refer the interested reader to ‘Round the Bend’ by Neville Shute.
Meanwhile – back to uninstalling this (currently virtual-bloody-feather-covered) device driver…
March 19th, 2009 in
Uncategorized |
1 Comment
An idle thought whilst many projects seem to keep me busy…
A key (but not often directly discussed) figure of merit in software development. How long is your debug/build/test cycle? On windows/mac where this is all integrated on our development machines – IDEs which keep this number down are nice to work with. In the embedded world – things have got a lot better over the last few years (flashing a device over USB and using an On-chip ICE to provide a good quality debug interface) from where they were (compile, build, use eprom programmer OR if you are luck an ICE that barely fits on the board, uploading over a slow serial line).
I was reminded of this yesterday. Debugging an issue on Vista (…) from code that works flawlessly on Windows XP. Unfortunately I cannot get my dev tools to work across with the target machine and so the compile/build/test cycle has a flash-drive in the middle of it, and debugging is strictly ‘printf-to-synchronized-file’. So the cycle time is up to about 6 minutes…
So as a result, I try and pack everything into that 6 minutes I can. An extra 2 minutes spent putting some more output into the code reduces the number of cycles as I traced down the execution stack to the failure point.
Net result – took me about 4 hours to find what I could have found via a good debugger interface in about 10 minutes…
However – I think the point (that I reminded myself of) is – get that cycle time as short as you can – because it dominates productivity when you are in debugging mode.
Of course – we could all write perfect code first time and be done with it…
Just coding away at a simple algorithm – finding where a string occurs in a firmware file to be uploaded onto a chip. The string identifies the position after which an address should be changed in the firmware to allow for load-time configuration changes.
I just checked out the original code that did this. Now, this code did the right kind of things – using standard library functions to find the start of the matching string in the file and then converting the resulting byte offset into an offset into the original data and replacing the data, accounting for the length of the matching string.
However to do this, the code had to allocate a number of large arrays JUST so the standard library calls could be used. Whilst not in general a big deal – it does introduce a number of potential error conditions (allocation failures) that this code did not trap – and it also looked a bit messy.
IMHO – a big part of good design is aesthetics. If code LOOKS right, then the chances are that thought and energy has gone into the design of that piece of code to make it RIGHT.
Another part is efficiency. I do not advocate optimizing everything, but sometimes an algorithm is so straightforward that it is simpler to expose the algorithm to make the code clearer to the reader/maintainer. In this case, this also saved on allocation/deallocation of over 1 megabyte. Yes, this code does run faster (not inner loop, so not a big deal – but could be significant on some systems).
New code:
bool QuadBuffer::replaceQuadletAfterStringTag(
const char *pSearchString, uint32 uReplacementQuadlet) {
uint uBytes = 4 * size();
uint8 *pArray = (uint8*)mpData;
// Match state machine
bool bFound = false;
const uint8 *pMatch = (uint8*)pSearchString;
for(uint uByte=0; uByte<uBytes; uByte++) {
if ((*pMatch) == pArray[uByte]) {
pMatch++;
if ('\0' == (*pMatch)) {
bFound = true;
uint uQuadPosition = (uByte+3)/4;
mpData[uQuadPosition] = uReplacementQuadlet;
break;
}
} else {
pMatch = (uint8*)pSearchString;
}
}
return bFound;
}
January 7th, 2009 in
Uncategorized |
No Comments
If any of you have ever coded up a state machine in C, you are likely to have used the venerable switch statement – something like:
//————————————–
switch(state) {
case STATE1:
…
state=STATE2;
break;
case STATE2:
…
state=STATE4;
break;
case STATE3:
…
state=STATE1;
break;
case STATE4:
…
state=STATE3;
break;
}
…
//————————————–
In my experience and view, before very long this type of code gets very ungainly, and is subject to a great deal of potential for coding error. To top it off, it does not even necassarily compile to very efficient code. Depending on the smarts of the compiler it may turn into a branch-ladder (non-constant time for accessing each state) or a table lookup.
The following is a coding technique that is arguably neater, more efficient and easier to reason with. And there is not a switch statement in sight.
//————————————–
void State1Fn(void);
void State2Fn(void);
void State3Fn(void);
void State4Fn(void);
static void (*spState)(void) = State1Fn;
//*** State functions
void State1Fn(void) {
…;
spState = State2Fn;
}
void State2Fn(void) {
void State3Fn(void) {
}
void State4Fn(void) {
…;
spState = State3Fn;
}
// State machine entry point
void StateMachineStep(void) {
(*spState)();
}
//————————————–
Advantages:
- Simple to read and understand
- Simple to add parameters and return values to behaviour
- Plenty of space for comments
- Compiles to very efficient code (constant time for all states)
Disadvantages:
- Debugging? Well, not really. A symbolic debugger will tell you what function spState points to. If not then a simple tell-tale piece of debug code will do the trick (see below).
- ?
This coding pattern works well for parsers and similar code, or any kind of state machine managing protocol states. Go fill yer boots.
//————————————–
// State Tell-tale code
static const struct {
void (*pFn)(void);
const char *pFnName;
}spFnArray[] = {
{spState1Fn,”spState1Fn”},
{spState2Fn,”spState2Fn”},
{spState3Fn,”spState3Fn”},
{spState4Fn,”spState4Fn”}
};
const char *nameThatFunction(void (*pFn)(void)) {
for(int i=0; i<(sizeof(spFnArray)/sizeof(spFnArray[0]); i++) {
if (pFn == spFnArray[i].pFn) {
return spFnArray[i].pFnName;
}
}
return NULL; // Did not find a match
}
//————————————–
Thought this little nugget was worth sharing.
I have seen lots of code with #ifdefs knocking around like they are going out of fashion. One thing that these are used for is to conditionally compile in-out debugging code.
The thing is – debugging code tends to have a very short shelf-life. It is in code, you find/fix the issue and then the code is essentially dead and done with.
However the code can end up looking pretty messy with all that vestigial code hanging around.
Now in some cases, there is a good argument for leaving ‘verbose’ reporting code in – and using macros/conditional compiling elsewhere to control that code. I am not knocking that – but I am of the opinion that #ifdefs should stay out of the way of main-line code – it just makes it messy and difficult to read. IMHO – clean code is the foundation for quality code.
Now the point of this post is a formatting trick that makes debugging code obvious and easy to pick out when browsing a code file – and easy to remove when you are done with it. The trick is to break the normal indenting rules. Left-justify all debugging code.
Example:
for(TIterator tIter = lClassList.begin();tIter != lClassList.end(); tIter++) {
(*tIter)->DoSomething(…);
printf(“Look at %s now! – it is %s!\n”, (*tIter)->name(), (*tIter)->whatIsIt());
(*tIter)->Process(…);
for(int i=0; i<(*tIter)->leaves; i++) {
printf(“\t %s leaf %d = %s”, (*tIter)->name(), i, (*tIter)->getLeaf(i)->name());
}
(*tIter)->Finish();
}
What is debugging code? What is application code? We want that debug code out of the inner loop for release time right?
The #ifdef solution:
for(TIterator tIter = lClassList.begin();tIter != lClassList.end(); tIter++) {
(*tIter)->DoSomething(…);
#ifdef DEBUG
printf(“Look at %s now! – it is %s!\n”, (*tIter)->name(), (*tIter)->whatIsIt());
#endif
(*tIter)->Process(…);
#ifdef DEBUG
for(int i=0; i<(*tIter)->leaves; i++) {
printf(“\t %s leaf %d = %s”, (*tIter)->name(), i, (*tIter)->getLeaf(i)->name());
}
#endif
(*tIter)->Finish();
}
The left-justified solution:
for(TIterator tIter = lClassList.begin();tIter != lClassList.end(); tIter++) {
(*tIter)->DoSomething(…);
printf(“Look at %s now! – it is %s!\n”, (*tIter)->name(), (*tIter)->whatIsIt());
(*tIter)->Process(…);
for(int i=0; i<(*tIter)->leaves; i++) {
printf(“\t %s leaf %d = %s”, (*tIter)->name(), i, (*tIter)->getLeaf(i)->name());
}
(*tIter)->Finish();
}
Now – which is more readable? Where can you quickly see the debugging code? Where is the logic of the application code obvious?
This is obviously a ‘trivial’ example, but give it a try and see if it works for you…
For most programmers, programs are simply thought of as sequences of things happening one after the other in a pre-determined (by the programmer) sequence.
Modern computing has changed that – many of the systems we now write are responsive to events that are not under our control (a page hit, a mouse click) and so we have programs that largely consist of ‘fragments’ of code that get run in response to an external stimulus. Whilst the ‘concurrency’ or, how it is perceived, the ‘asynchrony’ of the real world to our programs’ world is there – it is quickly tamed and we are back to thinking one-at-a-time.
The first place many people saw concurrency in action was probably in web browsers. There the limitations of the HTTP protocol (request-wait-response) were quickly overcome by having many concurrent requests, hiding the ‘wait’ latency from the user. When the internet was slower you could see different images and bits of text coming up on the display as their individual concurrent connections delivered their results.
So – concurrency is good right?
Have a look at this:
int a=0;
for(int i=0; i<1000; i++) {
a++;
}
Simple enough piece of code (I will use C- or Pascal-like pseudo-code throughout this blog). The result you would expect after this fragment runs is that ‘a’ will have the value ‘1000′.
Now if we have several ‘threads’ of execution running which do not share ‘a’ or ‘i’, then we will get several pieces of code that all produce the result ‘1000′. Not very interesting so far.
Now what happens if ‘a’ is shared? Then what happens if we use 10 threads to execute the code? When all the threads are done (an issue in itself – for another post) will a contain the result ‘10,000′?
Unless you are working on a quite special CPU, the answer is probably no. It will be somewhat less, and every time you run the program, the result will likely be different…
In this case concurrency is not tame and is causing problems – in this case the specific problem is a classic; ‘non-determinism’. And it comes from the state space of our system being far bigger than we thought.
On a modern CPU, the code above will have been converted by the compiler into something along the lines of:
// externals - 'a' is stored at &a in memory.
// i is stored in a register, r0
start
mov $1000,r1 ; loop limit
Loop:
mov $0,r0 ; L0 i = 0
mov a,r2 ; L1 r2 = memory[a]
inc r2 ; L2 r2 = r2+r1
mov r2,a ; L3 memory[a] = r2
inc r0 ; L4
cmp r0,r1 ; L5 Set flags for r0-r1
ble Loop ; L6
stop
Think of the ’states’ of this program fragment. The program counter will be at one of L1-L6 at any instant for each thread that is executing this fragment. For each thread, the complete computation is a 1000-fold unrolling of these instructions – for a grand total of 6000 states per thread. We could represent the state of the system as a list of the state of each thread (from 1-6000) for the 10 threads in the system – a total of 6000 to the power 10 states that the system could be in – a rather big number (more than 30 zeroes…). A specific execution will only consist of 60,000 states.
Now, consider L1-L3. If any two threads interleave their executions of L1,L2,L3 then we will loose a result (clue: both threads get the same value from memory). If we considered L1,L2,L3 for any thread must come together and cannot be interrupted by any other thread – ie. the loop becomes L123,L4,L5,L6 we now have a smaller set of states 4000 to the power 10. The characteristic we know of *all* of the possible 40,000 state-long executions weaving through this state space is that there will be no interleavings where an error (ie. missing an update to the value of ‘a’) can be introduced.
The chance of just happening to choose one of the error-free executions is small. If we want determinism, then we need to guarantee that the execution that occurs happens to be one that gives us the deterministic result we want.
In Part II we will look at simplifying the state problem further before going on to solutions.
The world has changed a lot in the last 18 years.
In 1990 I started working on my DPhil (PhD to the rest of ya) with sponsorship from Inmos Ltd in the UK – then a part of SGS-Thompson Microelectronics (having been passed like a hot potato from public ownership by the UK Government, on to Thorn-EMI and then…).
Little known outside of the UK and Europe, Inmos did some very interesting things – the key one of which was the design of the Inmos Transputer.

Inmos Transputer Burn-in Card
Unfortunately the Transputer never quite made the big-time – price was a big factor. But this little chip introduced some really radical concepts into the world. Eighteen years later and the rest of the microelectronics world is catching up. We now have a problem – more cores than we know what to do with, and a whole new model of programming that people in the embedded and desktop worlds are going to have to get their heads firmly around… Note the above board – 42 x 30MHz RISC CPUs with IEEE754-1985 floating point units on board. An aggregate 1.26GIPS – not bad for 1990.
For anyone really interested in the Transputer, Prof. David May is your man. His musings on the Transputer (which I hope he will embellish in the future) may be found here. Also, an interesting recent article by the good Professor regarding the whole concurrent programming problem may be found here.
My inspiration for starting this blog is to brain-dump some of the ideas that have come from my experience starting with the Transputer family, and working forwards through how many of the ideas and concepts I learned at that time and since, apply to modern design problems.
This blog I intend to be a hands-on, in-depth, thinking about the meta-problem kind of approach to modern embedded and desktop systems middle- and low-level design issues.
I hope you enjoy it.
Cheers
Don
November 11th, 2008 in
Musings | tags:
Introduction |
No Comments