001/* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013package org.jikesrvm.adaptive.controller; 014 015import org.jikesrvm.VM; 016import org.jikesrvm.adaptive.recompilation.CompilerDNA; 017import org.jikesrvm.adaptive.util.AOSLogging; 018import org.jikesrvm.classloader.NormalMethod; 019import org.jikesrvm.compilers.common.CompiledMethod; 020 021/** 022 * This class encapsulates the analytic model used by the controller 023 * to guide multi-level recompilation decisions. An early version of 024 * this model is described in the OOPSLA'2000 paper, but we've made 025 * some improvements since then... 026 * 027 * @see MultiLevelAdaptiveModel 028 */ 029abstract class AnalyticModel extends RecompilationStrategy { 030 031 //---- Interface ------ 032 // Code that inherits from AnalyticModel must define the 033 // following behavior 034 035 /** 036 * Initialize the set of "optimization choices" that the 037 * cost-benefit model will consider when using will consider when 038 * using adaptive compilation. 039 */ 040 abstract void populateRecompilationChoices(); 041 042 /** 043 * Computes the set of optimization choices that should be 044 * considered by the cost-benefit model, given the previous compiler. 045 * 046 * @param prevCompiler The compiler compiler that was used to 047 * compile cmpMethod 048 * @param cmpMethod The compiled method being considered 049 * 050 * @return the choices to consider 051 */ 052 abstract RecompilationChoice[] getViableRecompilationChoices(int prevCompiler, CompiledMethod cmpMethod); 053 054 // ----------------------------------------------------- 055 // Below code that is (currently) common to all recompilation 056 // strategies that use the analytic model. 057 058 /** 059 * Initialize the analytic model: 060 * 061 * NOTE: The call to super.init() uses the command line options to 062 * set up the optimization plans, so this must be run after the 063 * command line options are available. 064 */ 065 @Override 066 void init() { 067 // Do the common initialization first 068 super.init(); 069 070 // setup the recompilation choices that are available to the 071 // analytic model 072 populateRecompilationChoices(); 073 } 074 075 /** 076 * This method is the main decision making loop for all 077 * recompilation strategies that use the analytic model. 078 * <p> 079 * Given a HotMethodRecompilationEvent, this code will determine 080 * IF the method should be recompiled, and if so, HOW to perform 081 * the recompilation, i.e., what compilation plan should be used. 082 * The method returns a controller plan, which contains the compilation 083 * plan and other goodies. 084 * 085 * @param cmpMethod the compiled method of interest 086 * @param hme the HotMethodRecompilationEvent 087 * @return the controller plan to be used or NULL, if no 088 * compilation is to be performed. */ 089 @Override 090 ControllerPlan considerHotMethod(CompiledMethod cmpMethod, HotMethodEvent hme) { 091 // Compiler used for the previous compilation 092 int prevCompiler = getPreviousCompiler(cmpMethod); 093 if (prevCompiler == -1) { 094 return null; // Not a method that we can recompile (trap, JNI). 095 } 096 097 ControllerPlan plan = ControllerMemory.findMatchingPlan(cmpMethod); 098 099 // for a outdated hot method from baseline, we consider OSR, 100 // and execute plan in the routine, no more action here 101 if (considerOSRRecompilation(cmpMethod, hme, plan)) return null; 102 103 if (!considerForRecompilation(hme, plan)) return null; 104 105 // Now we know the compiler that generated the method (prevCompiler) and 106 // that the method is a potential candidate for additional recompilation. 107 // So, next decide what, if anything, should be done now. 108 // We consider doing nothing (ie leaving the method at the current 109 // opt level, which incurs no compilation cost), and recompiling the 110 // method at each greater compilation level. 111 double futureTimeForMethod = futureTimeForMethod(hme); 112 113 // initialize bestAction as doing nothing, which means we'll 114 // spend just as much time in the method in the future as we have so far. 115 RecompilationChoice bestActionChoice = null; 116 double bestActionTime = futureTimeForMethod; 117 double bestCost = 0.0; 118 119 AOSLogging.logger.recordControllerEstimateCostDoNothing(cmpMethod.getMethod(), 120 CompilerDNA.getOptLevel(prevCompiler), 121 bestActionTime); 122 123 // Get a vector of optimization choices to consider 124 RecompilationChoice[] recompilationChoices = getViableRecompilationChoices(prevCompiler, cmpMethod); 125 126 // Consider all choices in the vector of possibilities 127 NormalMethod meth = (NormalMethod) hme.getMethod(); 128 for (RecompilationChoice choice : recompilationChoices) { 129 // Get the cost and benefit of this choice 130 double cost = choice.getCost(meth); 131 double futureExecutionTime = choice.getFutureExecutionTime(prevCompiler, futureTimeForMethod); 132 133 double curActionTime = cost + futureExecutionTime; 134 135 AOSLogging.logger.recordControllerEstimateCostOpt(cmpMethod.getMethod(), choice.toString(), cost, curActionTime); 136 137 if (curActionTime < bestActionTime) { 138 bestActionTime = curActionTime; 139 bestActionChoice = choice; 140 bestCost = cost; 141 } 142 } 143 144 // if the best action is the previous than we don't need to recompile 145 if (bestActionChoice == null) { 146 plan = null; 147 } else { 148 plan = 149 bestActionChoice.makeControllerPlan(cmpMethod, prevCompiler, futureTimeForMethod, bestActionTime, bestCost); 150 } 151 return plan; 152 } 153 154 /* check if a compiled method is outdated, then decide if it needs OSR from BASE to OPT 155 */ 156 boolean considerOSRRecompilation(CompiledMethod cmpMethod, HotMethodEvent hme, ControllerPlan plan) { 157 boolean outdatedBaseline = false; 158 if (plan == null) { 159 // if plan is null, this method was not compiled by AOS; it was 160 // either in the boot image or compiled by the initial baseline 161 // compiler. In either case, if we've completed any recompilation 162 // then the compiled method is outdated. 163 outdatedBaseline = 164 ControllerMemory.planWithStatus(cmpMethod.getMethod(), ControllerPlan.COMPLETED) && 165 cmpMethod.getCompilerType() == CompiledMethod.BASELINE; 166 if (outdatedBaseline) { 167 AOSLogging.logger.debug("outdated Baseline " + cmpMethod.getMethod() + "(" + cmpMethod.getId() + ")"); 168 } 169 } 170 171 // consider OSR option for old baseline-compiled activation 172 if (outdatedBaseline) { 173 if (!hme.getCompiledMethod().getSamplesReset()) { 174 // the first time we see an outdated event, we clear the samples 175 // associated with the cmid. 176 hme.getCompiledMethod().setSamplesReset(); 177 Controller.methodSamples.reset(hme.getCMID()); 178 AOSLogging.logger.debug(" Resetting method samples " + hme); 179 return true; 180 } else { 181 plan = chooseOSRRecompilation(hme); 182 // insert the plan to memory, which sets up state in the system to trigger 183 // the OSR promotion 184 if (plan != null) { 185 ControllerMemory.insert(plan); 186 // to coordinate with OSRListener, it marks cmpMethod as outdated 187 if (VM.VerifyAssertions) { 188 VM._assert(cmpMethod.getCompilerType() == CompiledMethod.BASELINE); 189 } 190 cmpMethod.setOutdated(); 191 } 192 // we don't do any more action on the controller side. 193 return true; 194 } 195 } 196 return false; 197 } 198 199 /** 200 * @param hme sample data for an outdated cmid 201 * @return a plan representing recompilation with OSR, null if OSR not 202 * justified. 203 */ 204 private ControllerPlan chooseOSRRecompilation(HotMethodEvent hme) { 205 if (!Controller.options.OSR_PROMOTION) return null; 206 207 AOSLogging.logger.debug(" Consider OSR for " + hme); 208 209 ControllerPlan prev = ControllerMemory.findLatestPlan(hme.getMethod()); 210 211 if (prev.getStatus() == ControllerPlan.OSR_BASE_2_OPT) { 212 AOSLogging.logger.debug(" Already have an OSR promotion plan for this method"); 213 return null; 214 } 215 216 double millis = prev.getTimeCompleted() - prev.getTimeInitiated(); 217 double speedup = prev.getExpectedSpeedup(); 218 double futureTimeForMethod = futureTimeForMethod(hme); 219 220 double futureTimeOptimized = futureTimeForMethod / speedup; 221 222 AOSLogging.logger.debug(" Estimated future time for method " + hme + " is " + futureTimeForMethod); 223 AOSLogging.logger.debug(" Estimated future time optimized " + hme + " is " + (futureTimeOptimized + millis)); 224 225 if (futureTimeForMethod > futureTimeOptimized + millis) { 226 AOSLogging.logger.recordOSRRecompilationDecision(prev); 227 ControllerPlan p = 228 new ControllerPlan(prev.getCompPlan(), 229 prev.getTimeCreated(), 230 hme.getCMID(), 231 prev.getExpectedSpeedup(), 232 millis, 233 prev.getPriority()); 234 // set up state to trigger osr 235 p.setStatus(ControllerPlan.OSR_BASE_2_OPT); 236 return p; 237 } else { 238 return null; 239 } 240 } 241 242 /** 243 * This function defines how the analytic model handles a 244 * AINewHotEdgeEvent. The basic idea is to use the model to 245 * evaluate whether it would be better to do nothing or to recompile 246 * at the same opt level, assuming there would be some "boost" after 247 * performing inlining. 248 */ 249 @Override 250 void considerHotCallEdge(CompiledMethod cmpMethod, AINewHotEdgeEvent event) { 251 252 // Compiler used for the previous compilation 253 int prevCompiler = getPreviousCompiler(cmpMethod); 254 if (prevCompiler == -1) { 255 return; // Not a method we can recompile (trap, JNI). 256 } 257 258 ControllerPlan plan = ControllerMemory.findMatchingPlan(cmpMethod); 259 if (!considerForRecompilation(event, plan)) return; 260 double prevCompileTime = cmpMethod.getCompilationTime(); 261 262 // Use the model to calculate expected cost of (1) doing nothing 263 // and (2) recompiling at the same opt level with the FDO boost 264 double futureTimeForMethod = futureTimeForMethod(event); 265 double futureTimeForFDOMethod = prevCompileTime + (futureTimeForMethod / event.getBoostFactor()); 266 267 int prevOptLevel = CompilerDNA.getOptLevel(prevCompiler); 268 AOSLogging.logger.recordControllerEstimateCostDoNothing(cmpMethod.getMethod(), prevOptLevel, futureTimeForMethod); 269 AOSLogging.logger.recordControllerEstimateCostOpt(cmpMethod.getMethod(), 270 "O" + prevOptLevel + "AI", 271 prevCompileTime, 272 futureTimeForFDOMethod); 273 274 if (futureTimeForFDOMethod < futureTimeForMethod) { 275 // Profitable to recompile with FDO, so do it. 276 int optLevel = CompilerDNA.getOptLevel(prevCompiler); 277 double priority = futureTimeForMethod - futureTimeForFDOMethod; 278 plan = 279 createControllerPlan(cmpMethod.getMethod(), 280 optLevel, 281 null, 282 cmpMethod.getId(), 283 event.getBoostFactor(), 284 futureTimeForFDOMethod, 285 priority); 286 plan.execute(); 287 } 288 } 289 290 /** 291 * How much time do we expect to spend in the method in the future if 292 * we take no recompilation action? 293 * The key assumption is that we'll spend just as much time 294 * executing in the the method in the future as we have done so far 295 * in the past. 296 * 297 * @param hme The HotMethodEvent in question 298 * @return estimate of future execution time to be spent in this method 299 */ 300 double futureTimeForMethod(HotMethodEvent hme) { 301 double numSamples = hme.getNumSamples(); 302 double timePerSample = VM.interruptQuantum; 303 if (!VM.UseEpilogueYieldPoints) { 304 // NOTE: we take two samples per timer interrupt, so we have to 305 // adjust here (otherwise we'd give the method twice as much time 306 // as it actually deserves). 307 timePerSample /= 2.0; 308 } 309 if (Controller.options.mlCBS()) { 310 // multiple method samples per timer interrupt. Divide accordingly. 311 timePerSample /= VM.CBSMethodSamplesPerTick; 312 } 313 double timeInMethodSoFar = numSamples * timePerSample; 314 return timeInMethodSoFar; 315 } 316}