PopVision analysis library

The PopVision analysis library (libpva) allows programmatic analysis of the Poplar profiling information.

The library provides both C++ and Python APIs that can be used to query the Poplar profiling information.

This document describes the features of libpva and will use Python for most of the examples.

For details of the API please refer to the PopVision Analysis Python API or the PopVision Analysis C++ API.

Capturing IPU reports

In order to use libpva you must first generate a profile of your application. The simplest way is to use the POPLAR_ENGINE_OPTIONS environment variable and set the autoReport.all option to true.

For instance to collect a profile report from my_application.py, set the environment variable and the profile.pop file will be created in the current working directory:

POPLAR_ENGINE_OPTIONS='{"autoReport.all":"true"}' python3 my_application.py

Opening a report

After you have generated a report, you can open it by calling the openReport() function and passing the location of the report:

import pva

report = pva.openReport("./profile.pop")

If you are using Poplar there is a convenience method, poplar::Engine::getReport(), that you can use to open a report. This is a useful method, because the report has a temporary filename until the engine is destroyed, when it will be renamed to profile.pop

auto report = engine.getReport();

Reading a report

After you have opened the report you can query it for information on the compilation and execution of your application. The following sections describe common queries.

Reading target information

You can read information about the target from the target object. More information can be found in the description of the Target class.

numIpus = report.compilation.target.numIPUs
numReplicas = report.compilation.target.numReplicas

Reading memory usage by tile

You can read information about how much memory is used on each tile:

report.compilation.tiles[0].memory.total.includingGaps
report.compilation.tiles[1].memory.total.includingGaps
...

Or sum the total across all tiles:

sum(tile.memory.alwaysLiveBytes for tile in report.compilation.tiles)

Reading the liveness information

To look at liveness information for each program step you can iterate over the livenessProgramSteps array

for step in report.compilation.livenessProgramSteps:
    print(step)
    totalNotAlwaysLive += step.notAlwaysLiveBytes

Each step relates to a Poplar program instance. To view the details of the programs you have to create a ProgramVisitor class. The appropriate visit method will then be called depending on the type of the program.

class MyVisitor(pva.ProgramVisitor):
    def visitOnTileExecute(self, onTileExecute):
        # Details of the on tile execute
        totalNumVertices += len(onTileExecute.computeset.vertices)

    def visitDoExchange(self, doExchange):
        # Details of the exchange program
        pass


myVistor = MyVisitor()
for step in report.compilation.livenessProgramSteps:
   step.program.accept(myVistor)

Reading the execution steps

The preceding examples read information based on the compilation of an application. When an application is executed and the Poplar instrumentation is enabled then the report will also contain the execution profile. Instrumentation is enabled by default when autoReport.all is set to true.

You can iterate over the execution steps and query how many cycles each step took. A step refers to an execution of a Poplar Program object, for instance an OnTileExecute or a DoExchange program.

for step in report.execution.steps:
    # It is possible the step could contain more than 1 compute set
    if len(step.computeSets) > 1 :
        totalCycles = sum(step.computeSets[0].cyclesByTile)
    step.program.accept(myVisitor)

Again you can use the visitor, as described above, to inspect the details of the program.