Main menu
IT Visions
Programming

To obtain high performance, efficient programming methods are just as important as efficient computer architecture and high clock frequency - especially if high reliability and low price are also desirable. Unfortunately, efficient programming is almost an unknown conception today - especially in the PC world. However, the consequence of inefficient programming methods is far bigger than most people can imagine, and one ought to consider carefully if the very few advantages of these methods are worth the price.

All modern computer programming is object oriented. In traditional object oriented programming like C++, C#, Java and VB, all data are only accessible through there own methods/functions/procedures. This courses the program size to explode and makes the execution speed drop enormous because any data access requires at least one subroutine call instead of perhaps just a single assembler statement.

Subroutine calls and big programs are pure poison for any modern computer architecture for many reasons:

  • At any subroutine call it is necessary to save the return address on the stack together with any registers, which will be destroyed, and restore them again after the call.

  • The jump to and from the subroutine makes the content of the instruction pipeline in the CPU and the pipeline in the memory useless, so the CPU and the memory looses time corresponding to the time it takes to fill up the pipelines again. A long pipeline brings high speed, but takes more time to fill up. A Pentium 4 processor has a 20 step pipeline and the pipeline in the memory has 3 steps, so it is obvious that the number of jumps and random data accesses should be reduced as much as possible.

  • The big programs increase the probability for a so-called cache miss. As the main memory in a PC consist of relatively slow dynamic RAM (DRAM), the PC often has one or more extremely fast cache memories based on static RAM. This memory is used for temporary storage of instructions and data, so that reused instructions and data may be fetched very fast. However, this memory is as expensive as it is fast so often it is only 128-512 Kbytes or typical 1/1000 of the total memory. It is obvious that when the program and data size explode, the probability to find the needed instructions or data in the cache is reduced correspondingly. Each time the instructions or data are not available - a cache miss - it cost at least 40 nS. This is the time it takes for a DRAM from the address is issued until the first word is ready on the bus. This is true no matter what type of DRAM is used - even synchronous DRAM (SDRAM), Dual Data Rate RAM (DDR SDRAM), and even 800 MHz RAMBUS (RDRAM)! 40 nS corresponds to approximately 4 instructions on a 100 MHz RISC processor, but 80 instructions on a 2 GHz processor, so when the clock speed is increased it becomes more necessary to reduce the size of the program if it should be possible to utilize the higher speed. You may also say that for each cache miss, the computer speed is reduced to only 25 MHz instead of perhaps 2 GHz!

  • Big programs and big amount of data requires a big memory. To keep the price at an acceptable level, all PC's uses DRAM as this type of RAM only costs approximately 1/5 of SRAM. On the other hand, DRAM is approximately 4 times slower than SRAM in the same technology. As the bottle neck in a PC is the memory it means a 4 time slower PC.

To make the situation even worse, most PC operating systems use preemptive multitasking/multithreading, which means that the operation of one task/thread is interrupted due to an external factor such as time-slicing. This is also pure poison for more reasons:

  • There is no guarantee that a subroutine has finished the operation on one set of data before another part of the program can change these data. Therefore, a subroutine cannot just be called with a reference to the data, but must be called with its own copy of the data set. This leads to a serious fragmentation of the memory and takes a lot of time.

  • There is no guarantee that a program can utilize the whole timeslot, so the CPU may waist a lot of time.

  • At any task/thread switching it is necessary to save all internal registers in the CPU and restore the registers of the new task/thread.

Each time the hardware gets 2 times faster, the software nerds makes the software 2.5 times less efficient. In the childhood of the computer, we typically had a respons time of 100 mS on a 2 MHz 8080 system, which used 8 cycles for each instruction, so that it executed approximately 250,000 instructions per second. Today, modern RISC computers execute approximately 2,000,000,000 instructions per second, a factor 2**13. So according to this rule, the reduction in respons time is (2.5/2)**13 = 18 times. This fits very well with practical experience with automation systems, where a respons time of approximately 1.8 sec. is quite common for SCADA systems etc.

To compensate for the low execution speed, modern PC's use clock frequencies in the GHz range and extremely complicated microprocessor architectures to squeeze the last out of the system, but often almost nothing is gained because the programming methods counteract the processor architecture, and there are many serious disadvantages:

  • The price for the sole computer system (excluding any drives) may rise 5-10 times because of the big memory requirement and the complicated architecture.

  • The power consumption rises enormous. A modern computer has a power consumption which is proportional to the clock frequency and to the number of transistor elements, so that a 10 times rise in the clock frequency and the CPU complexity means a 100 times increase in the power consumption of the CPU! For the memory the difference is smaller. The necessary power to refresh a dynamic RAM is only proportional to the memory size, and the power for data access depends only on the number of bytes per second. However, because the CPU consumes the major part of the power, the consequence may easily be a 50 times increase in the total power consumption and with that a demand for a bigger power supply and perhaps for a cooling fan!

  • The reliability of especially the CPU falls drastic. Statistically, a 10 times increase in complexity reduces the reliability 10 times, but this factor is further accelerated by a big power consumption. For each time the temperature is increased 10° C, the reliability falls to the half. For a complicated microprocessor, which e.g. runs at 1 GHz or more, it is not unusual with a 40° increase in temperature on the CPU chip, which means a further 16 times reduction of the life time, so that the total life time reduction of the CPU is 160 times!

  • A high clock frequency creates big EMC (Electro Magnetic Compatibility) problems.
According to Intel the difference in performance between a Pentium III processor and a Pentium 4 processor is approximately 40%, but various tests has shown, that this enhancement is only obtainable under very special circumstances, which rarely occurs in practice, and the price is a very big increase in complexity, power consumption and price. As a comparison, it would in many cases be possible to obtain a more than 1000% improvement with more efficient programming methods and get a much lower price, a much lower power consumption and a higher reliability, but almost nobody is interested in that - except for Innovatic.


Readability

At Innovatic we also find it very difficult to understand that:

   class HelloWorld {
       static public void main(String args[]) {
           System.out.println("Hello world!");
       }
   }
   
which is the way "Hello world!" is written out in Java (pure object oriented), is easier to understand and overview than the traditional method:
   Println("Hello world!")
   

A Class is a group of similar objects. For example, one class of animals may be the insects. They all have six legs, three body parts, wings, compound eyes and grow from eggs. A class may be regarded as a "blueprint" of an object like e.g. the blueprint of a house - or at least it ought to be! As can be seen from the example, traditional object oriented programming mix the two things so that the definition of the class and the call is done in the same code, which is very unlogical. Why write all this class nonsense if "System.out.println("Hello world!");" is enough? The reason is the way traditional object oriented programming is implemented. Each data structure must have its own methods, so it is necessary to put a class definition on top of the data. However, this courses the readability to drop enormous and the program size to explode and with that all the disadvantages explained before.


The Innovatic
Alternative

It may sound as we are against object oriented programming, but this is not true! For example, our conveyor control system, which was used for the first time in 1980, was object oriented - long time before anybody talked about object oriented programming. The system used two classes - one for conveyors and one for valves and shutters. Also the way we program our gate arrays for our control module LC3000 and our new fieldbus is object oriented in the way of an advanced graphical and hierarchical design environment. It is the way it is done in e.g. C++, C#, Visual Basic 2005 and Java, which to our opinion is wrong and leads to too big a performance penalty.

Our alternative is based on a very simple Visual Basic like language - B# - with event driven data objects (no code) and with classes instead of subroutines and instead of functions with more than one input. If any input data to an object changes, the class is automatically called and uses the data of the object (fully reentrant). There is no need for event handlers or task switching and all the more or less unintelligible keywords of traditional object oriented programming like Binding, Class, Delegate, Finalize, Friend, Handles, Implements, Inherit, Interface, New, Overload, Override, RaiseEvent, WithEvents etc. plus the keyword Sub (subroutine) has been replaced with just two keywords - "Class" and "Remove". This makes it very easy to get started.

B# is based on a simple message queue very much like the message queue on the UI thread of .NET, but with the added feature that no object can be in the queue more than once. This not only brings higher efficiency, but it also guarantees that when an object reaches the top of the queue and gets evaluated, all other objects, which use the result, will get a chance of evaluation before the present object can be evaluated again. All other objects may therefore use the data from the present object by reference, so unlike the message queue of .NET, it is not necessary to buffer the data. Because there is no preemptive switching, each subroutine can finish its job and return the result without interference from other parts of the program as long as the execution time is below a given safety limit. In this way, a kind of pipe line or shift register is implemented with a very high efficiency.

The philosophy behind traditional multitask and multithread operating systems is to make it possible to run more big jobs simultaneously, but this is impossible! There is only one CPU and one memory, so the computer can only run one job at a time. What a traditional preemptive operating system actually does is to switch between small time-sliced fractions of the programs depending on the program priority. However, if each program is divided in "atomic" objects, which can be execuded individually and fast, there is no need for time-slicing, and all objects with the same priority may share the same thread without any change in program behaviour. This is the philosophy behing B#. B# splits up a program by replacing all subroutines with objects, which can be put on the queue. B# is not a multitask or multithread system, but a dual priority system with one queue for each level. It is therefore only necessary with two queues even for very complex systems and there is no need for threads, thread pools, invoke statements, background workers etc. When there are no objects on the high priority queue or a given number of high priority objects have been evaluated, one object from the low priority queue is evaluated and so on. This means that high priority jobs can slow down the system, but cannot block low priority jobs forever.

B# cannot be overloaded or run out of buffers or memory - simply because there are no buffers and no data copying. If a high priority input object supplies data faster than a low priority output object can handle them, some values are just thrown away. This is equivalent to a voltmeter where the input value changes faster than the display update. This does not create any problems in practice. It usually works much better than systems with big buffers, which may show "new" values long time after the input is removed, but if not a single value must be lost, it is easy to add a FIFO buffer object. It is not necessary to use the operating system for this. If it is important to detect an overload condition (which could course a crash in a traditional system), each data can just have a serial number.

The B# language uses almost the same syntax as Visual Basic .NET except that all the superfluous keywords like "Dim", "As" and "Then" are omitted, and all "End" and "Next" statements are replaced with "-". The result is a language with much less keystrokes than C, but even more readable than Visual Basic as shown below:

   B#                              Visual Basic .NET       C#
  
   Integer A, I, N = 3, B.15           Dim A, I As Integer        int A, I;
                                       Dim N As Integer = 3       int N = 3;
   For I = 1 to 10                     Dim B(15) As Integer       int[] B = new int[15];
       If I = N
           A = B.(I)                   For I = 1 to 10            for (int I = 1; I <= 10; I++)
   --                                      If I = N Then          {
                                               A = B(I)               if (I == N)
                                           End If                     {
                                       Next                               A = B[I];
                                                                      }
                                                                  }
   

The difference between B# and C# is obvious. C# is for the software nerds, who only want to make programming so difficult that they are the only ones, who can do it!

The difference between B# and Visual Basic .NET for event driven objects is shown below:

   B#                              Visual Basic .NET
  
   Class Message                       Dim Clicked As Boolean
       Boolean Trig
       MsgBox.Text = String Text       Private Sub Msg_Click(ByVal sender As System.Object,_
       Boolean Clicked = True              ByVal e As System.EventArgs) Handles MsgButton.Click
   -                                       MsgBox("Button Clicked")
                                           Clicked = True
   Message ClickMsg                    End Sub
       .Text = "Button Clicked"
                                       Private Sub Two_Click(ByVal sender As System.Object,_
   Message ClickTwo                        ByVal e As System.EventArgs) Handles TwoButton.Click
       .Text = "Click-Click"               MsgBox("Click-Click")
                                           Clicked = True
   ClickMsg.Trig = MsgButton.Click     End Sub
   ClickTwo.Trig = TwoButton.Click
   

In the Innovatic implementation, an object is nothing but a data area and any data area is an object! Any object declaration just reserves a data area big enough to hold all values in the class. This is exactly the same thing, which happens when e.g. an Integer is declared, and in the same way, it is possible to assign a default value to a variable. If an object has more parameters, it is necessary to specify which one to initialize by means of .parameter like .Text = "Button Clicked" in the above example. Because no other line starts with a dot, more parameters may be initialized without expanding the line length with the usual _. When the class is executed, it just receives a pointer to this data area and may then access the various values of the object by means of an offset. Most microprocessors are able to do this in a single assembler instruction. Even references to values in other objects are done by means of pointers and offsets instead of methods. In this way, the Innovatic implementation takes the best from traditional programming and traditional object oriented programming. The code execute almost as fast as traditional programs and is equally readable, but the values are as loosely coupled as in traditional object oriented programming and XML, so that any data structure may be changed without notifying the users. Note that unlike traditional object oriented languages, the class itself has no data storage! It only defines the method. This makes it possible to regard even the simplest data structures like bytes and integers as object. Most methods to work on such objects like addition, subtraction, multiplication, division etc. are typically handled by an arithmetic unit build into the microprocessor, where it is obvious that methods and data are separated. B# is therefore very consistent.

Because also arrays and strings are regarded as objects, the length/size of array or string B may be accessed as B.Length and element number 3 may be accessed as B.3 or B.(I) where I may be any expression where the result is 3. The brackets indicate that I is an expression and not a name. In case of strings, it is possible to set or initialize more consecutive characters in one statement like .3 = "Default" or S.(I + J) = "Hello". Note that the dot is also used to distinguish between object elements and functions. For example, B.I is element I of object B, B.(I) is element number I of object B, and B(I) is a call of the function B with I as input data.

The size of an array shall always be specified, but the size of a string may be omitted if the size is not known at the time of declaration. In this case, the compiler sets the maximum size to the longest string, which is assigned to this string. For example, if the string is declared as String S = "", and a later definition sets S = "Hello", the maximum length is set to 5. In case of more instances of the same class as shown above, the length of the string in the class and all instances is set to the longest string. The size cannot be increased at run time, so if it should be possible to expand the string like S = S & " World!", it is necessary to define the maximum length like String S.31 = "Hello", or String S.31 .10 = "Hello" if Hello should be loaded with the offset 10. In Visual Basic .Net and C#, all strings are immutable, meaning you cannot change its length or contents once it is defined. Therefore, all string manipulations has to be done by making a new instance of the String class, copying the string(s) and then abandon the previous string. This is a very time consuming procedure, which also leads to a serious fragmentation of the memory.

In B#, simple classes like integers, arrays and strings are handled by the compiler and do not require any class calls, but from a language point of view there is no difference. B# is therefore very consistent unlike e.g. Visual Basic, where e.g. element 3 in array B is accessed as B(3) and therefore uses the same syntax as a Visual Basic subroutine or function.

When a new Class is defined like "Message" in the above example, the name of the class (Message) becomes a new keyword, which may be used to declare one or more objects in exactly the same way as the keyword "Integer" is used to declare integers or integer arrays. Because the declaration and the use of a class are not mixed up as in traditional object oriented programming, it is not necessary with a "New" keyword. Note that in B# object inheritance (use of objects in other objects) is almost invisible. If you declare an array in the code of a class it is regarded as a normal definition of a data area, but because objects in B# are nothing but data areas it is in fact the use of one object in another object.

Any "Xxx.Yyy =" statement defines an input value to an object, and if this value goes high or is changed, the object must be executed. In the above example, ClickMsg.Trig = MsgButton.Click defines an input value to the object ClickMsg. It does not matter if Trig is not used in the Message class as long as it is declared. If the following expression MsgButton.Click goes high or changes state depending on whether Click is declared as Event or Boolean, the object ClickMsg is put into the evaluation queue. Unlike Visual Basic .NET, C# etc., the same trigger signal may be used as input (and trigger) in any number of objects, and the trigger signal may even be gated with other Boolean values so that the message is only displayed under some circumstances. In B#, an event is just a normal Boolean value, which may be tested and used in the expressions of the class, so unlike Visual Basic .NET, C# etc. it is not necessary with extra System.EventArgs to find out what has happened if there are more trigger signals.

Because the value Clicked is defined in the class "Message", ClickMsg.Clicked and ClickTwo.Clicked becomes valid values, which may be used as input to other objects.

It is possible to group classes together in a so-called Namespace. For example, if the company Innovatic has made four classes - Conveyor, Valve, TwoWay and Gauge, it may be practical to name these Innovatic.Conveyer, Innovatic.Valve, Innovatic.TwoWay and Innovatic.Gauge to distinguish them from classes from other companies. To avoid writing these long names each time a new object is declared, it is possible to import the namespace Innovatic (Imports Innovatic) and then just write Conveyor, Valve and TwoWay. If more namespaces are imported, which contain the same names, a compile error will be generated if a duplicated name is used. In this case, the full identifier may just be used.

The class works very much like a traditional subroutine, but with some major differences:

  • The input arguments are much looser coupled. In a subroutine or function, all arguments must be supplied in a precise order, and if there are many arguments the call may take up multiple lines. In a class only one argument need to be supplied - the one, which must be changed. All other arguments remain unchanged because each object has its own data area. Like XML, this loose coupling is possible because each argument has a name. In this way, it is easy to change the class and add new arguments and returned values. In the above example, the Message class includes the MsgBox object, which may have many properties like size, position etc., but in this case, it is only necessary to specify the text, which must be displayed.

  • All objects are event driven so that they are only calculated if an input changes state. This makes it possible to change an output value or parameter to something, which does not corresponds to the expression in the class as long as the object is not set up for evaluation! This is extremely practical e.g. during commissioning of a process control system. For example, if a material gauge do not work, the output value may just be set by hand. It also makes it possible to override definitions in a base object. For example, in the above example the MsgBox object may be an instance of a Window class, which displays various windows. This class may contain an input parameter to set the color. If this parameter is changed, all instances of the class will be set up for evaluation, so the color of all MsgBox objects and with that all Message objects will change. However, as each instance of the MsgBox object has its own data area, the color of each Message object may also be changed individually with ".color =" or "ClickMsg.color =" in the same way as the text is initialized with ".Text =" in the above example.

  • Only that part of the class, where the input value has changed, needs to be calculated. For example, if the color parameter of the Windows class in the above example changes, all MsgBox objects are set up for evaluation, but since these objects has no outputs, the whole Message class is not calculated. The calculations may also stop if the values no longer change. For example, if a signal is AND'ed with another signal with the value False, the result do not change and no further calculations are necessary.

  • In a class, any value, which is only changed once during the execution of the class, may be used as a returned value, and it is not necessary to use any keywords like the Return keyword in Visual Basic. This makes it unnecessary to rewrite the class if more values are needed, and it makes it much easier to debug a class since all internal values may be read.

  • A class may be much more complex than a traditional subroutine, which do not return anything, or a function, which only returns one value.

  • The code for imported classes is not loaded to memory before an input to an instance of this class changes state or if there are no instances of the class. This saves a lot of memory - especially for image processing programs etc. with a lot of effects and wizards. The "Remove" keyword is used to reset this function and remove the code from memory until it is used again. Note that this does not destroy the data area of the object so that the function is not destroyed. In traditional object oriented programming, an object (data and code) may be removed with "Finalize" or "Destructor", but this removes the function, so if an event occurs, which is handled by the removed object, a run time error is generated.


Proces Control
Example

B# has been designed for real world applications. The following example shows how easy it is to program a process control system using B# and the Innovatic namespace.

   Imports Innovatic              ' This imports the Innovatic namespace with the classes
                                  '   Valve, Conveyor and Gauge (and others).

   Valve Shutter1
       .OpTime = 5                ' 5 sec. operation time
       .Out = 23                  ' Physical output to open the shutter
       .InO = 124                 ' Physical input to tell that the shutter is open
       .InC = 125                 ' Physical input to tell that the shutter is closed
 
   Conveyor Belt1
       .StartTime = 0.5           ' Start time = 0.5 sec.
       .EmptyTime = 5             ' Time to empty the band = 5 sec.
       .Out = 24                  ' Physical output to start the band
       .In = 126                  ' Physical acknowledge that the band is running
 
   Conveyor Belt2
       .StartTime = 0.5
       .EmptyTime = 6
       .Out = 25
       .In = 127

   Gauge Empty1
       .TimeUp = 1                ' Activation delay = 1 sec.
       .TimeDown = 10             ' Fall delay = 10 sec.
       .In = 128                  ' Physical Input
   
   Gauge Full1
       .TimeUp = 1
       .TimeDown = 20
       .In = 129
   
   Belt2.RunIn = True
   Belt2.MatIn = Belt1.MatOut     ' Belt2 receives material from Belt1
   Belt2.Run = StartStop          ' StartStop is a global signal to start the chain

   Belt1.RunIn = Belt2.RunOut     ' Belt1 must only run if Belt2 is running
   Belt1.MatIn = Shutter1.MatOut
   Belt1.Run = StartStop

   Shutter1.RunIn = Belt1.RunOut And Full1.False ' If the silo runs full, the shutter
                                                 '   shall close immediately
   Shutter1.MatIn = (Empty1.True Or StartStop) And ProductType
   Shutter1.Open = StartStop       
   

As can be seen, it is possible to write the code directly by looking at the flow diagram - even without any knowledge about programming. 9 lines of code are everything, which is needed, when the necessary timers and physical input/output connections has been defined. Just imagine what it would require to write this application in any other programming language!

The statement "Shutter1.MatIn = (Empty1.True Or StartStop) And ProductType" shows another feature of B#. Any value may be tested as Boolean. If the value is 0, it corresponds to False. In all other cases, the value is regarded as True. In this way, it is possible to do Boolean arithmetic and simultaneously transfer information like batch number, material type etc. In Visual Basic only one bit out of 16 is utilized in a Boolean value.

The reason for Or'ing the material gauge with StartStop is that material gauges usually have a fairly low reliability so it is not wise to depend 100% on these.

This page is updated February 26th 2007