I am using TTree::Draw with a user defined formula, kind of like this:
tree.Draw(“RFun::function(x)”);
where x is a leaf in the tree. RFun is a class with a static function
function. I created a CINT dictionary from RFun.h and linked it into the executable that contains the tree.Draw call.
It works, but it is quite slow.
Is there anything that can be done to speed this up, like precompiling
the formula string at the start of the executable or something like that?
// multiply by 2
int fun(int i){
return 2*i;
}
// multiply by 2 but take a second input parameter
int fun2(int i, int j){
return 2*i;
}
LinkDef.h
#ifdef __CINT__
#pragma link off all globals;
#pragma link off all classes;
#pragma link off all functions;
#pragma link C++ function fun;
#pragma link C++ function fun2;
#endif
TTreeFormula (the interpreter for TTree::Draw) is extremely optimized.
However to make an external function call, it needs to call it indirectly and use the interpreter to build up the arguments list and then call it via the function stubs from the dictionary. This is indeed amount to a significant slow down especially in your case (where the TTree is in memory and the expression is very simple).
You can recover most of the performance by avoiding to go via the interpreter by using the ‘MakeProxy’ drawing technique. For this create 2 files: //File calc.h
#include fun.h[code]//File calc.C (must have the same name before the extension as the header)
double calc() { // must have the same name as the file (without the extension)
return fun2(a,0);
}[/code]
and use it as follow:
Your solution does recover the performance pretty much completely,
but I have two problems with it:
It seems that one can only have one such macro per root session
because otherwise the compiler keeps overwriting the shared library.
In my case I have a number of formulas that I would like to draw.
How do I communicate the value of my function back to my program in an unbinned way (i.e. not using htemp)? It seems that the array that one
usually gets with tree.GetV1() does not get filled when drawing a macro.
or you can call TTree::MakeProxy to generate a skeleton and load it as a TSelector and call TTree::Process.
[quote]2. How do I communicate the value of my function back to my program in an unbinned way (i.e. not using htemp)? It seems that the array that one usually gets with tree.GetV1() does not get filled when drawing a macro. [/quote]Use
//File calc.C (must have the same name before the extension as the header)
std::vector<double> fVar1;
double calc() { // must have the same name as the file (without the extension)
double val = fun2(a,0);
fVar1.push_back(val);
return val;
}and do:
tree->MakeProxy("calcSel","calc.C");
gROOT->ProcessLine(".L calcSel.h+O");
calcSel *s = new calcSel(tree);
tree->Process(s);
// the result is in s->fVar1.
Thank you for your reply. It solves the 2 problems that I had, but
it brings up 2 new problems:
The solution will work interactively but not in compiled code because
at compilation time there is no calcSel object since that only gets created
at run time which means the line calcSel *s = new calcSel(tree); won’t compile.
The number of formulas is only defined at run time and could be large
(the executable is going to be a strip chart application) so even if the
compiler knew what calcSel is it would not be possible to hardcode
the “new” statements like this because it’s not clear how many are needed.
Is there any workaround for this like putting the code that processes
the tree into some code that’s only compiled at runtime or something
like that?
[quote]Sorry about being so hard to please. [/quote]No problem. The solution is actually quite simple thanks to the interpreter
Just do:TSelector *sel = (TSelector*)gROOT->ProcessLine(Form("new calcSel((TTree*)0x%x);",tree));once you loaded the correct code.
If you want to retrieve just the fVar1 from a known selector you can do something like: