Skip to content

PAPI LL

Treece-Burgess edited this page Jan 18, 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 measurement 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:

PAPI_library_init(''version'')

Fortran:

PAPIF_library_init(check)

Argument 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 specified component from the version number.

The following is a code example of using PAPI_library_init to initialize the PAPI library:

#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));
}

Output for PAPI Version 7.1.0

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

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]);
}

Possible Output:

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

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.


System Detection Low Level API

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

Enumeration of device types present in the system:

PAPI_enum_dev_type(modifier, **handle)

Arguments:

  • modifier -- device type modifier, used to filter out enumerated device types

Device type attribute query:

PAPI_get_dev_type_attr(*handle, attribute, *value)

Arguments:

  • *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

Device attribute query:

PAPI_get_dev_attr(*handle, dev_id, attribute, *value)

Arguments:

  • *handle -- opaque handle for device, obtained through PAPI_enum_dev_type
  • dev_id -- device attribute to query
  • attribute -- device attribute to query
  • *value -- value of the requested device attribute

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);
            }   
        }   
    }   
    PAPI_shutdown();
    exit(EXIT_SUCCESS);
}
Clone this wiki locally