|
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:
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:
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:
|
|
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 |
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:
|
|
Proces Control |
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
|