1. Introduction

Warning

The data formats described in this document are no longer supported. They are replaced by the PopVision analysis API.

There are various forms of profiling data that can be generated by the Poplar tools. Several of these can be used as input to the PopVision Graph Analyser, which will give an interactive, graphical view of the data. For more information see the PopVision Graph Analyser User Guide.

This document describes the contents of the data files and may be useful to those looking for a more detailed understanding of program execution, or who want to create their own profiling or analysis tools.

Warning

Profiling is a rapidly changing part of Poplar, so this information is subject to change without notice.

1.1. Types of profiling data

The following profile data can be generated by the Poplar SDK.

  • Summary report: basic information about the host and execution environment.

  • graph-profile: static profile information about a compiled graph and the resources required.

  • execution-profile: information about the last execution of a graph including what code was run, and cycle counts for execution, exchange and sync.

  • lowered-vars: provides information about variable and memory use.

1.2. Storage categories

Both the graph profile and the lowered variables report, show the “category” of data storage. These categories are defined in the table below.

Table 1.1 Storage categories

Category

Description

constant

Constants added by the user. Variables added by the compiler that happen to be constant will be categorised as “variable”.

controlCode

Code for Program objects and running compute sets.

controlId

Variables that are used in switch programs or variables that store a sync ID for tracking host/device synchronisation points.

controlTable

A table that lists the vertices to run in each compute set. Only used if the table scheduler is enabled.

copyDescriptor

Copy descriptors are special variable-sized fields used by copy vertices.

globalExchangeCode

Code for performing exchange operations between IPUs.

globalExchangePacketHeader

Packet headers for exchange operations between IPUs.

globalMessage

Message variables holding data being sent between IPUs.

hostExchangeCode

Code for performing exchange operations to and from the host

hostExchangePacketHeader

Data used as packet headers for hist exchange.

hostMessage

Message variables holding data being sent or received from the host.

instrumentationResults

Storage for profiling information.

internalExchangeCode

Code for performing internal exchanges.

message

Message data for internal exchanges.

multiple

Space shared by variables from multiple different categories.

outputEdge

Storage for output edge data before an exchange takes place.

rearrangement

Variables holding rearranged versions of tensor data. A rearranged variable will never be always live as it is only required in the context of a specific compute set.

sharedCodeStorage

Code shared bey vertices.

sharedDataStorage

Data shared by vertices.

stack

The work and supervisor stacks allocated on the specified tile. For more information about worker stack allocation see the Vertex Assembly Programming Guide.

variable

Variables created in the program (for example, created by the Poplar graph.addVariable() function).

vectorListDescriptor

The data for VectorList<Input<...>, DeltaN> fields.

vertexCode

Code for vertex functions (codelets).

vertexFieldData

Variable-sized fields, e.g. the data for Vector<float>, Vector<Input<...>> and InputSet<...> fields.

vertexInstanceState

An instance of a Vertex class object. This will be sizeof(VertexName) for each vertex.

1.3. Variable liveness

The concept of “liveness” is important to understand the use of memory by variables through the lifetime of a program. If a variable is “live”, it means it holds a value that may need to be read in future. Therefore, that memory cannot be reused until the variable is no longer live.

Poplar does liveness analysis using a standard algorithm except that Poplar’s variables are not scalar values; they are arrays. In the standard analysis a variable is “killed” when it is written to with a new value. This means that it is dead immediately before that point because its value there can never be read.

int a = 1;
// a is dead here because its current value (1) can never be read.
a = 2; // a is killed here, which makes it dead on the line above.

In Poplar, a variable is killed when all of its elements are written in the same compute set. Consider the pseudo-code:

var = graph.addVariable(FLOAT, {2}, ...);

seq.add(Execute( var[0] = 1, var[1] = 2 ));
// var is dead here (it is killed on the line below) because none of its
// element values (1, 2) can ever be read.
seq.add(Execute( var[0] = 3, var[1] = 4 ));

If only some of the elements are written, then the entire variable is still live before the write because we may still need the value of an element that was not written to.

seq.add(Execute( var[0] = 1, var[1] = 2 ));
// var is alive here because the value 2 might be read later.
seq.add(Execute( var[0] = 3 ));

Here var is still alive because no compute set writes to every element. If the entire variable is overwritten but in separate compute sets, then it will still be considered to be live because Poplar does not track the liveness of each variable element, only the entire variable.

seq.add(Execute( var[0] = 1, var[1] = 2 ));
// var is alive here even though 1 and 2 can never be read.
seq.add(Execute( var[0] = 3 ));
seq.add(Execute( var[1] = 4 ));

This may mean that var is considered to be alive longer than necessary, which could lead to increased memory use. The WriteUndef program can be used to explicitly mark a variable as no longer alive.

For more information about liveness analysis see: