Cint incremental bytecode compiler
# Cint incremental bytecode compilation mode ################################
In order to improve interpreter speed, cint has incremental bytecode compiler.
The bytecode compiler is excited by loop commands, for, while, do. You can
choose level of bytecode optimization by '-O[n]' command line option or
'O[n]' interactive command.
O0 : No bytecode compilation, everything is interpreted. very slow
O1 : Loops are bytecode compiled
O2 : + some instructions are optimized
O3 : + some more instructions are optimized
O4 : + function called within a loop is bytecode compiled
O5 : All interpreted functions will be bytecode compiled when called
Default is O4. You need to consult cint@pcroot.cern.ch when using O5.
There is another level O10 , but this can be only used with special care.
(Not recommended, only experimental)
O10 : all interpreted functions will be bytecode compiled when loaded
Bytecode compilation fails when it encounters limitations described in this
document. In such case, bytecode is discarded and the code is interpreted.
# Incremental bytecode compilation of loops #################################
For loop in follwing example is compiled as bytecode in the first iteration,
2nd to 10000th iterations execute bytecode.
const int NUM=10000;
double ary[NUM];
for(int i=0;i<NUM;i++) { // bytecode
ary[i] = i*2; // bytecode
} // bytecode
If interpreted function is called with in the loop, that function is compiled
as bytecode too. For example, function f() called with in for loop is compiled
as bytecode.
void f(double *p,int i) { // bytecode
p[i] = i*2; // bytecode
} // bytecode
g() {
const int NUM=10000;
double ary[NUM];
for(int i=0;i<NUM;i++) { // bytecode
f(ary,i); // bytecode
} // bytecode
}
# Limitations of bytecode compilation ######################################
Cint bytecode compiler has some limitations. To enjoy confortable execution
speed, your code must conform to following guideline.
* Don't use 'goto' statement. Use break or continue statement instead.
for(int i=0;i<10000;i++) {
if(i=1000) goto end; // bytecode aborted, will be slow
}
end:
for(int i=0;i<10000;i++) { // bytecode
if(i=1000) break; // bytecode, this is fine
} // bytecode
* Don't use 'switch' statement. Use if-else-if instead.
for(int i=0;i<10000;i++) {
switch(i) { // X bytecode aborted, will be slow
case 1: doit1(); break;
case 2: doit2(); break;
default: dodefault(); break;
}
}
for(int i=0;i<10000;i++) { // bytecode
if(1==i) doit1(); // bytecode
else if(2==i) doit2(); // bytecode
else dodefault(); // bytecode
} // bytecode
* Function called within loop, we have relaxed limitation.
void f1(int i) { // bytecode
if(100=i) goto end; // bytecode, goto is OK
switch(i) { // bytecode, switch is OK
case 1: doit1(); break; // bytecode
case 2: doit2(); break; // bytecode
default: // bytecode
dodefault(); break; // bytecode
} // bytecode
end: // bytecode
} // bytecode
void f2(int i) { // bytecode
if(100=i) return; // bytecode
if(1==i) doit1(); // bytecode
else if(2==i) doit2(); // bytecode
else dodefault(); // bytecode
} // bytecode
g() {
for(int i=0;i<NUM;i++) { // bytecode
f1(i); // bytecode, f1 runs bytecode
f2(i); // bytecode, f2 runs bytecode
} // bytecode
}
* There are additional limitations for the bytecode function.
- Don't declare automatic class object, use pointer and new statement.
- Don't return class object, use reference or pointer
- Don't take class object as argument, use reference or pointer
- Function must be less than 2000 lines
- No array and/or struct initialization
class A { ... } ; // percompiled class
struct B { double a,b; };
A f1() { // X returning class object
A a; // X automatic class object decl, abort bytecode
int x[5]={1,2,3,4,5}; // X array initialization
B b = { 1.2, 3.4 }; // X struct initialization
a.Doit();
.
// X more than 2000 lines of code
.
return(a);
}
A* f2(A* pd) { // bytecode, reference or pointer is fine
A *p = new A; // bytecode, new PrecompiledClass is fine
p->Doit(); // bytecode
delete p; // bytecode
return(pd); // bytecode
} // bytecode
g() {
for(int i=0;i<NUM;i++) { // bytecode
f1(i); // bytecode, inside f1 remain interpreted
f2(i); // bytecode, f2 runs bytecode
} // bytecode
}
# Bytecode compilation check #############################################
Unfortunately, above description is not very thorough.
There are a few other limitations which are sabtle and hard to explain.
To check bytecode compilation status, you can use following technique.
* Use -v command line option or 'debug' command from the interactive
interface. Bytecode compiler displays messages.
$ cint -v myprog.cxx
$ cint iostream.h
cint> debug
cint> { for(int i=0;i<5;i++) cout << i << endl; }
!!!bytecode compilation operator<< start FILE:/tmp/01030aaa LINE:1
Bytecode compilation of operator<< successful FILE:iostream.h LINE:346
0
Bytecode loop compilation successful FILE:/tmp/01030aaa LINE:1
1
2
3
4
cint>
* After running the loop, use 'func' or 'class [classname]' command to
show list of function. Those which successfully compiled as bytecode
are marked with '*'.
cint> func
.
(compiled) 0:0 0 public: istream& ws(istream&);
iostream.h 346:1 * 0 public: ostream& operator<<(ostream& ostr,...
iostream.h 349:1 0 public: ostream& operator<<(ostream& ostr,...
iostream.h 352:1 0 public: ostream& operator<<(ostream& ostr,...
.