/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.adaptive.controller;
import static org.jikesrvm.VM.NOT_REACHED;
import java.io.PrintStream;
import java.util.LinkedList;
import org.jikesrvm.VM;
import org.jikesrvm.classloader.RVMMethod;
import org.jikesrvm.compilers.common.CompiledMethod;
import org.jikesrvm.compilers.common.CompiledMethods;
import org.jikesrvm.util.ImmutableEntryHashMapRVM;
/**
* This class records decisions taken by the controller. It will remember
* controller plans, which contain compilation plans and other goodies,
* and allows searching for previous decisions
*/
public final class ControllerMemory {
/**
* This is a hashtable of controller plans indexed by RVMMethod.
* Each method can have a list of such plans associated with.
*/
private static final ImmutableEntryHashMapRVM<RVMMethod, LinkedList<ControllerPlan>> table =
new ImmutableEntryHashMapRVM<RVMMethod, LinkedList<ControllerPlan>>();
/**
* Number of times controller is awoken and did nothing.
*/
private static int didNothing = 0;
/**
* Number of times controller is awoken
*/
private static int awoken = 0;
// counters for chosen opt levels
private static int numMethodsConsidered = 0;
private static int numMethodsScheduledForRecomp = 0;
private static int numBase = 0;
private static int numOpt0 = 0;
private static int numOpt1 = 0;
private static int numOpt2 = 0;
private static int numOpt3 = 0;
private static int numOpt4 = 0;
public static int getNumAwoken() {
return awoken;
}
public static int getNumDidNothing() {
return didNothing;
}
public static int getNumMethodsConsidered() {
return numMethodsConsidered;
}
public static int getNumMethodsScheduledForRecomp() {
return numMethodsScheduledForRecomp;
}
public static int getNumBase() {
return numBase;
}
public static int getNumOpt0() {
return numOpt0;
}
public static int getNumOpt1() {
return numOpt1;
}
public static int getNumOpt2() {
return numOpt2;
}
public static int getNumOpt3() {
return numOpt3;
}
static int getNumOpt4() {
return numOpt4;
}
static void incrementNumAwoken() {
awoken++;
}
static void incrementNumDidNothing() {
didNothing++;
}
static void incrementNumMethodsConsidered() {
numMethodsConsidered++;
}
static void incrementNumMethodsScheduledForRecomp() {
numMethodsScheduledForRecomp++;
}
public static void incrementNumBase() {
numBase++;
}
static void incrementNumOpt0() {
numOpt0++;
}
static void incrementNumOpt1() {
numOpt1++;
}
static void incrementNumOpt2() {
numOpt2++;
}
static void incrementNumOpt3() {
numOpt3++;
}
static void incrementNumOpt4() {
numOpt4++;
}
/**
* Inserts a controller plan keyed on the underlying method
*
* @param plan the controller plan to insert
*/
static synchronized void insert(ControllerPlan plan) {
numMethodsScheduledForRecomp++;
int optLevel = plan.getCompPlan().options.getOptLevel();
switch (optLevel) {
case 0:
numOpt0++;
break;
case 1:
numOpt1++;
break;
case 2:
numOpt2++;
break;
case 3:
numOpt3++;
break;
case 4:
numOpt4++;
break;
default:
if (VM.VerifyAssertions) VM._assert(NOT_REACHED, "Unknown Opt Level");
}
// first check to see if there is a plan list for this method
LinkedList<ControllerPlan> planList = findPlan(plan.getCompPlan().method);
if (planList == null) {
// create a plan list, with the single element being this plan
planList = new LinkedList<ControllerPlan>();
// no synch needed here because the planList is not in the table yet
planList.addLast(plan);
// insert in the hashtable using the method as the hash value
table.put(plan.getCompPlan().method, planList);
} else {
// add the current plan to the end of the list
synchronized (planList) {
planList.addLast(plan);
}
}
// tell the plan what list it is on
plan.setPlanList(planList);
}
/**
* Looks for a controller plan for the passed method
*
* @param method The method to look for
* @return the list of controller plans for this method if one exists,
* otherwise, {@code null}
*/
private static synchronized LinkedList<ControllerPlan> findPlan(RVMMethod method) {
return table.get(method);
}
/**
* Find the plan for the compiled method that is passed
* @param cmpMethod the compiled method of interest
* @return the matching plan or {@code null} if none exists.
*/
public static synchronized ControllerPlan findMatchingPlan(CompiledMethod cmpMethod) {
RVMMethod method = cmpMethod.getMethod();
LinkedList<ControllerPlan> planList = findPlan(method);
if (planList == null) {
return null;
} else {
// iterate over the planList until we get to this item
synchronized (planList) {
for (ControllerPlan plan : planList) {
// exit when we find ourselves
if (plan.getCMID() == cmpMethod.getId()) {
return plan;
}
} // more to process
}
return null;
}
}
/**
* Determine if the passed method should be considered as a candidate
* for _initial_ AOS recompilation.
* A method should not be reconsider for initial AOS recompilation if
* a plan already exists for the method whose status is {@link ControllerPlan#IN_PROGRESS},
* {@link ControllerPlan#COMPLETED}, {@link ControllerPlan#OUTDATED},
* or {@link ControllerPlan#ABORTED_COMPILATION_ERROR} because of compilation error.
*
* @param method the method of interest
* @return whether the method should be considered or not
*/
static synchronized boolean shouldConsiderForInitialRecompilation(RVMMethod method) {
LinkedList<ControllerPlan> planList = findPlan(method);
if (planList == null) {
return true;
} else {
// iterate over the planList until we find a plan whose status is
// inprogress, completed,
synchronized (planList) {
for (ControllerPlan curPlan : planList) {
// exit when we find ourselves
byte status = curPlan.getStatus();
if (status == ControllerPlan.COMPLETED ||
status == ControllerPlan.IN_PROGRESS ||
status == ControllerPlan.ABORTED_COMPILATION_ERROR ||
status == ControllerPlan.OUTDATED) {
return false;
}
}
}
return true; // we didn't find any, so return true
}
}
/**
* Return {@code true} if there is a plan with the given status for the given method
*
* @param method the method of interest
* @param status the status of interest
* @return whether or not there is plan with that status for the method
*/
static synchronized boolean planWithStatus(RVMMethod method, byte status) {
LinkedList<ControllerPlan> planList = findPlan(method);
if (planList != null) {
// iterate over the planList until we find a plan with status 'status'
synchronized (planList) {
for (ControllerPlan curPlan : planList) {
if (curPlan.getStatus() == status) {
return true;
}
}
}
}
return false;
}
/**
* @return {@code true} iff there is a plan to transition from Base to Opt for a
* given CMID
*
* @param cmid the compiled method's ID
*/
public static synchronized boolean requestedOSR(int cmid) {
CompiledMethod cm = CompiledMethods.getCompiledMethod(cmid);
// make sure that the cm in question is baseline-compiled
if (cm.getCompilerType() != CompiledMethod.BASELINE) return false;
// OK; now check for an OSR plan
RVMMethod m = cm.getMethod();
if (m == null) return false;
return planWithStatus(m, ControllerPlan.OSR_BASE_2_OPT);
}
/**
* Return {@code true} if there is a completed plan with the given opt level for
* the given method
*
* @param method the method of interest
* @param optLevel the opt level of interest
* @return whether or not there is completed plan with that level
* for the method
*/
static synchronized boolean completedPlanWithOptLevel(RVMMethod method, int optLevel) {
LinkedList<ControllerPlan> planList = findPlan(method);
if (planList != null) {
// iterate over the planList until we find a completed plan with the
// opt level passed
synchronized (planList) {
for (ControllerPlan curPlan : planList) {
if (curPlan.getStatus() == ControllerPlan.COMPLETED &&
curPlan.getCompPlan().options.getOptLevel() == optLevel) {
return true;
}
}
}
}
return false;
}
/**
* Looks for the last controller plan for the passed method
*
* @param method The method to look for
* @return The last controller plan for this method if it exists,
* otherwise, {@code null}
*/
public static synchronized ControllerPlan findLatestPlan(RVMMethod method) {
LinkedList<ControllerPlan> planList = findPlan(method);
if (planList == null) {
return null;
} else {
return planList.getLast();
}
}
/**
* This method summarizes the recompilation actions taken for all methods
* in this object and produces a report to the passed PrintStream.
* @param log the stream to print to
*/
public static synchronized void printFinalMethodStats(PrintStream log) {
// We will traverse the hash table and for each method record its status as
// one of the following
// B -> 0 -> 1 -> 2
// B -> 0 -> 1
// B -> 0
// B -> 1 -> 2
// B -> 0 -> 2
// B -> 2
// B -> 1
//
// We encode these possibilities by turning on 1 of three bits for 0, 1, 2
// Also, for all methods that eventually get to level 2, they can be
// recompiled an arbitrary amount of times. We record this in in a counter.
final int MAX_BIT_PATTERN = 7;
int[] summaryArray = new int[MAX_BIT_PATTERN + 1];
int[] recompsAtLevel2Array = new int[MAX_BIT_PATTERN + 1];
int totalRecompsAtLevel2 = 0;
// traverse table and give a summary of all actions that have occurred
for (RVMMethod meth : table.keys()) {
LinkedList<ControllerPlan> planList = table.get(meth);
int bitPattern = 0;
int recompsAtLevel2 = 0;
for (ControllerPlan plan : planList) {
// only process plans that were completed or completed and outdated
// by subsequent plans for this method
byte status = plan.getStatus();
if (status == ControllerPlan.COMPLETED || status == ControllerPlan.OUTDATED) {
int optLevel = plan.getCompPlan().options.getOptLevel();
// check for recomps at level 2
if (optLevel == 2 && bitIsSet(bitPattern, 2)) {
recompsAtLevel2++;
}
bitPattern = setBitPattern(bitPattern, optLevel);
} // if
} // while
if (Controller.options.LOGGING_LEVEL >= 2) {
log.println("Method: " + meth + ", bitPattern: " + bitPattern + ", recompsAtLevel2: " + recompsAtLevel2);
}
summaryArray[bitPattern]++;
// track level 2 recomps per pattern
recompsAtLevel2Array[bitPattern] += recompsAtLevel2;
}
// Print the summary
int totalUniqueMethods = 0;
for (int i = 1; i <= MAX_BIT_PATTERN; i++) {
log.print(" Base");
for (int optLevel = 0; optLevel <= 2; optLevel++) {
if (bitIsSet(i, optLevel)) {
log.print(" -> " + optLevel);
}
}
log.print(": " + summaryArray[i]);
// print any level 2 recomps for this pattern
if (recompsAtLevel2Array[i] > 0) {
totalRecompsAtLevel2 += recompsAtLevel2Array[i];
log.println(" (" + recompsAtLevel2Array[i] + " opt level 2 recomps)");
} else {
log.println();
}
totalUniqueMethods = totalUniqueMethods + summaryArray[i];
}
log.println(" Num recompilations At level 2: " + totalRecompsAtLevel2);
log.println(" Num unique methods recompiled: " + totalUniqueMethods);
log.println();
}
/**
* Sets the optLevel bit in the passed bitPattern and return the result
* @param bitPattern the given bit pattern
* @param optLevel the new opt level
* @return the new bit pattern
*/
static int setBitPattern(int bitPattern, int optLevel) {
int newPattern = 1;
newPattern = newPattern << optLevel;
return newPattern | bitPattern;
}
/**
* Checks if the bit position defined by the 2nd parm is set in the first parm
* @param bitPattern a bit pattern
* @param optLevel the opt level to check for
* @return whether the passed bit is set
*/
static boolean bitIsSet(int bitPattern, int optLevel) {
int newPattern = 1;
newPattern = newPattern << optLevel;
return (newPattern & bitPattern) > 0;
}
static synchronized String asString() {
return table.toString();
}
}