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.recompilation;
014
015import static org.jikesrvm.VM.NOT_REACHED;
016
017import java.io.FileReader;
018import java.io.IOException;
019import java.io.LineNumberReader;
020import java.util.StringTokenizer;
021import org.jikesrvm.VM;
022import org.jikesrvm.adaptive.controller.Controller;
023import org.jikesrvm.adaptive.util.AOSLogging;
024import org.jikesrvm.classloader.NormalMethod;
025import org.jikesrvm.compilers.common.RuntimeCompiler;
026
027/**
028 * This class codifies the cost/benefit properties of the various compilers
029 * used in the adaptive optimization system.<p>
030 *
031 * The DNA tells the AOS two important kinds of averages for each optimization
032 * level: the cost of compiling at an optimization level (as measured in
033 * bytecode/milliseconds) and the expected speedup of the resulting code
034 * (relative to the first compiler).<p>
035 *
036 * There is an AOS command-line option to set the compiler DNA.  The method
037 * {@link CompilerDNA#readDNA} contains a comment on the expected format.<p>
038 *
039 * This DNA was gathered on July 9, 2008 using revision r14679 + the bugfix in r14688.
040 * The PowerPC data was gathered on piccolo.watson.ibm.com (JS21, machine type 8884; ppc64-aix).
041 * The IA32 data was gathered on lyric.watson.ibm.com (LS41, machine type 7972; x86_64-linux).
042 */
043public class CompilerDNA {
044
045  private static final String[] compilerNames = {"Baseline", "Opt0", "Opt1", "Opt2"};
046  public static final int BASELINE = 0;
047  static final int OPT0 = 1;
048  static final int OPT1 = 2;
049  static final int OPT2 = 3;
050
051  /**
052   *  The number of compilers available
053   */
054  private static int numCompilers;
055
056  /**
057   * Average bytecodes compiled per millisecond.
058   */
059  private static final double[] compilationRates;
060
061  static {
062    if (VM.BuildForPowerPC) {
063      compilationRates = new double[]{667.32,             // base
064                                      26.36, 13.41, 12.73}; // opt 0...2
065    } else if (VM.BuildForIA32) {
066      compilationRates = new double[]{909.46,               // base
067                                      39.53, 18.48, 17.28}; // opt 0...2
068    } else {
069      if (VM.VerifyAssertions) VM._assert(NOT_REACHED);
070      compilationRates = null;
071    }
072  }
073
074  /**
075   * What is the execution rate of each compiler normalized to the 1st compiler
076   */
077  private static final double[] speedupRates;
078
079  static {
080    if (VM.BuildForPowerPC) {
081      speedupRates = new double[]{1.00,               // base
082                                  7.87, 12.23, 12.29};  // opt 0...2
083    } else if (VM.BuildForIA32) {
084      speedupRates = new double[]{1.00,               // base
085                                  4.03, 5.88, 5.93};  // opt 0...2
086    } else {
087      if (VM.VerifyAssertions) VM._assert(NOT_REACHED);
088      speedupRates = null;
089    }
090  }
091
092  /**
093   * Benefits of moving from one compilation level to another
094   * USAGE NOTE: The data is layed out in a upper triangular matrix
095   */
096  private static double[][] benefitRatio;
097
098  /**
099   * Compile time ratio of one compilation level to another
100   * For example, if compiler1 (say OPT1) compiles at 50 bc/msec
101   * and compiler2 (say OPT2) compiles at 100 bc/msec,
102   *    compileTimeRatio[OPT1][OPT2] = 2
103   * USAGE NOTE: The data is layed out in a upper triangular matrix
104   */
105  private static double[][] compileTimeRatio;
106
107  static {
108    initializeCompilerRatioArrays();
109  }
110
111  /**
112   * This method returns the expected speedup from going from compiler1 to compiler2
113   * @param compiler1 old compiler
114   * @param compiler2 new compiler
115   * @return the benefit ratio (speedup) of moving from compiler1 to compiler2
116   */
117  public static double getBenefitRatio(int compiler1, int compiler2) {
118    return benefitRatio[compiler1][compiler2];
119  }
120
121  /**
122   * What is the additional overhead (relative to compiler1 compile time)
123   * of compile2 compile time.  For example, if compiler1 compiles at
124   * 50 bc/msec and compiler2 compiles at 100 bc/msec, this method returns 2
125   * @param compiler1 the compiler whose compile time we compare to
126   * @param compiler2 the compiler's compile time we care about
127   * @return the additional overhead (relative to compiler1 compile time)
128   * of compile2 compile time
129   */
130  public static double getCompileTimeRatio(int compiler1, int compiler2) {
131    return compileTimeRatio[compiler1][compiler2];
132  }
133
134  /**
135   * Estimate how long (in milliseconds) it will/did take the
136   * given compiler to compile the given method.
137   *
138   * @param compiler the compiler to compile meth
139   * @param meth the method to be compiled
140   * @return an estimate of compile time (in milliseconds)
141   */
142  public static double estimateCompileTime(int compiler, NormalMethod meth) {
143    double bytes = meth.getBytecodeLength();
144    double runtimeBaselineRate = RuntimeCompiler.getBaselineRate();
145    double compileTime = bytes / runtimeBaselineRate;
146    if (compiler != BASELINE) {
147      compileTime *= compileTimeRatio[BASELINE][compiler];
148    }
149    return compileTime;
150  }
151
152  /**
153   * Returns the compilation rates of the baseline compiler in
154   *  bytecodes/millisecond.
155   * @return the compilation rates of the baseline compiler in
156   *   bytecodes/millisecond
157   */
158  public static double getBaselineCompilationRate() {
159    return compilationRates[BASELINE];
160  }
161
162  /**
163   * initialize static fields
164   */
165  public static void init() {
166    // check to see if the raw rates are specified during boot time
167    if (Controller.options.COMPILER_DNA_FILE_NAME.length() != 0) {
168      //  Read the DNA values from disk
169      readDNA(Controller.options.COMPILER_DNA_FILE_NAME);
170      initializeCompilerRatioArrays();
171    }
172
173    for (int i = 0; i < compilationRates.length; i++) {
174      AOSLogging.logger.reportCompilationRate(i, compilationRates[i]);
175    }
176    for (int i = 0; i < speedupRates.length; i++) {
177      AOSLogging.logger.reportSpeedupRate(i, speedupRates[i]);
178    }
179
180    // Compute MAX_OPT_LEVEL
181    int maxProfitableCompiler = 0;
182    for (int compiler = 1; compiler < numCompilers; compiler++) {
183      if (compilationRates[compiler] > compilationRates[compiler - 1] ||
184          speedupRates[compiler] > speedupRates[compiler - 1]) {
185        maxProfitableCompiler = compiler;
186      }
187    }
188    int maxOptLevel = getOptLevel(maxProfitableCompiler);
189    Controller.options.DERIVED_MAX_OPT_LEVEL = Math.min(maxOptLevel,Controller.options.MAX_OPT_LEVEL);
190    Controller.options.DERIVED_FILTER_OPT_LEVEL = Controller.options.DERIVED_MAX_OPT_LEVEL;
191  }
192
193  private static void initializeCompilerRatioArrays() {
194    numCompilers = compilerNames.length;
195    benefitRatio = new double[numCompilers][numCompilers];
196    compileTimeRatio = new double[numCompilers][numCompilers];
197
198    // fill in the upper triangular matrices
199    for (int prevCompiler = 0; prevCompiler < numCompilers; prevCompiler++) {
200
201      benefitRatio[prevCompiler][prevCompiler] = 1.0;
202      compileTimeRatio[prevCompiler][prevCompiler] = 1.0;
203
204      for (int nextCompiler = prevCompiler + 1; nextCompiler < numCompilers; nextCompiler++) {
205        benefitRatio[prevCompiler][nextCompiler] = speedupRates[nextCompiler] / speedupRates[prevCompiler];
206
207        // Since compilation rates are not relative to the 1st compiler
208        //  we invert the division.
209        compileTimeRatio[prevCompiler][nextCompiler] = compilationRates[prevCompiler] / compilationRates[nextCompiler];
210        AOSLogging.logger.reportBenefitRatio(prevCompiler, nextCompiler, benefitRatio[prevCompiler][nextCompiler]);
211
212        AOSLogging.logger.reportCompileTimeRatio(prevCompiler, nextCompiler, compileTimeRatio[prevCompiler][nextCompiler]);
213      }
214    }
215  }
216
217  /**
218   * Read a serialized representation of the DNA info
219   * @param filename DNA filename
220   */
221  private static void readDNA(String filename) {
222    try {
223
224      LineNumberReader in = new LineNumberReader(new FileReader(filename));
225
226      // Expected Format
227      //   CompilationRates  aaa.a  bbbb.b cccc.c dddd.d ....
228      //   SpeedupRates      aaa.a  bbbb.b cccc.c dddd.d ....
229      processOneLine(in, "CompilationRates", compilationRates);
230      processOneLine(in, "SpeedupRates", speedupRates);
231    } catch (Exception e) {
232      e.printStackTrace();
233      VM.sysFail("Failed to open controller DNA file");
234    }
235  }
236
237  /**
238   *  Helper method to read one line of the DNA file
239   *  @param in the LineNumberReader object
240   *  @param title the title string to look for
241   *  @param valueHolder the array to hold the read values
242   *  @throws IOException when there's a failure during processing of the file
243   */
244  private static void processOneLine(LineNumberReader in, String title, double[] valueHolder) throws IOException {
245
246    String s = in.readLine();
247    if (VM.VerifyAssertions) VM._assert(s != null);
248
249    // parse the string
250    StringTokenizer parser = new StringTokenizer(s);
251
252    // make sure the title matches
253    String token = parser.nextToken();
254    if (VM.VerifyAssertions) VM._assert(token.equals(title));
255
256    // walk through the array, making sure we still have tokens
257    for (int i = 0; parser.hasMoreTokens() && i < valueHolder.length; i++) {
258
259      // get the available token
260      token = parser.nextToken();
261
262      // convert token to a double
263      valueHolder[i] = Double.parseDouble(token);
264    }
265  }
266
267  /**
268   * returns the number of compilers
269   * @return the number of compilers
270   */
271  public static int getNumberOfCompilers() {
272    return numCompilers;
273  }
274
275  /**
276   * A mapping from an Opt compiler number to the corresponding Opt level
277   * @param compiler the compiler constant of interest
278   * @return the Opt level that corresponds to the Opt compiler constant passed
279   */
280  public static int getOptLevel(int compiler) {
281    switch (compiler) {
282      case BASELINE:
283        return -1;
284      case OPT0:
285        return 0;
286      case OPT1:
287        return 1;
288      case OPT2:
289        return 2;
290      default:
291        if (VM.VerifyAssertions) VM._assert(NOT_REACHED, "Unknown compiler constant\n");
292        return -99;
293    }
294  }
295
296  /**
297   * maps a compiler constant to a string
298   * @param compiler the constant for the compiler
299   * @return the string that represents the passed compiler constant
300   */
301  public static String getCompilerString(int compiler) {
302    return compilerNames[compiler];
303  }
304
305  /**
306   * maps opt levels to the compiler
307   * @param optLevel opt level
308   * @return the opt level that corresponds to the passed compiler constant
309   */
310  public static int getCompilerConstant(int optLevel) {
311    switch (optLevel) {
312      case 0:
313        return OPT0;
314      case 1:
315        return OPT1;
316      case 2:
317        return OPT2;
318      default:
319        if (VM.VerifyAssertions) VM._assert(NOT_REACHED, "Unknown Opt Level\n");
320        return -99;
321    }
322  }
323}