Scripting API

Home Scripting API Index

Tips and Tricks

This page gives some tips and tricks that may help to write error free and efficient code.


Use the message window

You can write messages to the message window using either 'System.out' or 'System.err'. This might help in debugging scripts or to display error messages. Messages printed to System.err will be displayed as error messages.

Example A: System.out.println("Hello World");
Example B: System.err.println("My Error Text");



Hide objects with 'setVisible(false)'

When you want to hide scene objects, do not hide objects by setting a very small scale or by moving them out of the scene. Those tricks will work to hide the object, but they are not very fast. Use SceneObjectElement.setVisible(false) instead. This is much much faster.


Optimize loops by hand

Optimizing loops will typically have the largest benefit on better performance. The script engine's compiler is not very good at optimizing loops automatically. Move as much code that can be precomputed out of the loop's body as you can.


Declare Constants as 'static final'

By declaring constants as 'static final' outside of methods, those fields can be accessed faster than other type of fields.


Avoid using 'double' and 'long'

Use 'double' and 'long' built-in types only if you really need the extra precision. Otherwise use the 32-bit types 'float' and 'int'. 'float' and 'int' are faster than their 64-bit counterparts, because the script engine internally is otimized for 32-bit types. Using 'byte' or 'short' is typically not faster than using 'int', only for arrays those are treated differently than 'int' internally. It makes sense to use 'byte' or 'short' when using larger arrays to save memory and speed up access time, otherwise simply use 'int' and everything will be fine.


Declare methods as 'static' when possible

Static methods cannot access non-static methods or fields. When a method only accesses static fields and methods, you should definitely declare the method static as well. The time it takes to dispatch a static method is faster than any other kind of method.


Declare public non-static methods as 'final' when possible

When added to the declaration of a method, the 'final' keyword will make the method non-virtual. This means that the method cannot be overloaded by sub-classes for polymorphic runtime dispatch. If your class never gets extended by a subclass, or you do not require polymorphism, you can safely declare all public methods as 'final'. Private methods cannot be polymorphic as well, so by declaring a method as private, you will achieve a similar result. The time it takes to dispatch a private or final method is faster than an ordinary non-static polymorphic method.


Try to avoid new calls when possible

Creating many objects with new might affect the overall performance. In a lot of cases it is possible to reuse objects instead of creating new ones. The more living objects, the more work the garbage collector needs to do. The garbage collector is an internal mechanism that will remove unused objects from memory. It runs in the background and the less work it has to do, the better. For some classes like SceneObjectElement there are also some overloaded methods available that accept float parameters instead of Vector3f objects. Prefer making use of these overloaded methods when possible that accept plain data types such as float versus those that accept objects. This will automatically prevent you from creating more objects than necessary.


Try optimizing identical expressions

When you have multiple math constructs that are identical, the result can be computed only once and stored inside a variable and then reused multiple times. This is important, because the script engine currently does not opimize those expressions automatically. This is even more important when the expressions are used inside a loop.

Example:

Bad:
for (int i=0; i<10; i++)
{
  obj[i].setPosition(scalex*Math.sin(Math.toRadians(angle + phase)) + globalx[i],
                     scaley*Math.cos(Math.toRadians(angle + phase)) + globaly[i],
                     globalz[i]);
}

Much better:
float angleRad = Math.toRadians(angle + phase);
float offsx = scalex*Math.sin(angleRad);
float offsy = scaley*Math.cos(angleRad);
for (int i=0; i<10; i++)
{
  obj[i].setPosition(offsx + globalx[i], offsy + globaly[i], globalz[i]);
}


Both examples should do the same thing, the second example is much faster.


Prefer Math.atan2(x, y) over Math.atan(x/y)

The Math.atan2 method is a a special version of Math.atan for cases where you need to do a division before calling atan. This is a very common case when for example computing angles inside a right-angled triangle. Prefer the Math.atan2 method over the Math.atan for these cases, it is faster and it will also work correctly when y is zero or near zero, and it also takes the signs of x and y into account separately.


Prefer switches over many if/else ifs

The switch statement is highly optimized internally. It is faster than multiple if/else if sequences in most cases. Here is an example of bad code and good code...

Bad:
if (i%5 == 0)
{
  ...
}
else if (i%5 == 1)
{
  ...
}
else if (i%5 == 2)
{
  ...
}
else if (i%5 == 3)
{
  ...
}
else //if (i%5 == 4)
{
  ...
}


Much better:
switch (i%5)
{
case 0: ... break;
case 1: ... break;
case 2: ... break;
case 3: ... break;
case 4: ... break;
}

The second example using the switch will be faster in many ways. First of all the expression i%5 will be computed only once. In the bad example, the same expression might be computed multiple times for each if, because the script engine does not eliminate identical expressions automatically. This is also important because the % (modulo) expression is quite slow, and also the switch will not require to do many compares internally, because it can make use of an internal jump table. As a rule of thumb, prefer the switch statement when you have three or more if/else ifs


Try deactivating the Shared VM option

Sharing a virtual machine will allow that multiple scripts can access static variables of other scripts. Shared VMs will also speed up script starting times. If you do not need those features, for example when your scripts do not communicate with each other and only access their local and member variables, and when long starting times are no big problem, you might try to disalble the Shared VM option in the script settings. Shared VMs will not make the scripts run on multiple cores, so disabling this option might speed up your scripts noticeably on multi-core CPUs. When the Shared VM option is activated, all scripts inside a virtual machine will run sequencially, otherwise they might run parallely.


An NL2SCO can have more than one script

If you have a very complex and long script, it might make sense to split it up into two or more scripts. E.g. one script might take care of animating the objects, while another script animates light effects etc. This might be faster, because the scripts are executed on individual CPUs/cores parallely if your computer has multiple cores. The engine will take care of all the synchronization when the same objects are referenced from multiple scripts at the same time. This only makes sense for very long and complex scripts though. It may only work faster when not having the Shared VMs option enabled. Sharing VMs will not make the scripts run on multiple cores.


Fastest for loop

The fastest way to iterate through all objects of an array is by using a reverse for loop. This only works if iterating reversly is acceptable.

int c = vector.length; // number of iterations
for (int i=c-1; i>=0; --i)
{
  ...
}

The reverse for loop is a bit faster than the corresponding forward loop, because it requires one byte-code instruction less inside the loop body, because there is a special opcode for comparing values against zero.