Skip to content

PAPI LL

Treece-Burgess edited this page Jan 30, 2024 · 19 revisions

Low Level API

The low-level API (Application Programming Interface) manages hardware events in user-defined groups called Event Sets. It is meant for experienced application programmers and tool developers wanting fine-grained measurements and control of the PAPI interface. Unlike the high-level interface, it allows both PAPI preset and native events. Other features of the low-level API are the ability to obtain information about the executable and the hardware as well as to set options for multiplexing and overflow handling. Some of the benefits of using the low-level API rather than the high-level API are that it increases efficiency and functionality.

It should also be noted that the low-level interface could be used in conjunction with the high-level interface, as long as attention is paid to insure that the PAPI library is initialized prior to the first low-level PAPI call.

The low-level API is only as powerful as the substrate upon which it is built. Thus, some features may not be available on every platform. The converse may also be true, that more advanced features may be available on every platform and defined in the header file. Therefore, the user is encouraged to read the documentation for each platform carefully. There are approximately 50 functions that represent the low-level API. For an example using the low-level interface, see the Low Level Code Example below, or src/ctests/low-level.c in the PAPI source distribution.

Note that most functions are implemented in both C and Fortran, but some are implemented in only one of these two languages. For full details on the calling semantics of these functions, please refer to the PAPI Programmer’s Reference.


Initializing the Low Level API

The PAPI library must be initialized before it can be used. It can be initialized explicitly by calling the following low-level function:

C:

int version;
int retval = PAPI_library_init(version);

Arguments for PAPI_library_init:

  • version -- upon initialization, PAPI checks the argument against the internal value of PAPI_VER_CURRENT when the library was compiled. This guards against portability problems when updating the PAPI shared libraries on your system.

Fortran:

use iso_c_binding
integer(c_int) version
call PAPIF_library_init(version)

Fortran arguments for PAPIF_library_init:

  • version -- upon initialization, PAPI checks the argument against the internal value of PAPI_VER_CURRENT when the library was compiled. This guards against portability problems when updating the PAPI shared libraries on your system.

Note that this function must be called before calling any other low-level PAPI function.

On success, this function returns PAPI_VER_CURRENT.

On error, a positive return code other than PAPI_VER_CURRENT indicates a library version mismatch and a negative return code indicates an initialization error.

Beginning with PAPI 3.0, there are a number of options for examining the current version number of PAPI:

PAPI_VERSION produces an integer containing the complete current version including MAJOR, MINOR, and REVISION components. Typically the REVISION component changes with bug fixes or minor enhancements, the MINOR component changes with feature additions or API changes, and the MAJOR component changes with significant API structural changes.

PAPI_VER_CURRENT contains the MAJOR and MINOR components and is useful for determining library compatibility changes.

PAPI_VERSION_MAJOR, PAPI_VERSION_MINOR, PAPI_VERSION_REVISION are macros that extract a specified component from the version number.

The following is a code example of using PAPI_library_init to initialize the PAPI library and extract the major, minor and revision version numbers:

#include <papi.h>
#include <stdio.h>
#include <stdlib.h>

void handle_error (int retval)
{
    printf("PAPI error %d: %s\n", retval, PAPI_strerror(retval));
    exit(1);
}
    
int main()
{
    int retval;    

    /* Initialize the PAPI library */
    retval = PAPI_library_init(PAPI_VER_CURRENT);
    if (retval != PAPI_VER_CURRENT)
        handle_error(retval);
    
    /* Check for possible failures */
    if (retval < 0)
        handle_error(retval);

    /* Print PAPI Version */
    fprintf(stdout, "PAPI Version Number\n");
    fprintf(stdout, "MAJOR:    %d\n", PAPI_VERSION_MAJOR(retval));
    fprintf(stdout, "MINOR:    %d\n", PAPI_VERSION_MINOR(retval));
    fprintf(stdout, "REVISION: %d\n", PAPI_VERSION_REVISION(retval));

    /* Executes if all low-level PAPI
    function calls returned PAPI_OK */
    printf("\033[0;32m\n\nPASSED\n\033[0m");
    exit(0); 
}

Output for PAPI Version 7.1.0

PAPI Version Number
MAJOR:    7
MINOR:    1
REVISION: 0


PASSED

On success, all PAPI functions return PAPI_OK and the above output is returned for PAPI Version 7.1.0. On error, a non-zero error code is returned.


Low Level Code Example

The following is a simple code example that applies the same technique as the High Level example, except it uses the Low Level API:

#include <papi.h>
#include <stdio.h>
#include <stdlib.h>

void handle_error (int retval)
{
    printf("PAPI error %d: %s\n", retval, PAPI_strerror(retval));
    exit(1);
}

void do_flops( int n ) 
{
    int i;
    double a = 0.5, b = 2.2, c = 0.11;

    for ( i = 0; i < n; i++ ) { 
        c += a * b;
    }   
}

int main()
{
    int retval, EventSet = PAPI_NULL, NUM_FLOPS = 10000;
    long_long values[1];

    /* Initialize the PAPI library */
    retval = PAPI_library_init(PAPI_VER_CURRENT);
    if (retval != PAPI_VER_CURRENT)
        handle_error(retval);

    /* Create the Event Set */
    retval = PAPI_create_eventset(&EventSet);
    if (retval != PAPI_OK)
        handle_error(retval);

    /* Add Total Instructions Executed to our Event Set */
    retval = PAPI_add_event(EventSet, PAPI_TOT_INS);
    if (retval != PAPI_OK)
        handle_error(retval);

    /* Start counting events in the Event Set */
    retval = PAPI_start(EventSet);
    if (retval != PAPI_OK)
        handle_error(retval);

    /* Calling do_flops */
    do_flops(NUM_FLOPS);

    /* Read the counting events in the Event Set */
    retval = PAPI_read(EventSet, values);
    if (retval != PAPI_OK)
        handle_error(retval);

    printf("After reading the counters: %lld\n",values[0]);

    /* Reset the counting events in the Event Set */
    retval = PAPI_reset(EventSet);
    if (retval != PAPI_OK)
        handle_error(retval);

    /* Calling do_flops */
    do_flops(NUM_FLOPS);

    /* Add the counters in the Event Set */
    retval = PAPI_accum(EventSet, values);
    if (retval != PAPI_OK)
        handle_error(retval);

    printf("After adding the counters: %lld\n",values[0]);

    /* Calling do_flops */
    do_flops(NUM_FLOPS);

    /* Stop the counting of events in the Event Set */
    retval = PAPI_stop(EventSet, values);
    if (retval != PAPI_OK)
        handle_error(retval);

    printf("After stopping the counters: %lld\n",values[0]);

    /* Executes if all low-level PAPI
    function calls returned PAPI_OK */
    printf("\033[0;32m\n\nPASSED\n\033[0m");
    exit(0); 
}

Possible Output:

After reading the counters: 90833
After adding the counters: 181619
After stopping the counters: 91940


PASSED

Notice that in order to get the desired results (the second line approximately twice as large as the first line), PAPI_reset was called to reset the counters, since PAPI_read did not reset the counters.

On success, all PAPI functions return PAPI_OK and the possible above output is returned. On error, a non-zero error code is returned.


System Detection Low Level API

The sysdetect component comes with three additional functions that allow users to access system information.

C:

int modifier;
void *handle;
int retval = PAPI_enum_dev_type(modifier, &handle);

Arguments for PAPI_enum_dev_type:

  • modifier -- device type modifier, used to filter out enumerated device types.
  • handle -- opaque handle.
void *handle, *value;
PAPI_dev_type_attr_e attribute;
int retval = PAPI_get_dev_type_attr(handle, attribute, &value);

Arguments for PAPI_get_dev_type_attr:

  • handle -- opaque handle for device, obtained through PAPI_enum_dev_type.
  • attribute -- device type attribute to query.
  • value -- value of the requested device type attribute.
void *handle, *value;
int dev_id;
PAPI_dev_attr_e attribute;
int retval = PAPI_get_dev_attr(handle, dev_id, attribute, &value);

Argument for PAPI_get_dev_attr:

  • handle -- opaque handle for device, obtained through PAPI_enum_dev_type.
  • dev_id -- integer identifier of queried device.
  • attribute -- device attribute to query.
  • value -- value of the requested device attribute.

Fortran:

use iso_c_binding
integer(c_int) modifier, handle_index, check
call PAPIF_enum_dev_type(modifier, handle_index, check)

Fortran arguments for PAPIF_enum_dev_type:

  • modifer -- device type modifier, used to filter out enumerated device types.
  • handle_index -- stores handle of next device type.
  • check -- an error return value for Fortran.
    use iso_c_binding
    integer(c_int) handle_index, attribute, value, check
    character(PAPI_MAX_STR_LEN, c_char) string
    call PAPIF_get_dev_type_attr(handle_index, attribute,
& value, string, check)

Fortran arguments for PAPIF_get_dev_type_attr:

  • handle_index -- opaque handle for device, obtained through PAPIF_enum_dev_type.
  • attribute -- device type attribute to query.
  • value -- stores value of the requested device attribute if the selected attribute returns a numeric value.
  • string -- stores value of the requested device attribute if the selected attribute returns either a character or character string.
  • check -- an error return value for Fortran.
    use iso_c_binding
    integer(c_int) handle_index, dev_id, attribute, value, check
    character(PAPI_MAX_STR_LEN, c_char) string
    call PAPIF_get_dev_attr(handle_index, dev_id, attribute,
& value, string, check)

Fortran arguments for PAPIF_get_dev_attr:

  • handle_index -- opaque handle for device, obtained through PAPIF_enum_dev_type.
  • dev_id -- integer identifier of queried device.
  • attribute -- device attribute to query.
  • value -- stores value of the requested device attribute if the selected attribute returns a numeric value.
  • string -- stores value of the requested device attribute if the selected attribute returns either a character or character string.
  • check -- an error return value for Fortran.

Code Example

#include <papi.h>
#include <stdio.h>
#include <stdlib.h> 

void handle_error (int retval)
{
    printf("PAPI error %d: %s\n", retval, PAPI_strerror(retval));
    exit(1);
}

int main()
{
    /* sysdetect device type count and id */
    int retval, id, count;
    /* sysdetect enum modifier (filter devices by type) */
    int enum_modifier = PAPI_DEV_TYPE_ENUM__CPU | PAPI_DEV_TYPE_ENUM__CUDA;
    /* sysdetect device type opaque handle */
    void *handle;

    /* Initialize the library */
    retval = PAPI_library_init(PAPI_VER_CURRENT);
    if (retval != PAPI_VER_CURRENT)
        handle_error(retval);

    /* scan through device types available in the system */
    while (PAPI_OK == PAPI_enum_dev_type(enum_modifier, &handle)) {
        retval = PAPI_get_dev_type_attr(handle, PAPI_DEV_TYPE_ATTR__INT_PAPI_ID, &id);
        /* check retval for errors */
        if (retval != PAPI_OK)
            handle_error(retval);

        retval = PAPI_get_dev_type_attr(handle, PAPI_DEV_TYPE_ATTR__INT_COUNT, &count);
        /* check retval for errors */
        if (retval != PAPI_OK)
            handle_error(retval);

        if (PAPI_DEV_TYPE_ID__CUDA == id) {
            for (int i = 0; i < count; ++i) {
                unsigned int cc_major, cc_minor;
                retval = PAPI_get_dev_attr(handle, i, PAPI_DEV_ATTR__CUDA_UINT_COMP_CAP_MAJOR, &cc_major);
                /* check retval for errors */
                if (retval != PAPI_OK)
                    handle_error(retval);

                retval = PAPI_get_dev_attr(handle, i, PAPI_DEV_ATTR__CUDA_UINT_COMP_CAP_MINOR, &cc_minor);
                /* check retval for errors */
                if (retval != PAPI_OK)
                    handle_error(retval);
            }   
        }   
    }   
    /* Executes if all low-level PAPI
    function calls returned PAPI_OK */
    PAPI_shutdown();
    printf("\033[0;32mPASSED\n\033[0m");
    exit(0); 
}

Output

PASSED

On success, all PAPI functions return PAPI_OK and the above output is returned. On error, a non-zero error code is returned.