Howto:Vectorizing the Nasal setprop() API
This article is a stub. You can help the wiki by expanding it. |
Objective
Motivation
Making setprop() faster
Background
Let's consider the following snippet of code [1]:
var path = my_graph._node.getPath();
for (var i = 0; i< size(cmd); i=i+1)
{
index1 = 2 * i;
index2 = index1+1;
setprop(path~"/cmd["~i~"]", cmd[i]);
setprop(path~"/coord["~index1~"]", coord[index1]);
setprop(path~"/coord["~index2~"]", coord[index2]);
}
This contains a single for-loop that ends up calling one Nasal extension function (setprop) 3 times per loop iteration. However, the overhead from switching between C and Nasal once per call can obviously add up, depending on the size of the cmd vector. Thus, we could greatly reduce this overhead by coming up a modified setprop() equivalent that directly operates on a path and vector accordingly:
var path = my_graph._node.getPath();
# cmd being a Nasal vector, the for-loop can be executed in native C code instead of requiring context switches:
setprop(path~"/cmd", cmd);
Understanding setprop()
One quick workaround would be providing a setprop() equivalent that directly works using Nasal vectors (arrays), i.e. which directly operate on vectors (think GLSL-like): setprop_vector("/foo/x", vector);
This would be the equivalent of the Nasal code shown above, just in C/C++ space - i.e. it would be a subset of the f_setprop() API: Howto:Extend Nasal
However, it's pretty much copy & paste actually - i.e. a new f_setpropv() function and adapting it to directly get the sie of the passed vector and use that in a for-loop to do indexed property updates directly - which should translate into N fewer C<->Nasal context switches, because it'd be all done in native C++ code at that point. We once did that to help speed up diff'ing two Nasal vectors in a fast way, and it caused a massive speed-up - I think it was James or Stuart who helped get that commited back then. [2]
// setprop() extension function. Concatenates its string arguments as
// property names and sets the value of the specified property to the
// final argument.
static naRef f_setprop(naContext c, naRef me, int argc, naRef* args)
{
// this checks if we have two arguments, and bails out if we don't
if (argc < 2) {
naRuntimeError(c, "setprop() expects at least 2 arguments");
}
// next, this will get the 2nd argument and store it as val
naRef val = args[argc - 1];
// this will look up the property matching the property path specified and store it as p
// TODO: this can be further simplified for the vector use case
SGPropertyNode* p = findnode(c, args, argc-1, true);
bool result = false;
try {
// if the value is a Nasal string, set the property to be a string
if(naIsString(val)) result = p->setStringValue(naStr_data(val));
// check if we have a vector
else if (naIsVector(val) ) {
SG_LOG(SG_GENERAL, SG_ALERT, "2nd argument to setprop() is a vector !");
for (int i=0;i<naVec_size(val);i++) {
SG_LOG(SG_GENERAL, SG_ALERT, "Updating property via vector index:" << i);
//TODO: recall and adapted version of findnode() here
}
} // vectorized
else {
// it is neither a string, nor a value, so it bails out here:
if(!naIsNum(val))
naRuntimeError(c, "setprop() value is not string or number");
if (SGMisc<double>::isNaN(val.num)) {
naRuntimeError(c, "setprop() passed a NaN");
}
// finally, treat the whole thing a s a number and update that
result = p->setDoubleValue(val.num);
}
} catch (const string& err) {
naRuntimeError(c, (char *)err.c_str());
}
// return the number to the caller
return naNum(result);
}
Thus, to fix up setprop() to also work on a vector of properties, we need to validate its arguments - by requiring the first argument to be a string (and a valid property path) and the 2nd argument should be a vector.
To do this, we can use the APIs discussed at Howto:Extend_Nasal#Argument_processing
References
References
|