The Sparta Modeling Framework
|
Sparta contains a Discrete Event Simulation (DES) engine. This means that a modeler is allowed to schedule events, or work, only when there's work to do. This naturally allows for faster simulation times and "fast forwarding" of simulation, skipping time when there's nothing for the scheduler to do.
Unfortunately, in most other DES-based simulation engines, a level of indeterminism is introduced due to random scheduling of events into the simulation kernel, especially if a developer introduces a timing or functional change.
As a pure C++ development platform, Sparta does not suffer from indeterminism between one simulation run and another. All events in a Sparta-based model are registered during construction time. Sparta will then map out those events with dependencies (either explicit or implicit) in a Directed Acyclic Graph (DAG). Those dependencies are established using precedence rules (see Precedence operators for EventNode/Scheduleables).
Those events that do not have any explicit dependencies are "free form," but deterministic as long as the modeler does not introduce random behaviors during a running simulation: i.e. each run of the same workload will be exactly the same with the same parameters.
However, those "free form" events have the potential to be scheduled in a different order between different workloads or with different simulation parameters. This is mostly harmless, but can cause headache especially if the modeler assumes one event will be fired before another. To avoid that, the suggestion to the modeler is to provide a clean, clear design of their model and to draw ordering lines between items of work and establish precedence between Events. This will ensure determinism in the DES.
Debugging a DES based model has challenges. Some known problems to solve:
First, if you are using sparta::app::CommandLineSimulator class, you can always dump the internal DAG that defines the given relationships between sparta::Scheduleable objects:
Take an example from the DAG output and analyze it:
This ordering is an implicit ordering between functions within the Decode block, specifically the receiving of uop credits from Rename (not listed) on a sparta::DataInPort (named receiveUopQueueCredits
) and decoding of instructions (named decodeInsts_
). (See Core Example Using Sparta).
The V
symbol represents a vertex in the DAG, which is one sparta::Scheduleable. This sparta::Scheduleable is a sparta::PayloadEvent inside the sparta::DataInPort named in_uop_queue_credits<DataInPort>[Decode::receiveUopQueueCredits_(uint32_t)]
. Specifically, this internal sparta::PayloadEvent that will call the method Decode::receiveUopQueueCredits_(uint32_t)
when data is to be delivered from the port.
This vertex has 2 edges in and 2 edges out. Those edges going out are listed, one being a GOP or sparta::GlobalOrderingPoint and the other is a registered triggered event that is designated to be scheduled after receiving of Uop Queue credits.
If the modeler introduces DAG cycle, the framework with throw and exception and generate dag_cycle.dot
file. Using Graphviz tools (or the like), the modeler can visualize the DAG cycle graphically. Some common ways to solve cycles:
To determine which event is scheduling another event, the sparta::Scheduler has an internal debugging logger (called debug
) to help. To invoke the logger, use the the
command line option if the model uses sparta::app::CommandLineSimulator class:
The above example will dump the sparta::Scheduler debug logger to stdout which is fed through a pipe to the less
command with escape sequences displayed (-R
). Using less
, search for the named event of interest. For example, taking the output from the sparta_core_example, search for dispatchInstructions_
to see which event caused it to be triggered:
From the lines above, the modeler can see the event dispatchInstructions_
was scheduled when the event retireEvent_
was fired.
Remember that logging and the command line flag
can be used together to only dump a log starting at the given cycle.