/**
* ****************************************************************************
* 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.uncore.mlp;
import archimulator.common.Simulation;
import archimulator.common.SimulationEvent;
import archimulator.common.SimulationType;
import archimulator.common.report.ReportNode;
import archimulator.common.report.Reportable;
import archimulator.uncore.MemoryHierarchyAccess;
import archimulator.uncore.coherence.event.GeneralCacheControllerServiceNonblockingRequestEvent;
import archimulator.uncore.coherence.event.LastLevelCacheControllerLineInsertEvent;
import archimulator.uncore.coherence.msi.controller.DirectoryController;
import archimulator.util.math.Quantizer;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
/**
* Memory level parallelism (MLP) profiling helper.
*
* @author Min Cai
*/
public class MLPProfilingHelper implements Reportable {
/**
* L2 miss MLP profiled event.
*/
public class L2MissMLPProfiledEvent extends SimulationEvent {
private int set;
private int way;
private PendingL2Miss pendingL2Miss;
/**
* Create an L2 miss MLP profiled event.
*
* @param set the set
* @param way the way
* @param pendingL2Miss the pending L2 miss
*/
public L2MissMLPProfiledEvent(int set, int way, PendingL2Miss pendingL2Miss) {
super(l2Controller);
this.set = set;
this.way = way;
this.pendingL2Miss = pendingL2Miss;
}
/**
* Get the L2 cache controller.
*
* @return the L2 cache controller
*/
public DirectoryController getL2Controller() {
return l2Controller;
}
/**
* Get the set.
*
* @return the set
*/
public int getSet() {
return set;
}
/**
* Get the way.
*
* @return the way
*/
public int getWay() {
return way;
}
/**
* Get the pending L2 miss.
*
* @return the pending L2 miss
*/
public PendingL2Miss getPendingL2Miss() {
return pendingL2Miss;
}
}
private DirectoryController l2Controller;
private Quantizer mlpCostQuantizer;
private Map<Integer, PendingL2Miss> pendingL2Misses;
private Map<Integer, Long> numL2MissesPerMlpCostQuantum;
private Map<Integer, Long> numCyclesPerMlp;
/**
* Create an MLP profiling helper.
*
* @param simulation the simulation
*/
public MLPProfilingHelper(Simulation simulation) {
this.l2Controller = simulation.getProcessor().getMemoryHierarchy().getL2Controller();
this.mlpCostQuantizer = new Quantizer(7, 40);
this.pendingL2Misses = new LinkedHashMap<>();
this.numL2MissesPerMlpCostQuantum = new TreeMap<>();
this.numCyclesPerMlp = new TreeMap<>();
this.l2Controller.getBlockingEventDispatcher().addListener(GeneralCacheControllerServiceNonblockingRequestEvent.class, event -> {
if (event.getCacheController() == MLPProfilingHelper.this.l2Controller && !event.isHitInCache()) {
profileBeginServicingL2Miss(event.getAccess());
}
});
this.l2Controller.getBlockingEventDispatcher().addListener(LastLevelCacheControllerLineInsertEvent.class, event -> {
if (event.getCacheController() == MLPProfilingHelper.this.l2Controller) {
profileEndServicingL2Miss(event.getSet(), event.getWay(), event.getAccess());
}
});
this.l2Controller.getCycleAccurateEventQueue().getPerCycleEvents().add(() -> {
if (simulation.getType() != SimulationType.FAST_FORWARD) {
updateL2MlpCostsPerCycle();
}
});
this.l2Controller.getBlockingEventDispatcher().addListener(MLPProfilingHelper.L2MissMLPProfiledEvent.class, event -> {
double mlpCost = event.getPendingL2Miss().getMlpCost();
int quantizedMlpCost = getMlpCostQuantizer().quantize((int) mlpCost);
if (!numL2MissesPerMlpCostQuantum.containsKey(quantizedMlpCost)) {
numL2MissesPerMlpCostQuantum.put(quantizedMlpCost, 0L);
}
numL2MissesPerMlpCostQuantum.put(quantizedMlpCost, numL2MissesPerMlpCostQuantum.get(quantizedMlpCost) + 1);
});
}
/**
* To be invoked per cycle for updating MLP-costs for in-flight L2 cache accesses.
*/
private void updateL2MlpCostsPerCycle() {
int mlp = this.pendingL2Misses.size();
if (!this.pendingL2Misses.isEmpty()) {
for (PendingL2Miss pendingL2Miss : this.pendingL2Misses.values()) {
pendingL2Miss.setMlpCost(pendingL2Miss.getMlpCost() + (double) 1 / mlp);
}
if (!this.numCyclesPerMlp.containsKey(mlp)) {
this.numCyclesPerMlp.put(mlp, 0L);
}
this.numCyclesPerMlp.put(mlp, this.numCyclesPerMlp.get(mlp) + 1);
}
}
/**
* Profile the beginning of servicing an L2 cache request.
*
* @param access the memory hierarchy access
*/
private void profileBeginServicingL2Miss(MemoryHierarchyAccess access) {
int tag = access.getPhysicalTag();
PendingL2Miss pendingL2Miss = new PendingL2Miss(access, this.l2Controller.getCycleAccurateEventQueue().getCurrentCycle());
this.pendingL2Misses.put(tag, pendingL2Miss);
}
/**
* Profile the end of servicing an L2 cache request.
*
* @param set the set
* @param way the way
* @param access the memory hierarchy access
*/
private void profileEndServicingL2Miss(int set, int way, MemoryHierarchyAccess access) {
int tag = access.getPhysicalTag();
PendingL2Miss pendingL2Miss = this.pendingL2Misses.get(tag);
pendingL2Miss.setEndCycle(this.l2Controller.getCycleAccurateEventQueue().getCurrentCycle());
this.pendingL2Misses.remove(tag);
this.l2Controller.getBlockingEventDispatcher().dispatch(new L2MissMLPProfiledEvent(set, way, pendingL2Miss));
}
@Override
public void dumpStats(final ReportNode reportNode) {
reportNode.getChildren().add(new ReportNode(reportNode, "mlpCostQuantizer") {{
getChildren().add(
new ReportNode(
this,
"maxValue",
String.format("%d", mlpCostQuantizer.getMaxValue())
)
);
getChildren().add(
new ReportNode(
this,
"quantum",
String.format("%d", mlpCostQuantizer.getQuantum())
)
);
}});
for (int i = 0; i < mlpCostQuantizer.getMaxValue(); i++) {
reportNode.getChildren().add(
new ReportNode(
reportNode,
String.format("numL2MissesPerMlpCostQuantum/%d", i),
String.format(
"%d",
numL2MissesPerMlpCostQuantum.containsKey(i) ? numL2MissesPerMlpCostQuantum.get(i) : 0
)
)
);
}
for (int i : numCyclesPerMlp.keySet()) {
reportNode.getChildren().add(
new ReportNode(
reportNode,
String.format("numCyclesPerMlp/%d", i),
String.format("%d", numCyclesPerMlp.get(i))
)
);
}
}
/**
* Get the MLP-cost quantizer.
*
* @return the MLP-cost quantizer
*/
public Quantizer getMlpCostQuantizer() {
return mlpCostQuantizer;
}
}