Performance and Interactivity
Modern Forths do not generate threaded code any more, except for very particular sets of circumstances. Modern Forths generate native code using the same techniques as compilers for other languages. When we implemented our VFX code generator 10 years ago, we found that there is only one algorithm needed specially for Forth, and even that has an analogue in some C compilers. Consequently the limits to Forth performance are the same as for any other language -- design objectives, manpower, money, and time.
The requirements of interactivity do introduce some constraints. Forth programmers expect their compilers to be fast. A client's application containing 80,347 words in 815,548 lines of source produces 15MB of binary in under a minute on an old 2.8GHz P4 box. Recompiling the application reduced compilation time from 56 seconds to 34 seconds, showing the impact of hard disk performance. Even on this old PC, we're seeing compilation speeds of nearly 25,000 lines per second generating nearly 500KB per second of binary. For an embedded application it is common to see compilation times under one second, and for small applications less than 0.1 seconds.
There are several consequences that affect the way Forth programmers can work. Compilation time is unimportant compared to testing time, which in turn means that changes in low level code can be investigated very quickly. We'll come back to this later. In most modern Forths, cross-reference and source location tools are built-in, and the compiler and symbols remain active during testing. Forth has a strong focus on debugging.
Interactivity and Debugging
We know from experience that the productivity of programmers varies over a huge range. A large part of this has nothing to do with coding, and a great deal to do with project management and the ability to debug. There's nothing magic about debugging, it's just the application of formal scientific method:
- Experiment with a yes/no answer
Figure 2 shows that debugging consists of two nested loops. How fast you can go around the inner loop determines how fast you can debug a system. Interactive debugging is the fastest route I have found. The stages of debugging are:
- Make the problem repeatable. This usually involves finding out which inputs cause the problem.
- Gather data about the problem. Observation is crucial, so take this stage slowly and carefully. I have seen people immediately dismiss exception displays and crash-dumps which contain vital clues.
- From the data, form a hypothesis as to what caused the problem.
- Design an experiment which tests the hypothesis. The important part in designing the experiment is to ensure that it gives a yes/no answer.
- Run the experiment. If the hypothesis is incorrect, go back to stage 2.
- Fix the problem.
- If you have more bugs to fix, go back to stage 1 for the next problem.
Programmers are very rarely taught how to debug. The problems they face are almost always in their source code. So why don't they read it more carefully? In particular, why don't they write a line or two describing the whats and whys of of a routine before they write the code? The poor relation who maintains your code for the next 10 years costs your employer a fortune because you could not be bothered. As a sidenote to this potential rant, my company has been using literate programming for 10 years and we believe that it has increased the quality of our code. Whenever we incorporate third-pary code into our code bases we document it to our house standards. This process nearly always reveals bugs. To produce stable software the golden rule is to fix the bugs first. A second rule is not to write clever code. Although this upsets people trying to achieve alpha-male guru status, you should remember the aphorism attibuted to Brian Kernighan:
Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.
Forth is a wonderful tool for debugging systems and their software. Since we spend most of our time debugging and testing, it astounds me how our industry accepts mediocre tools for critical activities.
When I'm programming embedded systems that control mechanisms, I always treat the hardware specifications with as much concern as the software specification, which probably doesn't exist. A large number of things you have no control over are changing around you on a daily basis; gear ratios, airline lengths, and more. The team proudly put the prototype bomb-disposal machine together according to the ministry specifications. Then the joint services bomb-disposal team came to visit. No bs, just "No, we don't do it that way, we want it done this way". The conversation with the navy people about doing this underwater was memorable. We all admired that team for their useful directness as well as for the job they do.
Of course, the management just said we could fix it in the software and we can't change the delivery deadline. This is known in our shop as "tail-end Charlie syndrome". The software team accumulates all the upstream problems, the mechanics are delivered late, the air lines are of a length to cause resonance problems at certain motor speeds, and the control system needs a rewrite when the four-axis frame is at the bottom of a muddy trench being sprayed with abrasive grit. The fourth air motor driver was acceptable (Figure 3).
Commissioning takes far longer than anyone likes to admit unless it has been properly planned for. I'm old enough to have seen a few projects go wrong. Fred Brooks said "Good judgement comes from experience, and experience comes from bad judgement". In the majority of cases software projects go wrong for lack of good project manangement. It is part of your resposibility as a software engineer to train management to understand your problems and to plan for them.
During development, you will not predict the problems you will actually encounter during the product life, so you cannot provide the test code for those problems. A remote console with full access to all words becomes a lifesaver. The bomb-disposal machine contained two embedded systems and a Windows panel PC, all running Forth systems linked by an RS485 network. The Forth interpreter in each system was available at all times. Some of the printable stories from this project are available in the EuroForth 2003 conference proceedings.