Disclaimer: I guess I'll generally suggest things which are not considered 'elegant' coding - I do this because they're fast.
Fairly typical situation, we have a display and run an update loop to adapt it to the internal state of the simulation. The basic structure of the individual updates is something like
- Code: Select all
var my_variable = getprop("/sim/systems/my_variable");
my_canvas_element.setText(my_variable);
(could also be setTranslation() for a scale, or setRotation() for a gauge,..., anyway, you fetch a number, you update a canvas element with it.
Now, most of the performance impact of this is not in how canvas assembles the texture or how that texture gets rendered, it's in how Nasal interacts with the property tree. Fast canvas code has to minimize property I/O.
To gauge that, take the cost of a setprop/getprop to be 1. Then the relative cost of using props.nas if you have a pointer to the node via
- Code: Select all
var val = node.getValue();
is about 3. The relative cost of props.nas if you need to get a pointer to the node via
- Code: Select all
var val = props.globals.getNode(my_node_name).getValue();
is about 10. In contrast, the relative cost of fetching something from Nasal, for instance
- Code: Select all
var val = MyAircraft.guidance.target_array[1];
is pretty much zero - it's unmeasurably small.
So the first thing to do is that your getter method of data should always use getprop(), then the relative cost of the update code snippet above is 4 (1 for the getter, and 3 for setText which uses props.nas internally). Avoid props.nas for getter methods if you don't need to (usually you do not), and never discard the pointers to the node once you have them.
As a side note, if you have something in Nasal already (because the system you simulate happens to be implemented in Nasal) never go via the property tree to pass it to a canvas display, always pass it directly.
This leaves the setter. If you have a sizable performance issue, it is possible to by-pass the canvas API to use setprop() also for writing, cutting the total performance cost of the update down to 2.
I'd not recommend this generally though. In many instances, you can do other speedups.
1) If the getter refers to a value which you use in many places in the same frame (things like attitude or airspeed come to mind...), use a data provider - have a dedicated loop execute all getter functions you need once per frame and store the result in a Nasal data structure, use that Nasal data structure inside the canvas update loops.
Say you use airspeed in 5 different locations, then the cost of the getter function is no longer 1 but effectively 1/5.
2) If you have a status update, like an AP mode label, update it only when needed. For instance you can store a string with it
- Code: Select all
my_label.lasttext = "";
and then, rather than setText, do
- Code: Select all
var updateText = func (node, text) {
if (text == node.lasttext) {return;}
node.lasttext = text;
node.setText(text);
}
and in most frames, you'll never pay the cost for the setter function. Of course that performs worse with any elements which do change every frame, so you have to know what kind of element it is you're updating.
(It's also possible to set these with dedicated functions and pull them out of the update loop altogether, but that makes for rather messy code imo).
So, a well-organized property I/O might in an ideal case run some 4 times faster than a non-optimized code. It's unlikely that you'll always get that much, but it's not negligible either.
Enjoy speeding up your displays.