/* This file is part of JOP, the Java Optimized Processor see <http://www.jopdesign.com/> Copyright (C) 2011, Benedikt Huber (benedikt@vmars.tuwien.ac.at) This program 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. This program 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. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.jopdesign.wcet.analysis.cache; import java.util.ArrayList; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import lpsolve.LpSolveException; import com.jopdesign.common.MethodInfo; import com.jopdesign.common.code.Segment; import com.jopdesign.common.code.SuperGraph.SuperGraphEdge; import com.jopdesign.common.code.SuperGraph.SuperGraphNode; import com.jopdesign.common.misc.Iterators; import com.jopdesign.common.misc.MiscUtils; import com.jopdesign.common.misc.MiscUtils.F1; import com.jopdesign.wcet.WCETTool; import com.jopdesign.wcet.analysis.GlobalAnalysis; import com.jopdesign.wcet.analysis.InvalidFlowFactException; import com.jopdesign.wcet.ipet.IPETConfig; import com.jopdesign.wcet.ipet.IPETConfig.CacheCostCalculationMethod; import com.jopdesign.wcet.ipet.IPETSolver; import com.jopdesign.wcet.ipet.IPETUtils; /** * Purpose: This is the common interface for cache persistence analyses, * either using persistence region constraints (ALL_FIT_REGION) or * persistence region costs (ALL_FIT_SIMPLE). * * <p> TODO: Currently, this classes is * only used as a utility class, until the design for the object and method * cache analyses are fletched out.</p> * * @param <T> type of the cache tags * * <h1>Persistence Analysis</h1> * Analyze the number of distinct cache tags accessed in each scope * <h2>Technique</h2> * <p>Traverse the analyzed segment, and calculate the maximum number of distinct cache tags * mapping to one set accessed when executing the segment. Approximations for this problem * can be found (with decreasing precision) by solving an ILP problem, the relaxed LP problem, * or by simply counting the number of distinct methods accessed. * </p> * <p>If the maximum number of distinct tags accessed is less than or equal to the * associativity of the set, all cache blocks for the set are persistent within the * segment. That is, they are missed at most once (LRU: first miss, FIFO: one miss) * whenever the segment is executed.</p> * <p>In this case, we (a) add the maximum cache cost to all entry edges of the segment * (ALL_FIT_SIMPLE) (b) introduce one miss-cost variable for each edge accessing a certain * tag, and add a constraint that at most one of these variables is not zero (ALL_FIT_REGION) * <h2>Explanation</h2> * <p>Short Proof: Assume that at most {@code N} blocks used, and those {@code N} blocks * correspond to cache tags {@code T_1} through {@code T_n}. Then there is a path, s.t. * for each tag {@code T_k} the frequency of one edge accessing the tag * is greater than zero. Conversely, if for all edges accessing the tag the * frequency is 0, the corresponding data is never loaded into the cache. */ public abstract class CachePersistenceAnalysis extends CacheAnalysis { public static enum PersistenceCheck { /** check persistence by counting the total number of distinct methods (cheap) */ CountTotal, /** check persistence by maximizing the number of distinct blocks accessed (LP relaxation) */ CountRelaxed, /** check persistence by maximizing the number of distinct blocks accessed (LP relaxation) */ CountILP }; @Override public Set<SuperGraphEdge> addCacheCost(Segment segment, IPETSolver<SuperGraphEdge> ipetSolver, CacheCostCalculationMethod cacheCostCalc) throws InvalidFlowFactException, LpSolveException { Set<SuperGraphEdge> missEdges; switch(cacheCostCalc) { case ALL_FIT_REGIONS: missEdges = addMissOnceConstraints(segment, ipetSolver); break; case ALL_FIT_COST: missEdges = addMissOnceCost(segment, ipetSolver, EnumSet.allOf(PersistenceCheck.class)); break; case ALL_FIT_SIMPLE: missEdges = addMissOnceCost(segment, ipetSolver, EnumSet.of(PersistenceCheck.CountTotal)); break; case ALWAYS_MISS: missEdges = addMissAlwaysCost(segment, ipetSolver); break; case GLOBAL_ALL_FIT: missEdges = addGlobalAllFitConstraints(segment, ipetSolver); break; case ALWAYS_HIT: missEdges = new HashSet<SuperGraphEdge>(); break; /* no additional costs */ default: throw new RuntimeException("addCacheCost(): Unexpected cache cost calculation mode "+cacheCostCalc); } return missEdges; } protected abstract Set<SuperGraphEdge> addMissAlwaysCost(Segment segment, IPETSolver<SuperGraphEdge> ipetSolver); protected abstract Set<SuperGraphEdge> addMissOnceCost(Segment segment, IPETSolver<SuperGraphEdge> ipetSolver, EnumSet<PersistenceCheck> enumSet) throws InvalidFlowFactException, LpSolveException; protected abstract Set<SuperGraphEdge> addMissOnceConstraints(Segment segment,IPETSolver<SuperGraphEdge> ipetSolver) throws InvalidFlowFactException, LpSolveException; protected abstract Set<SuperGraphEdge> addGlobalAllFitConstraints(Segment segment, IPETSolver<SuperGraphEdge> ipetSolver); /* strategies */ /* ---------- */ /* utilities */ /* --------- */ /** * Add extra costs for all edges in segment. We use extra cost edges, * so that it easier to reconstruct the cache cost. For performance reasons, * we might consider just adding costs for the super graph edges themselves. * * @param edges * @param ipetSolver * @param costModel * @param key * @param tag * @return */ protected Set<SuperGraphEdge> addFixedCostEdges(Iterable<SuperGraphEdge> edges, IPETSolver<SuperGraphEdge> ipetSolver, F1<SuperGraphEdge, Long> costModel, Object key, Object tag) { Set<SuperGraphEdge> missEdges = new HashSet<SuperGraphEdge>(); for(SuperGraphEdge accessEdge: edges) { long cost = costModel.apply(accessEdge); if(cost != 0) { missEdges.add(fixedAdditionalCostEdge(accessEdge, key, tag, cost, ipetSolver)); } } return missEdges; } /** * Generate extra cost edge with fixed additional cost * @param accessEdge the corresponding flow edge (key-1) * @param key the access category (key-2) * @param tag the accessed tag (key-3) * @param cost the extra cost when executing the edge * @param ipetSolver the ipet solver to operate on * @return */ protected SuperGraphEdge fixedAdditionalCostEdge(SuperGraphEdge accessEdge, Object key, Object tag, long cost, IPETSolver<SuperGraphEdge> ipetSolver) { SuperGraphEdge missEdge = SuperGraphExtraCostEdge.generateExtraCostEdge(accessEdge, key, tag); ipetSolver.addConstraint(IPETUtils.relativeBound(Iterators.singleton(missEdge), Iterators.singleton(accessEdge),1)); ipetSolver.addEdgeCost(missEdge, cost); return missEdge; } /** * Add constraints for a persistence segment * @param <T> cache tag type * @param <C> access edge collection type * @param persistenceSegment * @param partition * @param ipetSolver * @param costModel * @param analysisKey * @return */ protected <T,C extends Iterable<SuperGraphEdge>> Set<SuperGraphEdge> addPersistenceSegmentConstraints( Segment persistenceSegment, Iterable<Entry<T, C>> partition, IPETSolver<SuperGraphEdge> ipetSolver, F1<SuperGraphEdge,Long> costModel, Object analysisKey) { HashSet<SuperGraphEdge> missEdges = new HashSet<SuperGraphEdge>(); for(Entry<T, C> accessed : partition) { List<SuperGraphEdge> missOnceEdges = new ArrayList<SuperGraphEdge>(); for(SuperGraphEdge accessEdge : accessed.getValue()) { long cost = costModel.apply(accessEdge); SuperGraphEdge missEdge = SuperGraphExtraCostEdge.generateExtraCostEdge(accessEdge, analysisKey, accessed.getKey()); ipetSolver.addConstraint(IPETUtils.relativeBound(Iterators.singleton(missEdge), Iterators.singleton(accessEdge), 1)); ipetSolver.addEdgeCost(missEdge, cost); missOnceEdges.add(missEdge); } ipetSolver.addConstraint(IPETUtils.relativeBound(missOnceEdges,persistenceSegment.getEntryEdges(),1)); missEdges.addAll(missOnceEdges); } return missEdges; } /** * Analyze the cost for loading each distinct tag in the given segment at most once. * This can also be used to count the number of distinct tags (given each tag the cost of 1), * or the number of cache blocks for the variable block method cache (given each tag a cost * equal to the number of cache blocks it needs) * @param segment the segment to analyze * @param accessEdges cache access edges partitioned by tag * @param costModel * @param useILP use integer variables (more expensive, more accurate) * @param analysisKey * @param wcetTool * @return * @throws InvalidFlowFactException * @throws LpSolveException */ protected <T> long computeMissOnceCost( Segment segment, Iterable<Entry<T, List<SuperGraphEdge>>> partition, F1<SuperGraphEdge,Long> costModel, boolean useILP, String analysisKey, WCETTool wcetTool) throws InvalidFlowFactException, LpSolveException { IPETConfig ipetConfig = new IPETConfig(wcetTool.getConfig()); String problemKey = GlobalAnalysis.formatProblemName(analysisKey, segment.getEntryCFGs().toString()); /* create an global IPET problem for the supergraph */ IPETSolver<SuperGraphEdge> ipetSolver = GlobalAnalysis.buildIpetProblem(wcetTool, problemKey, segment, ipetConfig); /* add persistence constraints */ addPersistenceSegmentConstraints(segment, partition, ipetSolver, costModel, analysisKey); /* Solve */ double lpCost = ipetSolver.solve(null, useILP); long maxCacheCost = (long) (lpCost + 0.5); return maxCacheCost; } /* convenience utilities */ /** * Convert costs per node, to costs per incoming edge * @param nodeCostMap costs per node * @return costs per incoming edge */ public Map<SuperGraphEdge, Long> nodeToEdgeCost(Segment segment, Map<SuperGraphNode, Long> nodeCostMap) { Map<SuperGraphEdge, Long> edgeCostMap = new HashMap<SuperGraphEdge, Long>(); for(Entry<SuperGraphNode, Long> entry : nodeCostMap.entrySet()) { for(SuperGraphEdge edge : segment.incomingEdgesOf(entry.getKey())) { MiscUtils.incrementBy(edgeCostMap , edge, entry.getValue(), 0); } } return edgeCostMap; } }