5.2. How to create burst events in OmpSs programs¶
Burst events are such with begin/end boundaries. They are useful to measure when we are starting to execute a code and when we are finalizing. Lets imagine we have the following code:
#include <stdio.h>
#define ITERS 10
#pragma omp task
void f( int n )
{
usleep(100);
fprintf(stderr,"[%3d]",n);
usleep(200);
}
int main (int argc, char *argv[] )
{
for (int i=0; i<ITERS; i++ )
f(i);
#pragma omp taskwait
fprintf(stderr,"\n");
}
And we want to distinguish the time consumed in the first usleep
from the
second. We will call them Phase 1 and Phase 2 respectively. So we will need
one new type of events (Key) and 2 new values with description. So first we
need to declare a nanos_event_t
variable and initializes type, key and value
members. The type member will be NANOS_BURST_START
or NANOS_BURST_END
depending if we are open or closing the burst. We also will use
nanos_instrument_register_key
and nanos_instrument_register_value
allowing Nanos++ to generate the appropriate Paraver configuration file. Finally we have
to raise the events from the proper place. In our case we want to surround
usleep
function calls.
The code in the function task f
will be:
#pragma omp task
void f( int n )
{
nanos_event_t e1, e2;
// Registering new event key
nanos_instrument_register_key ( &e1.key, "phases-of-f", "Phases of f()", false );
nanos_instrument_register_key ( &e2.key, "phases-of-f", "Phases of f()", false );
// Registering new event values (for key "phases-of-f")
nanos_instrument_register_value ( &e1.value, "phases-of-f", "phase-1", "Phase 1", false);
nanos_instrument_register_value ( &e2.value, "phases-of-f", "phase-2", "Phase 2", false);
// First phase
e1.type = NANOS_BURST_START;
nanos_instrument_events( 1, &e1);
usleep(100);
e1.type = NANOS_BURST_END;
nanos_instrument_events( 1, &e1);
fprintf(stderr,"[%3d]",n);
// Second phase
e2.type = NANOS_BURST_START;
nanos_instrument_events( 1, &e2);
usleep(200);
e1.type = NANOS_BURST_END;
nanos_instrument_events( 1, &e2);
}
Additionally you can use the nanos_instrument_begin_burst()
and nanos_instrument_end_burst()
which actually wrap the
behaviour of opening and closing the events. And if you don’t need to register the values you can use the functions
nanos_instrument_begin_burst_with_val()
and nanos_instrument_end_burst_with_val
:
#pragma omp task
void f( int n )
{
// Raising open event for phase-1
nanos_instrument_begin_burst ( "phases-of-f", "Phases of f()", "phase-1", "Phase 1" );
usleep(100);
// Raising close event for phase-1
nanos_instrument_end_burst ( "phases-of-f", "phase-1" );
fprintf(stderr,"[%3d]",n);
// Raising open event for phase-2
nanos_instrument_begin_burst ( "phases-of-f", "Phases of f()", "phase-2", "Phase 2" );
usleep(200);
// Raising close event for phase-2
nanos_instrument_end_burst ( "phases-of-f", "phase-2" );
for(int i=0; i<n; i++) {
nanos_event_value_t ev = i;
// Raising open event for iteration i
nanos_instrument_begin_burst_with_val ( "iterations-of-f", "Iterations of f()", &ev );
usleep(100);
// Raising close event for iteration i
nanos_instrument_end_burst_with_val ( "iterations-of-f", &ev );
}
}
This mechanism can also be used in Fortran codes as in the follow example:
!$OMP TASK
SUBROUTINE F()
IMPLICIT NONE
CHARACTER(LEN=*) :: KEY = "phase-of-f" // ACHAR(0)
CHARACTER(LEN=*) :: KEY_DESCR = "phase of f()" // ACHAR(0)
CHARACTER(LEN=*) :: VAL = "phase-1" // ACHAR(0)
CHARACTER(LEN=*) :: VAL_DESCR = "Phase 1" // ACHAR(0)
INTEGER :: ERROR
INTEGER, EXTERNAL :: NANOS_INSTRUMENT_BEGIN_BURST
INTEGER, EXTERNAL :: NANOS_INSTRUMENT_END_BURST
ERROR = NANOS_INSTRUMENT_BEGIN_BURST(KEY,KEY_DESCR,VAL, VAL_DESCR)
CALL SLEEP(1)
ERROR = NANOS_INSTRUMENT_END_BURST(KEY,VAL)
END SUBROUTINE F