Guidelines for Time Critical Code
Use evals instead of branches.
Use parameters passed by reference instead of by value. Take care when passing list elements that will be removed, ie:
branch test() exc(AllRefs[1]) end branch exc(ref) excludef(AllRefs, ref) ref.call() //error! end branch exc(REFERENCE ref) excludef(AllRefs, ref) ref.call() //ok! end
Use list call, array call and list operations instead of while and for, ie:
for INTEGER i = 1 .. count list list[i] += 1 list[i] *= 2 next //faster: list += 1 list *= 2
Another example:
for INTEGER i = i1..i2 list[i].Prop1 += 1 next //faster: [list[i1..i2].Prop1] = INTEGERS{list[i1..i2].Prop1} + 1
Use switch instead of if.
Ordering
Geeny generates fast links to object properties, branches and evals on the fly, wherever possible. To maximize performance, the same order of properties, branches and evals should be maintained for objects that will be accessed within a specific script expression.
To some extent, the ordering is done automatically in Geeny. This is true for derived templates, which always maintain the order of properties as defined by the parent, regardless of the order within template definition itself. However, consider this example:
BEGIN object TEST1 ATOM Prop1 = 'TEST1' INTEGER Prop2 = 10 END BEGIN object DERIVED OBJECT Parent = TEST1 INTEGER Prop2 = 5 ATOM Prop1 = 'DERIVED' END BEGIN object TEST2 INTEGER Prop2 = 15 ATOM Prop1 = 'TEST2' END BEGIN global ALIAS REFERENCES Fast = TEST1 DERIVED REFERENCES Slow = TEST1 TEST2 END branch test1(REFERENCE ref) ATOM Prop2 = ref.Prop2 end branch test2 INTEGERS Prop1 = Fast.Prop1 //fast access Prop1 = Slow.Prop1 //slow access test1( 'TEST1') test1( 'DERIVED') //ok test1( 'TEST2') //fast link overriden end
Similar problem may occur when the same script call may address an object with different branch or eval ordering:
BEGIN object TEST1 OBJECT Scripta = test1 END BEGIN object TEST2 OBJECT Scripta = test2 END branch test(REFERENCE ref) ref . foo1() ref . foo2() end
In the above example, both test1 and test2 scripts may contain branches or evals ‘foo1’ and ‘foo2’. However, the ordering may not be the same, which may induce a performance penalty.
This performance penalty may not be avoided for different call types (eval, branch) in both scripts; for example when in ‘test1’ script ‘foo1’ is eval, and in ‘test2’ script ‘foo1’ is a branch. When such is not the case, however, the ordering for such calls may be defined in base script, ie:
branch foo1 end branch foo2 end //or alternatively eval foo1 end eval foo2 end //or even branch foo1 end eval foo2 end
Then when an object template is declared, the base script is used as the first script in the list. This will define the ordering for these calls in all derived object templates as well, regardless of the actual ordering of brances and evals in actual script files.
BEGIN object TEST1 OBJECT Scripta = base test1 END BEGIN object TEST2 OBJECT Scripta = base test2 END BEGIN object DERIVED OBJECT Parent = TEST2 OBJECT Scripta = @ derived //base is inherited END BEGIN object DERIVED2 OBJECT Parent = TEST1 OBJECT Scripta = base derived test1 //base has to be first! END
Problems may occur when branches and evals with same name exist on an object. This should be avoided.
Fast routes
The compiler generates fast route code when specific patterns are found, such as:
for counter = x..y for counter = seqi(x, y, z) a[x..y] [a[x..y]] a[seqi(x, y, z)] [a[seqi(x, y, z)]]
The fast route cannot be generated when a pattern is broken across two lines or more, ie:
INTEGERS set = x..y for counter = set INTEGERS index = seqi(x, y, z) a[index]
As a general rule, performance should be better with a fast route, even if the sequence has already been calculated before hand for other reasons and can be reused, ie:
INTEGERS list = from..to lwriteln(list) //faster: lwriteln(array[from..to]) //slower: lwriteln(array[list])