Howto:Canvas Path Benchmarking
This article is a stub. You can help the wiki by expanding it. |
Motivation
Assume you have one array (Nasal vector) you have written into a path, now you want to replace the path element by one corresponding to the second array. [2]
Code
###
# Based on, and adapted from:
# http://wiki.flightgear.org/Canvas_Snippets#Adding_OpenVG_Paths
var (width,height) = (640,480);
var title = 'Canvas.Path.setData() test';
var window = canvas.Window.new([width,height],"dialog").set('title',title);
var myCanvas = window.createCanvas().set("background", canvas.style.getColor("bg_color"));
var root = myCanvas.createGroup();
var graph = root.createChild("group");
var x_axis = graph.createChild("path", "x-axis")
.moveTo(10, height/2)
.lineTo(width-10, height/2)
.setColor(1,0,0)
.setStrokeLineWidth(3);
var y_axis = graph.createChild("path", "y-axis")
.moveTo(10, 10)
.lineTo(10, height-10)
.setColor(0,0,1)
.setStrokeLineWidth(2);
var plot = graph.createChild("path", "plot")
.moveTo(10, height/2)
.setColor(0,1,0)
.setStrokeLineWidth(0.5);
var randomPoints = func(total=3000) {
window.set('title',title~" points="~total);
var cmds = [];
var points = [];
for(var i=0;i<=total;i+=1) {
append(cmds, canvas.Path.VG_LINE_TO);
var x = 10+rand() * (width-10) + rand()*5;
var y = 10+rand() * (height-10) + rand()*8;
append(points, [x,y]);
# print("x/y: ", x, y);
}
return {cmds:cmds, points:points};
}
var setData_test = func(data) {
plot.setData( data.cmds, data.points);
}
var updateCallback = func() {
var totalPoints = rand() * 5500;
var myPoints = randomPoints(totalPoints);
debug.benchmark("setData() test, points:"~totalPoints, func setData_test(myPoints) );
}
# http://wiki.flightgear.org/Built-in_Profiler#Nasal
fgcommand("profiler-start");
var updateTimer = maketimer(0.5, updateCallback);
updateTimer.start();
settimer(func updateTimer.stop(), 30);
window.del = func()
{
print("Cleaning up window:",title,"\n");
updateTimer.stop();
call(canvas.Window.del, [], me);
fgcommand("profiler-stop");
};
Ideas
Extend nasal-props.cxx to implement props.setValues() and props.setChildren() in C++ (or directly use Nasal/CppBind).
cppbind used for Nasal/C++ bindings, but probably it'll be better to port the whole file at once). One thing I don't like is the signature of getValue and similar methods. I'd keep it similar to the core SGPropertyNode and don't allow creating new nodes with eg. getValue but instead return a default value (which can be passed as default argument). Otherwise, having relative paths is definitely a useful addition.[3]
getprop/setprop are known for not just being "quick & dirty", but also for outperforming the props.nas magic performance-wise, including the Nasal ghosts, which is plausible - because of the API and stack machine overhead. Andy himself repeatedly mentioned that several times in the past, e.g. see: http://www.mail-archive.com/flightgear-devel@lists.sourceforge.net/msg12222.html Also, Thorsten has previously run benchmarks to compare props.nas vs. setprop/getprop.
Like Tom mentioned previously, it would probably make sense to eventually port the whole props.nas stuff to use cppbind and then proceed from there, that should leave little Nasal-space overhead, and once everything is in C++ space, it will be straightforward to see how to optimie things there using the google perftools fgcommands. Performance-wise, many fgcommands and Nasal extension functions show up - even without necessarily invoking the GC. And the constant string building is a known issue, but there are certainly workarounds or special-case optimizations possible, even if it's just a simple space/time tradeoff in the form of a cached property tree. [4]
we should not provide the wrappers in props.nas (which are naCodes) and instead directly port the C++ extensions (naCCodes) to live directly in the object? That could be done using the naRef me and a hash lookup for "_g" (which should be interned and/or cached just like arg and parents are). I'm not sure I understand your last paragraph though...[5]
Related
References
References
|