CUBE4 API

Creating CUBE Files

The CUBE data format in a tar envelope, having extension .cubex, with various files. Description of the cube stored in file anchor.xml inside of the cubex instance. The data stored in binary format in various files inside of the cubex file. The CUBE library provides an interface to create CUBE files. It is a simple class interface and includes only a few methods. This section first describes the CUBE and then presents a simple C++ program as an example of how to use it.

CUBE API

The class interface defines a class Cube. The class provides a default constructor and nearly forty methods. The methods are divided into five groups. The first three groups are used to define the three dimensions of the performance space, forth group is used to enter the actual data and last group is used to open or to save the cube report. In addition, an output operator << to export the data into CUBE3 format is provided.

Metric Dimension

This group refers to the metric dimension of the performance space. It consists of a single method used to build metric trees. Each node in the metric tree represents a performance metric. Metrics have different units of measurement. The unit can be either ``sec'' (i.e., seconds) for time based metrics, such as execution time, or ``occ'' (i.e., occurrences) for event-based metrics, such as floating-point operations. During the establishment of a metric tree, a child metric is usually more specific than its parent, and both of them have the same unit of measurement. Thus, a child performance metric has to be a subset of its parent metric (e.g., system time is a subset of execution time).

Metric* def_met(const std::string &disp_name, const std::string &uniq_name,
const std::string &dtype, const std::string &uom,
const std::string &val, const std::string &url,
const std::string &descr, Metric* parent,
TypeOfMetric type_of_metric = CUBE_METRIC_EXCLUSIVE,
const std::string& expression = "",
TypeOfMetric is_ghost = CUBE_METRIC_NO_GHOST);

Returns a metric with display name disp_name, unique name uniq_name and description descr.

dtype
specifies the data type, which can either be <tt>INTEGER</tt>'' or FLOAT''.
uom
is the unit of measurement, which is either <tt>sec</tt>'' for seconds orocc'' for number of occurrences.
val
specifies whether there is any data available for this particular metric. It can either be ``VOID'' (no data available, metric will not be shown in CUBE) or an empty string (metric will be shown and data is present).
parent
is a previously created metric which will be the new metric's parent. To define a root node, use NULL instead.
url
is a link to an page describing the new metric in detail. If you want to mirror the page at several locations, you can use the macro @mirror@ as a prefix, which will be replaced by an available mirror defined using def_mirror() (see Section ).
type_of_metric
specifies the nature of this metric. If you want to store exclusive (along call tree) values, use a constant CUBE_METRIC_EXCLUSIVE, if you want to store inclusive (along call tree) values, use a constant CUBE_METRIC_EXCLUSIVE, if you want to define a derived metric, use one of the constants: CUBE_POSTDERIVED_METRIC, CUBE_PREDERIVED_METRIC_EXCLUSIVE or CUBE_PREDERIVED_METRIC_INCLUSIVE.
expression

is a CubePL expression to specify the derived metric.

is_ghost
is used internally by CubePL Engine and can be left with default value CUBE_METRIC_NO_GHOST

For further information about kinds of the derived metrics in cube and about CubePL syntax see .

const std::vector<Metric*>& get_metv() const;

Returns a vector with all metrics in the CUBE object.

const std::vector<Metric*>& get_root_metv() const;

Returns a vector with all roots of the metric dimension in the CUBE object.

Metric* get_met( const std::string& uniq_name) const;

Returns a metric with the given uniq_name. Returns NULL if the CUBE object doesn't contain a metric with this name.

Metric* get_root_met( Metric * met);

Returns the root metric for the given metric met.

Program Dimension

This group refers to the program dimension of the performance space. The entities presented in this dimension are region, call site, and call-tree node (i.e., call paths). A region can be a function, a loop, or a basic block. Each region can have multiple call sites from which the control flow of the program enters a new region. Although we use the term call site here, any place that causes the program to enter a new region can be represented as a call site, including loop entries. Correspondingly, the region entered from a call site is called callee, which might as well be a loop. Every call-tree node points to a call site. The actual call path represented by a call-tree node can be derived by following all the call sites starting at the root node and ending at the particular node of interest. The user can choose among three ways of defining the program dimension:

  1. Call tree with line numbers
  2. Call tree without line numbers
  3. Flat profile

A call tree with line numbers is defined as a tree whose nodes point to call sites. A call tree without line numbers is defined as a tree whose nodes point to regions (i.e., the callees). A flat profile is simply defined as a set of regions, that is, no tree has to be defined.

Region* def_region
(const std::string &name, long begln, long endln,
const std::string &url, const std::string &descr,
const std::string &mod);

Returns a new region with region name name and description descr. The region is located in the module mod and exists from line begln to line endln.

url
is a link to an page describing the new region in detail. For example, if the region is a library function, the url can point its documentation. If you want to mirror the page at several locations, you can use the macro @mirror@ as a prefix, which will be replaced by an available mirror defined using disp{def_mirror()} (see Section ).
Cnode* def_cnode
(Region* callee,
const std::string &mod, int line,
Cnode* parent);

Returns a new call-tree node representing a call from call site located at the line line of the module mod. The call tree node calls the callee callee (i.e., a previously defined region). parent is a previously created call-tree node which will be the new one's parent. To define a root node, use NULL instead. This method is used to create a call tree with line numbers.

Cnode* def_cnode
(Region* region,
Cnode* parent);

Defines a new call-tree node representing a call to the region region. parent is a previously created call-tree node which will be the new one's parent. To define a root node, use NULL instead. Note that different from the previous def_cnode(), this method is used to create a call-tree without line numbers where each call-tree node points to a region.

To define a call tree with line numbers use def_cnode(Region*, string, int ...). To define a call tree without line numbers use def_cnode(Region*, Cnode*) instead. To create a flat profile use neither one — just defining a set of regions will be sufficient.

const std::vector<Region*>& get_regv() const;

Returns a vector with all regions in the CUBE object.

const std::vector<Cnode*>& get_cnodev() const;

Returns a vector with all call-tree nodes in the CUBE object.

Cnode* get_cnode(Cnode & cn) const;

Search a call-tree node cn. Returns NULL if the CUBE object does not contain the given call-tree node.

System Dimension

This group refers to the system dimension of the performance space. It reflects the system resources which the program is using at runtime. The entities present in this dimension are machine, node, process, thread, which populate four levels of the system hierarchy in the given order. That is, the first level consists of machines, the second level of nodes, and so on. Finally, the last (i.e., leaf) level is populated only by threads. The system tree is built in a top-down way starting with a machine. Note that even if every process has only one thread, users still need to define the thread level.

Machine* def_mach (const std::string &name, const std::string &desc);

Returns a new machine with the name name and description desc.

Node* def_node (const std::string &name, Machine* mach);

Returns a new () node which has the name name and which belongs to the machine mach.

Process* def_proc
(const std::string &name, int rank,
Node* node);

Returns a new process which has the name name and the rank rank. The rank is a number from 0 to $ (n-1) $, where $ n $ is the total number of processes. applications may use the rank in MPI_COMM_WORLD. The process runs on the node node.

Thread* def_thrd
(cosnt std::string name&, int rank,
~Process* proc);

Defines a new thread which has the name name and the rank rank. The rank is a number from 0 to $ (n-1) $, where $ n $ is the total number of threads spawned by a process. Openapplications may use the Openthread number. The thread belongs to the process proc.

const std::vector<Sysres*>& get_sysv() const;

Returns a vector with all system resources (e.g. node, thread, process) available in the CUBE object.

const std::vector<Machine*>& get_machv() const;

Returns a vector with all machines in the CUBE object.

const std::vector<Node*>& get_nodev() const;

Returns a vector with all nodes of all machines in the CUBE object.

const std::vector<Process*>& get_procv() const;

Returns a vector with all processes in the CUBE object.

const std::vector<Thread*>& get_thrdv() const;

Returns a vector with all threads in the CUBE object.

Machine * get_mach(Machine & mach) const;

Search for the machine mach in the CUBE object. Returns NULL if the CUBE object does not contain the given machine.

Node *get_node(Node & node) const;

Search for the node node in the CUBE object. Returns NULL if the CUBE object does not contain the given node.

Virtual Topologies

Virtual topologies are used to describe adjacency relationships among machines, nodes, processes or threads. A topology usually consists of a single class of entities such as threads or processes. The provides a set of functions to create Cartesian topologies and to define the machine/node/process/thread mappings onto coordinates. Note that the definition of virtual topologies is optional.

Cartesian* def_cart
(long ndims, const std::vector<long>& dimv,
const std::vector<bool>& periodv);

Defines a new Cartesian topology. ndims and dimv specify the number of dimensions and the size of each dimension. periodv specifies the periodicity for each dimension. Currently,the maximum value for ndims is three.

void def_coords
(Cartesian* cart, Sysres* sys,
const std::vector<long>& coordv);

Maps a specific system resource onto a Cartesian coordinate. The system resource sys may be a machine, node, process or a thread. It is not recommended to map a mixed set of entities onto one topology (e.g., machines and threads are located in the same topology). The parameter of cart has been defined by the above def_cart() method.

const std::vector<Cartesian *>& get_cartv () const;

Returns a vector of all cartesian topologies available in the CUBE object.

const Cartesian * get_cart ( int i) const;

Returns in i-th topology in the CUBE object.

Severity Mapping

After the establishment of the performance space, users can assign severity values to points of the space. Each point is identified by a tuple (met, cnode, thrd) . The value should be inclusive with respect to the metric, but exclusive with respect to the call-tree node, that is it should not cover its children. The default severity value for the data points left undefined is zero. Thus, users only need to define non-zero data points.

void set_sev
(Metric* met, Cnode* cnode,
Thread* thrd, double value);

Assigns the value value to the point (met, cnode, thrd).

void add_sev
(Metric* met, Cnode* cnode,
Thread* thrd, double value);

Adds the value value to the present value at point (met, cnode, thrd).

The previous two methods set_sev() and add_sev() are intended to be used when the program dimension contains a call tree and not a flat profile. As the flat profile does not require the definition of call-tree nodes, the following two functions should be used instead:

void set_sev
(Metric* met, Region* region,
Thread* thrd, double value);

Assigns the value value to the point (met, region, thrd).

void add_sev
(Metric* met, Region* region,
Thread* thrd, double value);

Adds the value value to the present value at point (met, region, thrd).

double get_sev ( Metric * met, Cnode * cnode, Thread * thrd) const;

Returns the value for the point (met, cnode, thrd).

Cube library provides various calls of type get_sev, which allow to perform different ways of aggregations.

Here is the short list

double get_sev ( Metric * met, CalculationFlavour mf,
Cnode * cnode, , CalculationFlavour cf,
Thread * thrd, CalculationFlavour sf) const;
double get_sev ( Metric * met, CalculationFlavour mf,
Region * region, , CalculationFlavour rf,
Thread * thrd, CalculationFlavour sf) const;
double get_sev ( Metric * met, CalculationFlavour mf,
Cnode * cnode, , CalculationFlavour cf) const;
double get_sev ( Metric * met, CalculationFlavour mf,
Region * region, , CalculationFlavour rf) const;
double get_sev ( Metric * met, CalculationFlavour mf) const;

With CalculationFlavour one calculates either inclusive or exclusive value along the corresponding tree. Value cube::CUBE_CALCULATE_EXCLUSIVE stands for exclusive value and value cube::CUBE_CALCULATE_INCLUSIVE - for inclusive.

Miscellaneous

Often users may want to define some information related to the CUBE file itself, such as the creation date, experiment platform, and so on. For this purpose, CUBE allows the definition of arbitrary attributes in every CUBE data set. An attribute is simply a key-value pair and can be defined using the following method:

void def_attr (const std::string &key, const std::tring &value);

Assigns the value value to the attribute key.

CUBE allows using multiple mirrors for the online documentation associated with metrics and regions. The url expression supplied as an argument for def_metric() and def_region() can contain a prefix @mirror@. When the online documentation is accessed, CUBE can substitute all mirrors defined for the prefix until a valid one has been found. If no valid online mirror can be found, CUBE will substitute the ./doc directory of the installation path for @mirror@.

void def_mirror (const std::string &mirror);

Defines the mirror mirror as potential substitution for the prefix @mirror@.

std::string get_attr(const std::string &key) const;

Returns the attribute in the CUBE object stored for the given key.

const std::map<std::string, std::string> get_attrs() const;

Returns all attributes associated to the CUBE object as a map.

const std::vector<std::string>& get_mirrors() const;

Returns all mirrors defined in the CUBE object.

int get_num_thrd() const;

Returns the maximal number of threads per process in the CUBE object.

void setGlobalMemoryStrategy( CubeStrategy strategy);

Sets same memory usage strategy for all metrics. Possible values are:

  • CUBE_MANUAL_STRATEGY
  • CUBE_ALL_IN_MEMORY_STRATEGY
  • CUBE_LAST_N_ROWS_STRATEGY
void setMetricMemoryStrategy( Metric* metric, CubeStrategy strategy);

Sets memory usage strategy for selected metric.

void dropRowInMetric( Metric* metric, Cnode * cnode);

Removes data row for the cnode from the memory of metric if its memory straregy allows.

In case of CUBE_MANUAL_STRATEGY it is allways the case.

In case of CUBE_ALL_IN_MEMORY_STRATEGY it is never the case and this call has no action.

void dropRowInAllMetrics(Cnode * cnode);

Removes data row for the cnode from the memory of all metrics if their memory straregy allows.

void reroot_cnode(Cnode * cnode);

Removes all parents of the cnode and sets it as a root.

void prune_cnode(Cnode * cnode);

Removes the cnode and its subtree and sets its parent as a leaf.

void set_cnode_as_leaf(Cnode * cnode);

Removes its subtree.

void set_statistics_name(const std::string& name);

Stores the name of the statistic file inside of cube report.

std::string get_statistics_name() const;

Returns the name of the statistic file if stored.

void enable_flat_tree(const bool status);

Enables or disables the calculation of the flat tree. For some applications flat tree doesn't make sense.

bool is_flat_tree_enabled() const;

Returns whether calculation of flat tree is enabled or disabled.

void set_metrics_title(const std::string& title);

Sets the title for the metric dimension. In some applications with CUBE name metric is misleading.

std::string&
set_metrics_title() const;

Returns the title of the metric dimension.

void set_calltree_title(const std::string& title);

Sets the title for the program dimension. In some applications with CUBE name calltree is misleading.

std::string&
set_calltree_title() const;

Returns the title of the program dimension.

void set_systemtree_title(const std::string& title);

Sets the title for the system dimension. In some applications with CUBE name System is misleading.

std::string&
set_systemtree_title() const;

Returns the title of the system dimension.

vector<string>
get_misc_data();

Returns a list with names of all files, stored inside of the cubex container. This list includes files with description of the cube and metric data.

vector<char>
get_misc_data(const std::string& name);

Returns content of the file name if present, otherwise empty vector.

void
write_misc_data(const std::string& name,
const chat* buffer,
size_t length);

Writes content of the buffer of length chars as a file with a name name.

void
write_misc_data(const std::string& name,
std:vector<char> buffer);

Alternatice call to previous.

Writer Library in C

In order to create data files, another possibility is to use the C version of the CUBE writer API. The interface defines a struct cube_t and provides the following functions:

cube_t* cube_create();

Returns a new CUBE structure.

void cube_free(cube_t* c);

Destroys the given CUBE structure.

cube_metric* cube_def_met
(cube_t* c, const char* disp_name,
const char* uniq_name, const char* dtype,
const char* uom, const char* val,
const char* url, const char* descr,
cube_metric* parent);

Returns a new metric structure.

cube_region* cube_def_region
(cube_t* c, const char* name, long begln,
long endln, ~const char* url,
const char* descr, const char* mod);

Returns a new region.

cube_cnode* cube_def_cnode_cs
(cube_t* c, cube_region* callee,
const char* mod, int line,
cube_cnode* parent);

Returns a new call-tree node structure with line numbers.

cube_cnode* cube_def_cnode
(cube_t* c, cube_region* callee,
cube_cnode* parent);

Returns a new call-tree node structure without line numbers.

cube_machine* cube_def_mach
(cube_t* c, const char* name
const char* desc);

Returns a new machine.

cube_node* cube_def_node
(cube_t* c, const char* name,
cube_machine* mach);

Returns a new node.

cube_process* cube_def_proc
(cube_t* c, const char* name,
int rank, cube_node* node);

Returns a new process.

cube_thread* cube_def_thrd
(cube_t* c, const char* name,
int rank, cube_process* proc);

Returns a new thread.

cube_cartesian* cube_def_cart
(cube_t* c, long ndims,
long int* dimv, int* periodv);

Defines a new Cartesian topology.

void cube_def_coords
(cube_t* c, cube_cartesian* cart,
cube_thread* thrd, long int* coord);

Maps a thread onto a Cartesian coordinate.

void cube_set_sev
(cube_t* c, cube_metric* met, cube_cnode* cnode,
cube_thread* thrd, double value);

Assigns the severity value to the point (met, cnode, thrd). Can only be used after metric, cnode and thread definitions are complete. Note that you can only use either the region or the cnode form of these calls, but not both at the same time.

double cube_get_sev
(cube_t* c, cube_metric* met, cube_cnode* cnode,
cube_thread* thrd);

Returns the severity of the point (met, cnode, thrd).

void cube_set_sev_reg
(cube_t* c, cube_metric* met, cube_region* reg,
cube_thread* thrd, double value);

Assigns the severity value to the point (met, reg, thrd). Can only be used after metric, regino and thread definitions are complete. Note that you can only use either the region or the cnode form of these calls, but not both at the same time.

void cube_add_sev
(cube_t* c, cube_metric* met, cube_cnode* cnode,
cube_thread* thrd, double value);

Adds the severity value to the present value at point (met, cnode, thrd). Can only be used after metric, cnode and thread definitions are complete. Note that you can only use either the region or the cnode form of these calls, but not both at the same time.

void cube_add_sev_reg
(cube_t* c, cube_metric* met, cube_region* reg,
cube_thread* thrd, double value);

Adds the severity value to the present value at point (met, reg, thrd). Can only be used after metric, region and thread definitions are complete. Note that you can only use either the region or the cnode form of these calls, but not both at the same time.

void cube_write_all
(cube_t* c, FILE* fp);

Writes the entire CUBE data to the given file. This basically corresponds to calling cube_write_def() and cube_write_sev_matrix().

void cube_write_def
(cube_t* c, FILE* fp);

Writes the definitions part of the CUBE data to the given file. Should only be used after definitions are complete.

void cube_write_sev_matrix
(cube_t* c, FILE* fp);

Writes the severity values part of the CUBE data to the given file. Should only be used after severity values are completely set. Unset values default to zero.

void cube_write_sev_row
(cube_t* c, FILE* fp,
cube_metric* met,
cube_cnode* cnode,
double* sevs);

Writes the given severity values of (met, cnode) for all threads to the given file. This can be used instead of cube_write_sev_matrix() to incrementally write parts of the severity matrix.

void cube_write_finish
(cube_t* c, FILE* fp);

Writes the end tags to a file. Must be called at the very end before closing the file, but only when incrementally writing the severity matrix using cube_write_sev_matrix(). When using cube_write_sev_matrix() to write the severity matrix in one chunk, calling this function is not needed.

Typical Usage

A simple C++ program is given to demonstrate how to use the CUBE write interface. Example below shows the corresponding CUBE display. The source code of the target application is provided below.

1 void foo() {
...
10 }
11 void bar() {
...
20 }
21 int main(int argc, char* argv) {
...
60 foo();
...
80 bar();
...
100 }

// A C++ example using CUBE write interface
#include <cube3/Cube.h>
#include <string>
#include <fstream>
using namespace std;
using namespace cube;
int main(int argc, char* argv[]) {
Cube cube;
// Specify mirrors (optional)
cube.def_mirror("http://icl.cs.utk.edu/software/kojak/");
cube.def_mirror("http://www.fz-juelich.de/jsc/kojak/");
// Specify information related to the file (optional)
cube.def_attr("experiment time", "September 27th, 2006");
cube.def_attr("description", "a simple example");
// Build metric tree
Metric* met0 = cube.def_met("Time", "Time", "FLOAT", "sec", "",
"@mirror@patterns-2.1.html#execution",
"root node", NULL); // using mirror
Metric* met1 = cube.def_met("User time", "User Time", "FLOAT", "sec", "",
"http://www.cs.utk.edu/usr.html",
"2nd level", met0); // without using mirror
Metric* met2 = cube.def_met("System time", "System Time", "FLOAT", "sec", "",
"http://www.cs.utk.edu/sys.html",
"2nd level", met0); // without using mirror
// Build call tree
string mod = "/ICL/CUBE/example.c";
Region* regn0 = cube.def_region("main", 21, 100, "", "1st level", mod);
Region* regn1 = cube.def_region("foo", 1, 10, "", "2nd level", mod);
Region* regn2 = cube.def_region("bar", 11, 20, "", "2nd level", mod);
Cnode* cnode0 = cube.def_cnode(regn0, mod, 21, NULL);
Cnode* cnode1 = cube.def_cnode(regn1, mod, 60, cnode0);
Cnode* cnode2 = cube.def_cnode(regn2, mod, 80, cnode0);
// Build system resource tree
Machine* mach = cube.def_mach("MSC", "");
Node* node = cube.def_node("Athena", mach);
Process* proc0 = cube.def_proc("Process 0", 0, node);
Process* proc1 = cube.def_proc("Process 1", 1, node);
Thread* thrd0 = cube.def_thrd("Thread 0", 0, proc0);
Thread* thrd1 = cube.def_thrd("Thread 1", 1, proc1);
// Build 2D Cartesian a topology (a 5x5 grid)
int ndims = 2;
vector<long> dimv;
vector<bool> periodv;
for (int i = 0; i < ndims; i++) {
dimv.push_back(5);
if (i % 2 == 0)
periodv.push_back(true);
else
periodv.push_back(false);
}
Cartesian* cart = cube.def_cart(ndims, dimv, periodv);
vector<long> coord0, coord1;
coord0.push_back(0);
coord0.push_back(0);
coord1.push_back(3);
coord1.push_back(3);
// map the two threads onto the above 2 coordinates
cube.def_coords(cart, thrd0, coord0);
cube.def_coords(cart, thrd1, coord1);
// Severity mapping
cube.set_sev(met0, cnode0, thrd0, 4);
cube.set_sev(met0, cnode0, thrd1, 4);
cube.set_sev(met0, cnode1, thrd0, 4);
cube.set_sev(met0, cnode1, thrd1, 4);
cube.set_sev(met0, cnode2, thrd0, 4);
cube.set_sev(met0, cnode2, thrd1, 4);
cube.set_sev(met1, cnode0, thrd0, 1);
cube.set_sev(met1, cnode0, thrd1, 1);
cube.set_sev(met1, cnode1, thrd0, 1);
cube.set_sev(met1, cnode1, thrd1, 1);
cube.set_sev(met1, cnode2, thrd0, 1);
cube.set_sev(met1, cnode2, thrd1, 1);
cube.set_sev(met2, cnode0, thrd0, 1);
cube.set_sev(met2, cnode0, thrd1, 1);
cube.set_sev(met2, cnode1, thrd0, 1);
cube.set_sev(met2, cnode1, thrd1, 1);
cube.set_sev(met2, cnode2, thrd0, 1);
cube.set_sev(met2, cnode2, thrd1, 1);
// Output to a cube file
ofstream out;
out.open("example.cube");
out << cube;
}


Scalasca