3.4. Compiling ROOT Macros
Table of Contents
Interpreted execution
When you run a macro in ROOT by typing something like
root> .x myMacro.C
ROOT uses its C++ interpreter to read, parse, and execute the code on the fly. This is called interpreted execution.
In interpreted mode you do not produce a compiled binary file. ROOT reads your .C file each time you execute it, translates it with its interpreter, and then runs it. This has several practical consequences that are important for your workflow.
Interpreted execution is very convenient while you are developing or debugging a macro. You can edit the file, save it, run .x myMacro.C again, and immediately see the effect of your changes. You do not need to run a compiler manually or wait for a build step. For beginners this usually feels similar to working in a scripting language.
ROOT’s interpreter is quite powerful, but it does not support every modern C++ feature equally well, and it performs additional work at runtime to interpret your code. This gives interpreted macros two main characteristics: they tend to start quickly for tiny scripts, but they can become noticeably slower when you run long event loops or heavy numerical calculations; and sometimes interpreter limitations or subtle differences from compiled C++ can show up when you use more advanced language features.
When you call functions from an interpreted macro, ROOT resolves them at runtime. If the function is in another interpreted macro that has been loaded, or in a shared library that ROOT knows about, the call will work. You can also define functions inside your macro file, and the interpreter will see them immediately.
For many small tasks like trying out commands, making quick plots, or testing a few lines of analysis logic, interpreted execution is more than enough and keeps your workflow simple.
Important: Interpreted macros are easy to edit and run, but they can be significantly slower for large loops or CPU intensive work, and support for some advanced C++ features may be limited compared to fully compiled code.
ACLiC compilation
ROOT provides a built in way to compile macros into optimized machine code called ACLiC. The name comes from Automatic Compiler of Libraries for CINT, but you only need to remember the user side: you can ask ROOT to compile a macro by changing the command you use to run it.
Instead of
root> .x myMacro.C
you can type
root> .x myMacro.C+
The plus sign tells ROOT to compile the macro with an external C++ compiler, such as g++ or clang, and then to load the resulting shared library and execute the macro from that compiled code.
When you run .x myMacro.C+ for the first time, ROOT performs several steps. It calls the system compiler, passes the appropriate include paths and libraries for ROOT, and builds a shared library file, typically something like myMacro_C.so or myMacro_C.dylib depending on your platform. It also generates some helper files, such as a dictionary, that allow ROOT to interact with your compiled code. This first compilation step takes extra time compared to interpreted execution because a real compilation happens.
After the compilation succeeds, ROOT loads the resulting library and calls the entry function in your macro. If your macro defines a function called myMacro(), ROOT will execute it from the compiled library rather than through the interpreter.
A useful feature of ACLiC is that ROOT checks timestamps. If you run .x myMacro.C+ again without changing the source file, ROOT detects that the compiled library is still up to date and reuses it instead of recompiling. If you edit myMacro.C and save it, the next .x myMacro.C+ triggers a new compilation automatically. This makes iteration reasonably convenient, while still giving you the performance benefits of compiled code.
There are a few related variants of the plus sign syntax that you may see:
| Command | Meaning |
|---|---|
.x macro.C+ | Compile if needed, then load and run the compiled macro |
.x macro.C++ | Force recompilation every time, then load and run |
In most cases .x macro.C+ is what you want, because it avoids unnecessary recompilation.
ACLiC compilation uses the same compiler flags that ROOT itself was built with, plus some additional options suitable for generating shared libraries. This means your compiled macros are real C++ code and can use the same language features and optimizations you would expect in a normal C++ project, including templates, standard library headers, and more advanced constructs.
Important: Adding a + after the macro name, for example .x myMacro.C+, tells ROOT to compile the macro into a shared library, load it, and run it as fast compiled C++ code.
When compilation is useful
Using ACLiC is not always necessary. For very small macros or one line tests in the interactive shell, interpreted execution is usually simpler and fast enough. However, compilation becomes very useful in several common analysis situations.
The first and most obvious case is when your macro runs large event loops or heavy numerical calculations. For example, if your macro reads millions of entries from a TTree, applies selection cuts, fills many histograms, and performs complex physics calculations, the overhead of the interpreter can become important. Compiled code typically runs much faster. In such cases, switching from .x myMacro.C to .x myMacro.C+ can give a large speedup, often by a factor that is clearly noticeable on real analysis jobs.
Compilation is also helpful when you rely on more advanced C++ language features that the interpreter may not fully support or may treat differently. Although modern ROOT interpreters are quite capable, compiled mode remains closer to standard C++ behavior. If you encounter strange interpreter errors or limitations while using templates, modern standard library classes, or tricky header configurations, compiling the macro often resolves the problem.
Another situation where compilation is useful is when you want to reuse your code in a more modular way. A compiled macro produces a shared library that ROOT can load in many sessions. You can write functions in a macro, compile them once with ACLiC, and then call those functions from other macros or from the interactive prompt without recompiling every time, as long as the library is loaded. This is a step toward organizing your analysis like a small C++ project.
Compiled execution also matters when you care about reproducibility and stability of your analysis environment. Once compiled, your macro code is fixed in a binary library, and repeated runs with the same inputs are more isolated from interpreter side effects, such as previously defined symbols or interactive changes. This becomes more important as your analysis grows in complexity.
You should also consider compilation when you run on batch systems or cluster environments, where long running jobs process large datasets without user interaction. In that context you usually want maximum performance and reliable C++ behavior. Many analyses are developed interactively with interpreted macros during the early stages, then switched to ACLiC compiled macros for large scale production runs.
On the other hand, there are cases where interpreted execution remains preferable. If you are experimenting with small changes, trying out a different cut value, or quickly adjusting a plot style, the extra time required for compilation may outweigh the runtime benefits. In those early exploratory phases, staying with .x myMacro.C keeps the edit run cycle very fast.
Rule of thumb: Use interpreted macros while developing and experimenting, then switch to ACLiC compiled macros when your code is stable, runs large loops, or needs the performance and reliability of full C++ compilation.
Views: 10
KAHIBARO