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