/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Common Public License (CPL); * 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/cpl1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.adaptive.recompilation; import java.io.FileReader; import java.io.IOException; import java.io.LineNumberReader; import java.util.StringTokenizer; import org.jikesrvm.VM; import org.jikesrvm.VM_Constants; import org.jikesrvm.adaptive.controller.VM_Controller; import org.jikesrvm.adaptive.util.VM_AOSLogging; import org.jikesrvm.classloader.VM_NormalMethod; import org.jikesrvm.compilers.common.VM_RuntimeCompiler; /** * This class codifies the cost/benefit properties of the various compilers * used in the adaptive optimization system. * * <p> * The DNA tells the AOS two important kinds of averages for each optimization * level: the cost of compiling at an optimization level (as measured in * bytecode/milliseconds) and the expected speedup of the resulting code * (relative to the first compiler). * * <p>There is an AOS command-line option to set the compiler DNA. The method * {@link VM_CompilerDNA#readDNA} contains a comment on the expected format. */ public class VM_CompilerDNA implements VM_Constants { private static final String[] compilerNames = {"Baseline", "Opt0", "Opt1", "Opt2"}; public static final int BASELINE = 0; static final int OPT0 = 1; static final int OPT1 = 2; static final int OPT2 = 3; /** * The number of compilers available */ private static int numCompilers; /** * Average bytecodes compiled per millisecond. * These numbers were measured on July 1, 2007 on excalibur (AIX/PPC) * and July 1, 2007 on legato (Linux/IA32) using unweighted compilation rate. */ private static final double[] compilationRates; static { if (VM.BuildForPowerPC) { compilationRates = new double[]{525.53, // base 15.28, 7.74, 7.68}; // opt 0...2 } else if (VM.BuildForIA32) { compilationRates = new double[]{1246.22, // base 30.03, 14.60, 14.59}; // opt 0...2 } else { if (VM.VerifyAssertions) VM._assert(NOT_REACHED); compilationRates = null; } } /** * What is the execution rate of each compiler normalized to the 1st compiler * These numbers were measured on July 1, 2007 on excalibur (AIX/PPC, IBM Power4+) * and July 1, 2007 on legato (Linux/IA32, IBM LS20) using unweighted compilation rate. */ private static final double[] speedupRates; static { if (VM.BuildForPowerPC) { speedupRates = new double[]{1.00, // base 8.22, 13.25, 13.26}; // opt 0...2 } else if (VM.BuildForIA32) { speedupRates = new double[]{1.00, // base 4.86, 6.39, 6.40}; // opt 0...2 } else { if (VM.VerifyAssertions) VM._assert(NOT_REACHED); speedupRates = null; } } /** * Benefits of moving from one compilation level to another * USAGE NOTE: The data is layed out in a upper triangular matrix */ private static double[][] benefitRatio; /** * Compile time ratio of one compilation level to another * For example, if compiler1 (say OPT1) compiles at 50 bc/msec * and compiler2 (say OPT2) compiles at 100 bc/msec, * compileTimeRatio[OPT1][OPT2] = 2 * USAGE NOTE: The data is layed out in a upper triangular matrix */ private static double[][] compileTimeRatio; static { initializeCompilerRatioArrays(); } /** * This method returns the expected speedup from going from compiler1 to compiler2 * @param compiler1 * @param compiler2 * @return the benefit ratio (speedup) of moving from compiler1 to compiler2 */ public static double getBenefitRatio(int compiler1, int compiler2) { return benefitRatio[compiler1][compiler2]; } /** * What is the additional overhead (relative to compiler1 compile time) * of compile2 compile time. For example, if compiler1 compiles at * 50 bc/msec and compiler2 compiles at 100 bc/msec, this method returns 2 * @param compiler1 the compiler whose compile time we compare to * @param compiler2 the compiler's compile time we care about * @return the additional overhead (relative to compiler1 compile time) * of compile2 compile time */ public static double getCompileTimeRatio(int compiler1, int compiler2) { return compileTimeRatio[compiler1][compiler2]; } /** * Estimate how long (in milliseconds) it will/did take the * given compiler to compile the given method. * * @param compiler the compiler to compile meth * @param meth the method to be compiled * @return an estimate of compile time (in milliseconds) */ public static double estimateCompileTime(int compiler, VM_NormalMethod meth) { double bytes = (double) meth.getBytecodeLength(); double runtimeBaselineRate = VM_RuntimeCompiler.getBaselineRate(); double compileTime = bytes / runtimeBaselineRate; if (compiler != BASELINE) { compileTime *= compileTimeRatio[BASELINE][compiler]; } return compileTime; } /** * Returns the compilation rates of the baseline compiler in * bytecodes/millisecond. * @return the compilation rates of the baseline compiler in * bytecodes/millisecond */ public static double getBaselineCompilationRate() { return compilationRates[BASELINE]; } /** * initialize static fields */ public static void init() { // check to see if the raw rates are specified during boot time if (VM_Controller.options.COMPILER_DNA_FILE_NAME.length() != 0) { // Read the DNA values from disk readDNA(VM_Controller.options.COMPILER_DNA_FILE_NAME); initializeCompilerRatioArrays(); } for (int i = 0; i < compilationRates.length; i++) { VM_AOSLogging.reportCompilationRate(i, compilationRates[i]); } for (int i = 0; i < speedupRates.length; i++) { VM_AOSLogging.reportSpeedupRate(i, speedupRates[i]); } // Compute MAX_OPT_LEVEL int maxProfitableCompiler = 0; for (int compiler = 1; compiler < numCompilers; compiler++) { if (compilationRates[compiler] > compilationRates[compiler - 1] || speedupRates[compiler] > speedupRates[compiler - 1]) { maxProfitableCompiler = compiler; } } int maxOptLevel = getOptLevel(maxProfitableCompiler); VM_Controller.options.DERIVED_MAX_OPT_LEVEL = Math.min(maxOptLevel,VM_Controller.options.MAX_OPT_LEVEL); VM_Controller.options.DERIVED_FILTER_OPT_LEVEL = VM_Controller.options.DERIVED_MAX_OPT_LEVEL; } private static void initializeCompilerRatioArrays() { numCompilers = compilerNames.length; benefitRatio = new double[numCompilers][numCompilers]; compileTimeRatio = new double[numCompilers][numCompilers]; // fill in the upper triangular matrices for (int prevCompiler = 0; prevCompiler < numCompilers; prevCompiler++) { benefitRatio[prevCompiler][prevCompiler] = 1.0; compileTimeRatio[prevCompiler][prevCompiler] = 1.0; for (int nextCompiler = prevCompiler + 1; nextCompiler < numCompilers; nextCompiler++) { benefitRatio[prevCompiler][nextCompiler] = speedupRates[nextCompiler] / speedupRates[prevCompiler]; // Since compilation rates are not relative to the 1st compiler // we invert the division. compileTimeRatio[prevCompiler][nextCompiler] = compilationRates[prevCompiler] / compilationRates[nextCompiler]; VM_AOSLogging.reportBenefitRatio(prevCompiler, nextCompiler, benefitRatio[prevCompiler][nextCompiler]); VM_AOSLogging.reportCompileTimeRatio(prevCompiler, nextCompiler, compileTimeRatio[prevCompiler][nextCompiler]); } } } /** * Read a serialized representation of the DNA info * @param filename DNA filename */ private static void readDNA(String filename) { try { LineNumberReader in = new LineNumberReader(new FileReader(filename)); // Expected Format // CompilationRates aaa.a bbbb.b cccc.c dddd.d .... // SpeedupRates aaa.a bbbb.b cccc.c dddd.d .... processOneLine(in, "CompilationRates", compilationRates); processOneLine(in, "SpeedupRates", speedupRates); } catch (Exception e) { e.printStackTrace(); VM.sysFail("Failed to open controller DNA file"); } } /** * Helper method to read one line of the DNA file * @param in the LineNumberReader object * @param title the title string to look for * @param valueHolder the array to hold the read values */ private static void processOneLine(LineNumberReader in, String title, double[] valueHolder) throws IOException { String s = in.readLine(); if (VM.VerifyAssertions) VM._assert(s != null); // parse the string StringTokenizer parser = new StringTokenizer(s); // make sure the title matches String token = parser.nextToken(); if (VM.VerifyAssertions) VM._assert(token.equals(title)); // walk through the array, making sure we still have tokens for (int i = 0; parser.hasMoreTokens() && i < valueHolder.length; i++) { // get the available token token = parser.nextToken(); // convert token to a double valueHolder[i] = Double.valueOf(token); } } /** * returns the number of compilers * @return the number of compilers */ public static int getNumberOfCompilers() { return numCompilers; } /** * A mapping from an Opt compiler number to the corresponding Opt level * @param compiler the compiler constant of interest * @return the Opt level that corresponds to the Opt compiler constant passed */ public static int getOptLevel(int compiler) { switch (compiler) { case BASELINE: return -1; case OPT0: return 0; case OPT1: return 1; case OPT2: return 2; default: if (VM.VerifyAssertions) VM._assert(NOT_REACHED, "Unknown compiler constant\n"); return -99; } } /** * maps a compiler constant to a string * @param compiler * @return the string that represents the passed compiler constant */ public static String getCompilerString(int compiler) { return compilerNames[compiler]; } /** * maps opt levels to the compiler * @param optLevel opt level * @return the opt level that corresponds to the passed compiler constant */ public static int getCompilerConstant(int optLevel) { switch (optLevel) { case 0: return OPT0; case 1: return OPT1; case 2: return OPT2; default: if (VM.VerifyAssertions) VM._assert(NOT_REACHED, "Unknown Opt Level\n"); return -99; } } }