Cling jit and openmp preprocessor directive

Hello,

I an trying to use cling as jit in c++ code, but I am wonder if it is possible to handle openmp preprocessor directive using jit.

If I have C++ code that need to jit for example:
gInterpreter->Declare( Form( “#pragma omp task depend(in:A[0:m]) depend(inout:B[0:n]) task1(A, B);
#pragma omp task depend(inout:B[0:n]) task2(B);”,
cl->GetName() ) );
gInterpreter->Declare( Form( “#pragma omp task depend(inout:B[0:n]) task2(B);”,
cl->GetName() ) );

void task1(double *A, double *B){
}

void task2(double *B){
}
Is it possible to jit openmp task preprocessor directive?

I’m struggling with something similar.

Since you’re calling gInterpreter, you’re running from ROOT, right? There’s a range of problems with custom compiler flags: there’s the precompiled header (allDict.cxx.pch) that will set certain things in stone during build time. There is the fact that Cling starts up at -O0. You can switch later, but for some headers, it’s too late. Finally, Cling only runs a selection of optimization passes atm. and that set seems to have been frozen out some time ago.

As it stands, cling under ROOT completely ignores #pragma omp.

I went through the moves for AVX support a short while ago, but it required hacking cling. If you’re willing to do that and are interested, I can write up what it required in my case.

Hello,

What I am interested in is to jit inside C++ code and to process task dependency information using pragma omp?
If I don’t run from ROOT is still possible to do it?
for example,
cling::Interpreter& interp ;
interp.process("#pragma omp task depend(in:A[0:m]) depend(inout:B[0:n]) task1(A, B)", …);

I would really appreciate if you share with me the requirements through AVX.

Thanks

Actually, first off, when enabling openmp within ROOT, I just found that Clang does not support the task directive (see here: https://clang.llvm.org/docs/OpenMPSupport.html). And that’s dev (7), whereas Cling contains 5. :frowning:

I was assuming you were working in ROOT b/c of “gInterpreter->Declare()” in the above code. You can make OpenMP work there (I just did :slight_smile: ), but you have to make sure to set the EXTRA_CLING_ARGS envar to ‘-fopenmp’ when building ROOT. That will make sure that all dictionaries (and allDict.cxx.pch) are build with openmp enabled, where that will then be baked in.

This is the same as for avx: both it and openmp are part code generation, part header files, and the code will miss preprocessor directives in either case if EXTRA_CLING_ARGS is not set consistently (to -fopenmp and -mavx respectively) and instead add macro’s disabling these features, leading to #pragma omp silently being ignored (even “unknown” ones such as task).

Then, also when running root.exe, EXTRA_CLING_ARGS still needs to be set to ‘-fopenmp’.

You can find the use of EXTRA_CLING_ARGS and how it is handed to the interpreter constructor in core/metacling/src/TCling.cxx where the Cling interpreter is created and given an array of options. That should be readily transferable to your own code.

Then for ROOT, you need to explicitly load libomp.so. For your own application, you can simply link with it (or use dlopen).

Here’s my ROOT example:

$ cat aap.C
void aap() {
#pragma omp parallel
    { std::cout <<  "Hello World" << std::endl; }
}

and used in a root.exe session (after building ROOT with EXTRA_CLING_ARGS=’-fopenmp’):

$ export EXTRA_CLING_ARGS='-fopenmp'
$ root.exe -l -b
root [0] gSystem->Load("libomp.so")
(int) 0
root [1] .x aap.C
Hello WorldHello WorldHello WorldHello WorldHello WorldHello WorldHello WorldHello WorldHello World
[.. snip etc., etc. ... I have lots of cores ... ..]
root [3]

That’s actually all there is to it. Except that, unfortunately, there’s no such thing as disabling openmp anymore at that point. Unsetting EXTRA_CLING_ARGS will not do the trick: it’s built into the pch … (The same problem exists for avx.) I assume, though, that as long as your program does not use pch’s or dictionaries, or keeps a separate set depending on whether -fopenmp is requested, that should not be too hard to work around.

For posterity, to continue my avx story (and in case it interests you at some later point in time): Cling creates its own set of passes, copied some long time ago from Clang, and mods some of the options. The reason for selecting its own passes and options, is that some passes really slowed down interactive use and others were needed to speed it up (eg. more aggressive inlining). But for avx to be used, the vectorization option needs to be on and the vectorization pass created. See lib/Interpreter/BackendPasses.cpp.

Now, this is different from openmp, b/c openmp is purely implemented as a pragma handler (callbacks for codegen) and Cling did not make any modifications AFAICT, nor did it disable it. This handler inserts run-time calls into the IR, not new instructions to be picked up by a later pass, so it’s not affected by later optimization passes.

Back to the avx story, setting EXTRA_CLING_ARGS='-O2 -mavx' was not sufficient, b/c Cling has its own set of optimization levels. Modifying that wasn’t enough either, because you can only do that after the fact (#pragma cling optimize 2) and then there are code paths that pull in code w/o -mavx set and when that happens, you’re done. So, I actually ended up hard-coding opt-level to a minimum of 2 if avx is enabled. And with that, avx works, too.

So to answer your original question: no, Clang doesn’t seem to support the openmp task directive. But other than that, with some effort, you can get the rest to work. Even in ROOT.

Thanks, Wim.

Everyone, I’m happy to receive patches for enabling openmp and for setting a specific architecture at build time, i.e. formalizing the EXTRA_CLING_ARGS part and possible changes in the BackendPasses.

Axel.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.

I realize that’s an old thread but this is what worked for me:

./bin/cling -fopenmp

****************** CLING ******************
* Type C++ code and press enter to run it *
*             Type .q to exit             *
*******************************************
[cling]$ .x ompdemo.C
max threads: 10
Hello World from thread = 5 with 10 threads
Hello World from thread = 8 with 10 threads
Hello World from thread = 3 with 10 threads
Hello World from thread = 7 with 10 threads
Hello World from thread = 1 with 10 threads
Hello World from thread = 9 with 10 threads
Hello World from thread = 0 with 10 threads
Hello World from thread = 4 with 10 threads
Hello World from thread = 2 with 10 threads
Hello World from thread = 6 with 10 threads
all done, with hopefully 10 threads
[cling]$ .q
(base) 18:06:20-vvassilev~/workspace/builds/cling-build$ cat ompdemo.C 
#include <stdio.h>
#include <omp.h>

#pragma cling load("libomp")
void ompdemo() {
    int max_threads = omp_get_max_threads();

    printf("max threads: %d\n", max_threads);
    omp_set_num_threads(max_threads);

#pragma omp parallel
    {
        int id = omp_get_thread_num();
        printf("Hello World from thread = %d with %d threads\n", id, omp_get_num_threads());
    }

    printf("all done, with hopefully %d threads\n", max_threads);
}