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.
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 |
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
|
vectorListDescriptor |
The data for |
vertexCode |
Code for vertex functions (codelets). |
vertexFieldData |
Variable-sized fields, e.g. the data for |
vertexInstanceState |
An instance of a |
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: