Statistics Capability
The purpose of simulators is not only to compute timing information, but also to gather information about the behavior of the different hardware mechanism during the simulation. The Statistic Capability allow a standardize access to event counters within the different modules.
Description
Many information in the module can be characterized by a single counter: The number of executed instruction in the CPU, the number of read hit in the cache, and so on... The Statistic Capability proposes a standardized interface for such counters.
Interface
The Statistic Interface is very simple, just providing a method to access a counter based on its name, as shown in the figure below:
A counter should consist of a unsigned 64 bit integer (uint64_t). The interface allows to get a counter from its name provided as a string. If a counter with the provided name does not exists in the module, an exception is raised.
Note that accessing the counter through its name as a string is much less efficient than directly accessing the integer counter. However the module internally will not use this interface, but directly use the integer counters (the cache module for instance will directly increment the read_miss counter every time there is a read miss.
External components (like GUI for instance) will access those counters through the interface. As this kind of access does not occur very often, the penalty for acceding a counter through its name remains minimal.
The interface for the Statistic Capability is defined in the StatisticInterface presented below:
class StatisticInterface {public: virtual uint64_t get_statistic(const string &name)=0; };
The next section describes how to use the statistic capability within your own modules.
How To use the Statistic Capability in your module
The Statistic Capability is meant to be used with modules which already use counters to count down some events during the simulation. As an example, let’s focus on the cache module. This module has the following counters defined as properties of the cache class:
class Cache : public module
, ...
{ ...
uint64_t accesses; // Total number of accesses
uint64_t accesses_read; // Total number of read accesses
uint64_t accesses_write; // Total number of write accesses
uint64_t hits; // Total number of hits
uint64_t hits_read; // Total number of read hits
uint64_t hits_write; // Total number of write hits
uint64_t misses; // Total number of misses
uint64_t misses_read; // Total number of read misses
uint64_t misses_write; // Total number of write misses
...
}
To make the Cache module statistic compliant, the first step is to add a new include, so that the Statistic Interface is defined:
#include "statistics/StatisticService.h"
The second step to make the cache module Statistic compliant, is to make the module inherits the Statistic Service, modifying the class declaration and the class constructor as shown below:
class Cache : public module
, public StatisticService
, ...
{ ...
Cache(char *name) : module(name)
, StatisticService(name, this)
, ...
{ ...
}
...
}
Inheriting the Statistic Service automatically creates the following service port in the module:
ServiceExport < StatisticInterface > statistic_export;
You will be able to use the service through this port.
The third step consist of registering each statistic to make it available through the statistic service. This registration maps a string corresponding to the statistic to the statistic itself.
Registering a statistic is done by using the add method of the statistics repository as shown below:
statistics.add("stat",stat);
The code above associate the name “stat” to a uint64_t statistic named stat.
To register the statistic of our cache module, we simply need to register all the statistic in the constructor of the class:
class Cache : public module
, public StatisticService
, ...
{ ...
Cache(char *name) : module(name)
, StatisticService(name, this)
, ...
{ ...
// --- Registering statistics ----------------
statistics.add("accesses",accesses);
statistics.add("accesses_read",accesses_read);
statistics.add("accesses_write",accesses_write);
statistics.add("hits",hits);
statistics.add("hits_read",hits_read);
statistics.add("hits_write",hits_write);
statistics.add("misses",misses);
statistics.add("misses_read",misses_read);
statistics.add("misses_write",misses_write);
// -------------------------------------------
}
...
}
The last step consist of accessing the statistics for your particular need. Let’s say for instance you want to display the cache miss rate at the end of the simulation. You will add the following code after the main simulation loop in your simulator:
cerr << "L1 miss rate: " << cache_l1.get_statistic("accesses_read") / cache_l1.get_statistic("misses_read") << endl;
Note that you can also display the miss rate as a timeline during the simlation by using the statistics in a dedicated thread displaying the timeline.
Reference Manual
Some modules using the Statistic Capability
The following module from the repository are using the Statistic Capability:
