/**
* ****************************************************************************
* Copyright (c) 2010-2016 by Min Cai (min.cai.china@gmail.com).
* <p>
* This file is part of the Archimulator multicore architectural simulator.
* <p>
* Archimulator is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* <p>
* Archimulator is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* <p>
* You should have received a copy of the GNU General Public License
* along with Archimulator. If not, see <http://www.gnu.org/licenses/>.
* ****************************************************************************
*/
package archimulator.common;
import archimulator.common.report.ReportNode;
import archimulator.common.report.Reportable;
import archimulator.core.BasicProcessor;
import archimulator.core.Core;
import archimulator.core.Processor;
import archimulator.core.Thread;
import archimulator.core.speculativePrecomputation.DynamicSpeculativePrecomputationHelper;
import archimulator.isa.Memory;
import archimulator.os.Context;
import archimulator.os.Kernel;
import archimulator.uncore.MemoryHierarchy;
import archimulator.uncore.cache.Interval.IntervalHelper;
import archimulator.uncore.cache.interference.CacheInteractionHelper;
import archimulator.uncore.cache.replacement.reuseDistancePrediction.ReuseDistancePredictionHelper;
import archimulator.uncore.cache.stackDistanceProfile.StackDistanceProfilingHelper;
import archimulator.uncore.coherence.msi.controller.GeneralCacheController;
import archimulator.uncore.coherence.msi.flow.CacheCoherenceFlow;
import archimulator.uncore.delinquentLoad.DelinquentLoadIdentificationHelper;
import archimulator.uncore.helperThread.HelperThreadL2RequestProfilingHelper;
import archimulator.uncore.helperThread.hotspot.HotspotProfilingHelper;
import archimulator.uncore.mlp.BLPProfilingHelper;
import archimulator.uncore.mlp.MLPProfilingHelper;
import archimulator.uncore.noc.NoCMemoryHierarchy;
import archimulator.uncore.tlb.TranslationLookasideBuffer;
import archimulator.util.Reference;
import archimulator.util.collection.tree.NodeHelper;
import archimulator.util.dateTime.DateHelper;
import archimulator.util.event.BlockingEvent;
import archimulator.util.event.BlockingEventDispatcher;
import archimulator.util.event.CycleAccurateEventQueue;
import org.apache.commons.lang.time.DurationFormatUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* Simulation.
*
* @author Min Cai
*/
public abstract class Simulation implements SimulationObject<CPUExperiment, Simulation>, Reportable {
protected Reference<Kernel> kernelRef;
private long beginTime;
private long endTime;
private SimulationType type;
private Processor processor;
private CPUExperiment experiment;
private BlockingEventDispatcher<BlockingEvent> blockingEventDispatcher;
private CycleAccurateEventQueue cycleAccurateEventQueue;
//TODO: the following stuffs are to be refactored out!!!
private LatencyTrackingHelper latencyTrackingHelper;
private StackDistanceProfilingHelper stackDistanceProfilingHelper;
private ReuseDistancePredictionHelper reuseDistancePredictionHelper;
private HotspotProfilingHelper hotspotProfilingHelper;
private HelperThreadL2RequestProfilingHelper helperThreadL2RequestProfilingHelper;
private CacheInteractionHelper cacheInteractionHelper;
private DelinquentLoadIdentificationHelper delinquentLoadIdentificationHelper;
private DynamicSpeculativePrecomputationHelper dynamicSpeculativePrecomputationHelper;
private MLPProfilingHelper mlpProfilingHelper;
private BLPProfilingHelper blpProfilingHelper;
private IntervalHelper intervalHelper;
/**
* Current (max) dynamic instruction ID.
*/
public long currentDynamicInstructionId;
/**
* Current (max) reorder buffer entry ID.
*/
public long currentReorderBufferEntryId;
/**
* Current (max) decode buffer entry ID.
*/
public long currentDecodeBufferEntryId;
/**
* Current (max) memory hierarchy access ID.
*/
public long currentMemoryHierarchyAccessId;
/**
* Current (max) net message ID.
*/
public long currentNetMessageId;
/**
* Current (max) cache coherence flow ID.
*/
public long currentCacheCoherenceFlowId;
/**
* Pending cache coherence flows.
*/
public List<CacheCoherenceFlow> pendingFlows = new ArrayList<>();
/**
* Create a simulation.
*
* @param type the simulation type
* @param experiment the experiment object
* @param blockingEventDispatcher the blocking event dispatcher
* @param cycleAccurateEventQueue the cycle accurate event queue
* @param kernelRef the kernel reference
*/
public Simulation(SimulationType type, CPUExperiment experiment, BlockingEventDispatcher<BlockingEvent> blockingEventDispatcher, CycleAccurateEventQueue cycleAccurateEventQueue, Reference<Kernel> kernelRef) {
this.experiment = experiment;
this.blockingEventDispatcher = blockingEventDispatcher;
this.cycleAccurateEventQueue = cycleAccurateEventQueue;
this.type = type;
File cwdFile = new File(this.getExperiment().getConfig().getOutputDirectory());
if (!cwdFile.exists() && !cwdFile.mkdirs()) {
throw new RuntimeException();
}
this.kernelRef = kernelRef;
Kernel kernel = this.prepareKernel();
if (!this.blockingEventDispatcher.isEmpty()) {
throw new IllegalArgumentException();
}
this.processor = new BasicProcessor(this.experiment, this, this.blockingEventDispatcher, this.cycleAccurateEventQueue, kernel, this.prepareMemoryHierarchy());
this.latencyTrackingHelper = new LatencyTrackingHelper(this);
this.stackDistanceProfilingHelper = new StackDistanceProfilingHelper(this);
this.reuseDistancePredictionHelper = new ReuseDistancePredictionHelper(this);
this.hotspotProfilingHelper = new HotspotProfilingHelper(this);
this.helperThreadL2RequestProfilingHelper = new HelperThreadL2RequestProfilingHelper(this);
this.cacheInteractionHelper = new CacheInteractionHelper(this);
this.delinquentLoadIdentificationHelper = new DelinquentLoadIdentificationHelper(this);
if (getExperiment().getConfig().isDynamicSpeculativePrecomputationEnabled()) {
this.dynamicSpeculativePrecomputationHelper = new DynamicSpeculativePrecomputationHelper(this);
}
this.mlpProfilingHelper = new MLPProfilingHelper(this);
this.blpProfilingHelper = new BLPProfilingHelper(this);
this.intervalHelper = new IntervalHelper(this);
}
/**
* Perform the simulation.
*/
public void simulate() {
Logger.info(Logger.SIMULATOR, "", this.cycleAccurateEventQueue.getCurrentCycle());
this.beginTime = DateHelper.toTick(new Date());
this.beginSimulation();
try {
if (this.getType() == SimulationType.FAST_FORWARD) {
this.doFastForward();
} else if (this.getType() == SimulationType.WARMUP) {
this.doWarmup();
} else if (this.getType() == SimulationType.MEASUREMENT) {
this.doMeasurement();
}
} catch (Exception e) {
e.printStackTrace();
this.endTime = DateHelper.toTick(new Date());
this.collectStats();
this.endSimulation();
Logger.info(Logger.SIMULATION, "Simulation aborted with errors.", this.getCycleAccurateEventQueue().getCurrentCycle());
throw new RuntimeException(e);
}
this.endTime = DateHelper.toTick(new Date());
this.collectStats();
Logger.info(Logger.SIMULATION, "Simulation completed successfully.", this.getCycleAccurateEventQueue().getCurrentCycle());
this.endSimulation();
}
/**
* Collect the statistics.
*/
private void collectStats() {
final List<ExperimentStat> stats = new ArrayList<>();
ReportNode rootReportNode = new ReportNode(null, "");
this.dumpStats(rootReportNode);
this.processor.dumpStats(rootReportNode);
for (Memory memory : this.getProcessor().getKernel().getMemories()) {
memory.dumpStats(rootReportNode);
}
for (Core core : this.getProcessor().getCores()) {
core.dumpStats(rootReportNode);
}
for (Thread thread : this.getProcessor().getThreads()) {
thread.dumpStats(rootReportNode);
}
for (TranslationLookasideBuffer tlb : this.getProcessor().getMemoryHierarchy().getTlbs()) {
tlb.dumpStats(rootReportNode);
}
for (GeneralCacheController cacheController : this.getProcessor().getMemoryHierarchy().getCacheControllers()) {
cacheController.dumpStats(rootReportNode);
}
this.getProcessor().getMemoryHierarchy().getMemoryController().dumpStats(rootReportNode);
((NoCMemoryHierarchy)this.getProcessor().getMemoryHierarchy()).dumpStats(rootReportNode);
this.getLatencyTrackingHelper().dumpStats(rootReportNode);
this.getStackDistanceProfilingHelper().dumpStats(rootReportNode);
this.getReuseDistancePredictionHelper().dumpStats(rootReportNode);
this.getHotspotProfilingHelper().dumpStats(rootReportNode);
this.getHelperThreadL2RequestProfilingHelper().dumpStats(rootReportNode);
this.getCacheInteractionHelper().dumpStats(rootReportNode);
this.getDelinquentLoadIdentificationHelper().dumpStats(rootReportNode);
this.getMlpProfilingHelper().dumpStats(rootReportNode);
this.getIntervalHelper().dumpStats(rootReportNode);
this.getProcessor().getMemoryHierarchy().getL2Controller().getCache().getReplacementPolicy().dumpStats(rootReportNode);
rootReportNode.traverse(node -> stats.add(new ExperimentStat(getPrefix(), node.getPath(), node.getValue())));
if (this.getType() == SimulationType.MEASUREMENT || this.getType() == SimulationType.WARMUP) {
getProcessor().getMemoryHierarchy().dumpCacheControllerFsmStats(stats);
}
this.getExperiment().getStats().addAll(stats);
}
/**
* Get a value indicating whether it can do fast forwarding one cycle or not.
*
* @return a value indicating whether it can fast forwarding one cycle or not
*/
protected abstract boolean canDoFastForwardOneCycle();
/**
* Get a value indicating whether it can do warmup one cycle or not.
*
* @return a value indicating whether it can do warmup one cycle or not
*/
protected abstract boolean canDoWarmupOneCycle();
/**
* Get a value indicating whether it can do measurement one cycle or not.
*
* @return a value indicating whether it can do measurement one cycle or not
*/
protected abstract boolean canDoMeasurementOneCycle();
/**
* Begin the simulation.
*/
protected abstract void beginSimulation();
/**
* End the simulation.
*/
protected abstract void endSimulation();
/**
* Do fast forwarding.
*/
public void doFastForward() {
Logger.info(Logger.SIMULATION, "Switched to fast forward mode.", this.getCycleAccurateEventQueue().getCurrentCycle());
while (!this.getProcessor().getKernel().getContexts().isEmpty() && this.canDoFastForwardOneCycle()) {
this.getProcessor().getCores().forEach(Core::doFastForwardOneCycle);
this.advanceOneCycle();
}
}
/**
* Do cache warmup.
*/
public void doWarmup() {
Logger.info(Logger.SIMULATION, "Switched to warmup mode.", this.getCycleAccurateEventQueue().getCurrentCycle());
while (!this.getProcessor().getKernel().getContexts().isEmpty() && this.canDoWarmupOneCycle()) {
this.getProcessor().getCores().forEach(Core::doWarmupOneCycle);
this.advanceOneCycle();
}
}
/**
* Do measurement.
*/
public void doMeasurement() {
Logger.info(Logger.SIMULATION, "Switched to measurement mode.", this.getCycleAccurateEventQueue().getCurrentCycle());
while (!this.getProcessor().getKernel().getContexts().isEmpty() && this.canDoMeasurementOneCycle()) {
this.getProcessor().getCores().forEach(Core::doMeasurementOneCycle);
this.advanceOneCycle();
}
}
/**
* Advance one cycle.
*/
private void advanceOneCycle() {
this.doHouseKeeping();
this.getCycleAccurateEventQueue().advanceOneCycle();
}
/**
* Do housekeeping work.
*/
public void doHouseKeeping() {
this.getProcessor().getKernel().advanceOneCycle();
this.getProcessor().updateContextToThreadAssignments();
}
/**
* Prepare the kernel.
*
* @return the kernel that is prepared
*/
public Kernel prepareKernel() {
Kernel kernel = new Kernel(this);
for (final ContextMapping contextMapping : this.getExperiment().getConfig().getContextMappings()) {
final Context context = Context.load(kernel, contextMapping);
if (!kernel.map(context, candidateThreadId -> candidateThreadId == contextMapping.getThreadId())) {
throw new RuntimeException();
}
kernel.getContexts().add(context);
}
return kernel;
}
/**
* Prepare the memory hierarchy.
*
* @return the memory hierarchy that is prepared
*/
public MemoryHierarchy prepareMemoryHierarchy() {
return new NoCMemoryHierarchy(this.getExperiment(), this, this.getBlockingEventDispatcher(), this.getCycleAccurateEventQueue());
}
/**
* Dump the tree of the pending cache coherence flows.
*/
public void dumpPendingFlowTree() {
for (CacheCoherenceFlow pendingFlow : this.pendingFlows) {
NodeHelper.print(pendingFlow);
System.out.println();
}
System.out.println();
}
@Override
public void dumpStats(ReportNode reportNode) {
reportNode.getChildren().add(new ReportNode(reportNode, "simulation") {{
getChildren().add(new ReportNode(this, "beginTimeAsString", getBeginTimeAsString()));
getChildren().add(new ReportNode(this, "endTimeAsString", getEndTimeAsString()));
getChildren().add(new ReportNode(this, "duration", getDuration()));
getChildren().add(new ReportNode(this, "durationInSeconds", getDurationInSeconds() + ""));
getChildren().add(new ReportNode(this, "cycleAccurateEventQueue/currentCycle", getCycleAccurateEventQueue().getCurrentCycle() + ""));
}});
}
/**
* Get the simulation type.
*
* @return the simulation type
*/
public SimulationType getType() {
return type;
}
/**
* Get the time in ticks when the simulation begins.
*
* @return the time in ticks when the simulation begins
*/
public long getBeginTime() {
return beginTime;
}
/**
* Get the time in ticks when the simulation ends.
*
* @return the time in ticks when the simulation ends
*/
public long getEndTime() {
return endTime;
}
/**
* Get the string representation of the time when the simulation begins.
*
* @return the string representation of the time when the simulation begins
*/
public String getBeginTimeAsString() {
return DateHelper.toString(beginTime);
}
/**
* Get the string representation of the time when the simulation ends.
*
* @return the string representation of the time when the simulation ends
*/
public String getEndTimeAsString() {
return DateHelper.toString(endTime);
}
/**
* Get the duration in seconds that the simulation lasts.
*
* @return the duration in seconds that the simulation lasts
*/
public long getDurationInSeconds() {
return (this.getEndTime() - this.getBeginTime()) / 1000;
}
/**
* Get the string representation of the duration that the simulation lasts.
*
* @return the string representation of the duration that the simulation lasts
*/
public String getDuration() {
return DurationFormatUtils.formatDurationHMS(this.getEndTime() - this.getBeginTime());
}
/**
* Get the processor object.
*
* @return the processor object
*/
public Processor getProcessor() {
return this.processor;
}
/**
* Get the experiment object.
*
* @return the experiment object
*/
public CPUExperiment getExperiment() {
return experiment;
}
@Override
public Simulation getSimulation() {
return this;
}
/**
* Get the cycle accurate event queue.
*
* @return the cycle accurate event queue
*/
public CycleAccurateEventQueue getCycleAccurateEventQueue() {
return this.cycleAccurateEventQueue;
}
/**
* Get the blocking event dispatcher.
*
* @return the blocking event dispatcher
*/
public BlockingEventDispatcher<BlockingEvent> getBlockingEventDispatcher() {
return this.blockingEventDispatcher;
}
/**
* Get the latency tracking helper.
*
* @return the latency tracking helper
*/
public LatencyTrackingHelper getLatencyTrackingHelper() {
return latencyTrackingHelper;
}
/**
* Get the stack distance profiling helper.
*
* @return the stack distance profiling helper
*/
public StackDistanceProfilingHelper getStackDistanceProfilingHelper() {
return stackDistanceProfilingHelper;
}
/**
* Get the reuse distance prediction helper.
*
* @return the reuse distance prediction helper
*/
public ReuseDistancePredictionHelper getReuseDistancePredictionHelper() {
return reuseDistancePredictionHelper;
}
/**
* Get the hotspot profiling helper.
*
* @return the hotspot profiling helper
*/
public HotspotProfilingHelper getHotspotProfilingHelper() {
return hotspotProfilingHelper;
}
/**
* Get the helper thread L2 cache request profiling helper.
*
* @return the helper thread L2 cache request profiling helper
*/
public HelperThreadL2RequestProfilingHelper getHelperThreadL2RequestProfilingHelper() {
return helperThreadL2RequestProfilingHelper;
}
/**
* Get the cache interaction helper.
*
* @return the cache interaction helper
*/
public CacheInteractionHelper getCacheInteractionHelper() {
return cacheInteractionHelper;
}
/**
* Get the delinquent load identification helper.
*
* @return the delinquent load identification helper
*/
public DelinquentLoadIdentificationHelper getDelinquentLoadIdentificationHelper() {
return delinquentLoadIdentificationHelper;
}
/**
* Get the dynamic speculative precomputation helper.
*
* @return the dynamic speculative precomputation helper
*/
public DynamicSpeculativePrecomputationHelper getDynamicSpeculativePrecomputationHelper() {
return dynamicSpeculativePrecomputationHelper;
}
/**
* Get the MLP profiling helper.
*
* @return the MLP profiling helper
*/
public MLPProfilingHelper getMlpProfilingHelper() {
return mlpProfilingHelper;
}
/**
* Get the BLP profiling helper.
*
* @return the BLP profiling helper
*/
public BLPProfilingHelper getBlpProfilingHelper() {
return blpProfilingHelper;
}
/**
* Get the interval helper.
*
* @return the interval helper
*/
public IntervalHelper getIntervalHelper() {
return intervalHelper;
}
@Override
public String getName() {
return "simulation";
}
/**
* Get the simulation title prefix.
*
* @return the simulation title prefix
*/
public abstract String getPrefix();
}