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.database.callgraph; 014 015import org.jikesrvm.classloader.RVMMethod; 016import org.jikesrvm.runtime.RuntimeEntrypoints; 017 018/** 019 * A collection of weighted call targets. 020 * Depending on the size of the callee set, we use different subclasses 021 * that optimize the time/space tradeoffs. 022 */ 023public abstract class WeightedCallTargets { 024 025 /** 026 * Iterate over all of the targets, evaluating the argument function on each edge.<p> 027 * NOTE: We guarantee that the targets will be iterated in monotonically decreasing 028 * edge weight. This simplifies the coding of the inlining clients that consume 029 * this information. 030 * @param func the function to evaluate on each target 031 */ 032 public abstract void visitTargets(Visitor func); 033 034 /** 035 * Augment the weight associated with the argument method by 1.<p> 036 * NOTE: This method may change the representation of the target 037 * method. The caller must be sure to update their backing store of 038 * WeightedCallTargets accordingly to avoid losing the update. 039 * 040 * @param target the method whose count is to be incremented 041 * @return the object representing the method's targets 042 */ 043 public final WeightedCallTargets incrementCount(RVMMethod target) { 044 return augmentCount(target, 1); 045 } 046 047 /** 048 * Augment the weight associated with the argument method by the argument amount. 049 * NOTE: This method may change the representation of the target 050 * method. The caller must be sure to update their backing store of 051 * WeightedCallTargets accordingly to avoid losing the update. 052 * 053 * @param target the method whose count is to be incremented 054 * @param amount amount to increment by 055 * @return the object representing the method's targets 056 */ 057 public abstract WeightedCallTargets augmentCount(RVMMethod target, double amount); 058 059 /** 060 * Decay the weights of all call targets by the specified amount 061 * @param rate the value to decay by 062 */ 063 public abstract void decay(double rate); 064 065 /** 066 * @return total weight of all call targets 067 */ 068 public abstract double totalWeight(); 069 070 /** 071 * @param goal RVMMethod that is the statically possible target 072 * @param isPrecise whether or not goal is a precise target, or should be 073 * interpreted as being the root of a virtual method family, any of which 074 * are statically possible. 075 * @return the filtered call targets or {@code null} if no such target exists 076 */ 077 public abstract WeightedCallTargets filter(RVMMethod goal, boolean isPrecise); 078 079 public static WeightedCallTargets create(RVMMethod target, double weight) { 080 return new SingleTarget(target, weight); 081 } 082 083 /** 084 * Used by visitTargets 085 */ 086 public interface Visitor { 087 void visit(RVMMethod target, double weight); 088 } 089 090 /** 091 * An implementation for storing a call site distribution that has a single target. 092 */ 093 private static final class SingleTarget extends WeightedCallTargets { 094 private final RVMMethod target; 095 private float weight; 096 097 SingleTarget(RVMMethod t, double w) { 098 target = t; 099 weight = (float) w; 100 } 101 102 @Override 103 public void visitTargets(Visitor func) { 104 func.visit(target, weight); 105 } 106 107 @Override 108 public WeightedCallTargets augmentCount(RVMMethod t, double v) { 109 if (target.equals(t)) { 110 weight += v; 111 return this; 112 } else { 113 MultiTarget ms = new MultiTarget(); 114 ms.augmentCount(target, weight); 115 ms.augmentCount(t, v); 116 return ms; 117 } 118 } 119 120 @Override 121 public void decay(double rate) { 122 weight /= rate; 123 } 124 125 @Override 126 public double totalWeight() { 127 return weight; 128 } 129 130 @Override 131 public WeightedCallTargets filter(RVMMethod goal, boolean isPrecise) { 132 if (isPrecise) { 133 return (goal.equals(target)) ? this : null; 134 } else { 135 if (goal.equals(target)) { 136 return this; 137 } 138 if (RuntimeEntrypoints.isAssignableWith(goal.getDeclaringClass(), target.getDeclaringClass())) { 139 return this; 140 } else { 141 return null; 142 } 143 } 144 } 145 } 146 147 /** 148 * An implementation for storing a call site distribution that has multiple targets. 149 */ 150 private static final class MultiTarget extends WeightedCallTargets { 151 RVMMethod[] methods = new RVMMethod[5]; 152 float[] weights = new float[5]; 153 154 @Override 155 public synchronized void visitTargets(Visitor func) { 156 // Typically expect elements to be "almost" sorted due to previous sorting operations. 157 // When this is true, expected time for insertion sort is O(n). 158 for (int i = 1; i < methods.length; i++) { 159 RVMMethod m = methods[i]; 160 if (m != null) { 161 float w = weights[i]; 162 int j = i; 163 while (j > 0 && weights[j - 1] < w) { 164 methods[j] = methods[j - 1]; 165 weights[j] = weights[j - 1]; 166 j--; 167 } 168 methods[j] = m; 169 weights[j] = w; 170 } 171 } 172 173 for (int i = 0; i < methods.length; i++) { 174 if (methods[i] != null) { 175 func.visit(methods[i], weights[i]); 176 } 177 } 178 } 179 180 @Override 181 public synchronized WeightedCallTargets augmentCount(RVMMethod t, double v) { 182 int empty = -1; 183 for (int i = 0; i < methods.length; i++) { 184 if (methods[i] != null) { 185 if (methods[i].equals(t)) { 186 weights[i] += v; 187 return this; 188 } 189 } else { 190 if (empty == -1) { 191 empty = i; 192 } 193 } 194 } 195 196 // New target must be added 197 if (empty == -1) { 198 // must grow arrays 199 empty = methods.length; 200 RVMMethod[] newM = new RVMMethod[methods.length * 2]; 201 System.arraycopy(methods, 0, newM, 0, methods.length); 202 methods = newM; 203 float[] newW = new float[weights.length * 2]; 204 System.arraycopy(weights, 0, newW, 0, weights.length); 205 weights = newW; 206 } 207 208 methods[empty] = t; 209 weights[empty] = (float) v; 210 return this; 211 } 212 213 @Override 214 public synchronized void decay(double rate) { 215 for (int i = 0; i < weights.length; i++) { 216 weights[i] /= rate; 217 } 218 } 219 220 @Override 221 public synchronized double totalWeight() { 222 double sum = 0; 223 for (float weight : weights) { 224 sum += weight; 225 } 226 return sum; 227 } 228 229 @Override 230 public synchronized WeightedCallTargets filter(RVMMethod goal, boolean isPrecise) { 231 if (isPrecise) { 232 for (int i = 0; i < methods.length; i++) { 233 if (goal.equals(methods[i])) { 234 return WeightedCallTargets.create(methods[i], weights[i]); 235 } 236 } 237 return null; 238 } else { 239 int matchCount = 0; 240 int mismatchCount = 0; 241 for (RVMMethod method : methods) { 242 if (method != null) { 243 if (RuntimeEntrypoints.isAssignableWith(goal.getDeclaringClass(), method.getDeclaringClass())) { 244 matchCount++; 245 } else { 246 mismatchCount++; 247 } 248 } 249 } 250 if (mismatchCount == 0) { 251 return this; 252 } 253 if (matchCount == 0) { 254 return null; 255 } 256 257 MultiTarget filtered = new MultiTarget(); 258 for (int i = 0; i < methods.length; i++) { 259 RVMMethod method = methods[i]; 260 if (method != null && RuntimeEntrypoints.isAssignableWith(goal.getDeclaringClass(), method.getDeclaringClass())) { 261 filtered.augmentCount(method, weights[i]); 262 } 263 } 264 return filtered; 265 } 266 } 267 } 268}