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.methodsamples; 014 015import java.util.ArrayList; 016import java.util.List; 017 018import org.jikesrvm.VM; 019import org.jikesrvm.adaptive.controller.Controller; 020import org.jikesrvm.adaptive.controller.HotMethodEvent; 021import org.jikesrvm.adaptive.controller.HotMethodRecompilationEvent; 022import org.jikesrvm.adaptive.measurements.Reportable; 023import org.jikesrvm.adaptive.util.AOSLogging; 024import org.jikesrvm.classloader.RVMMethod; 025import org.jikesrvm.compilers.common.CompiledMethod; 026import org.jikesrvm.compilers.common.CompiledMethods; 027import org.jikesrvm.compilers.opt.runtimesupport.OptCompiledMethod; 028import org.jikesrvm.scheduler.RVMThread; 029 030/** 031 * A container for recording how often a method is executed. 032 */ 033public final class MethodCountData implements Reportable { 034 035 private static final boolean DEBUG = false; 036 037 /** 038 * Sum of values in count array. 039 */ 040 private double totalCountsTaken; 041 042 /** 043 * Count array: counts how many times a method is executed. 044 * Constraint: counts[0] is not used. 045 */ 046 private double[] counts; 047 /** 048 * Maps count array index to compiled method id. 049 * Constraint: cmids[0] is not used. 050 */ 051 private int[] cmids; 052 /** 053 * Maps compiled method id to count array index. 054 * '0' implies that there is no entry in the count array for this cmid 055 */ 056 private int[] map; 057 /** 058 * Next available count array entry. 059 */ 060 private int nextIndex; 061 062 /** 063 * Constructor 064 */ 065 public MethodCountData() { 066 initialize(); 067 } 068 069 /** 070 * Reset fields. 071 */ 072 private void initialize() { 073 int numCompiledMethods = CompiledMethods.numCompiledMethods(); 074 map = new int[numCompiledMethods + (numCompiledMethods >>> 2)]; 075 counts = new double[256]; 076 cmids = new int[256]; 077 nextIndex = 1; 078 totalCountsTaken = 0; 079 } 080 081 /** 082 * Drain a buffer of compiled method id's and update the count array. 083 * 084 * @param countBuffer a buffer of compiled method id's 085 * @param numCounts the number of valid entries in the buffer 086 */ 087 public synchronized void update(int[] countBuffer, int numCounts) { 088 for (int i = 0; i < numCounts; i++) { 089 int cmid = countBuffer[i]; 090 int index = findOrCreateHeapIdx(cmid); 091 counts[index]++; // Record count 092 heapifyUp(index); // Fix up the heap 093 } 094 totalCountsTaken += numCounts; 095 if (DEBUG) validityCheck(); 096 } 097 098 /** 099 * Increment the count for a compiled method id. 100 * 101 * @param cmid compiled method id 102 * @param numCounts number of counts 103 */ 104 public synchronized void update(int cmid, double numCounts) { 105 int index = findOrCreateHeapIdx(cmid); 106 counts[index] += numCounts; // Record counts 107 heapifyUp(index); // Fix up the heap 108 totalCountsTaken += numCounts; 109 if (DEBUG) validityCheck(); 110 } 111 112 /** 113 * Print the counted (nonzero) methods. 114 * To get a sorted list, pipe the output through sort -n -r. 115 */ 116 @Override 117 public synchronized void report() { 118 RVMThread.dumpLock.lockNoHandshake(); 119 VM.sysWrite("Method counts: A total of " + totalCountsTaken + " samples\n"); 120 for (int i = 1; i < nextIndex; i++) { 121 double percent = 100 * countsToHotness(counts[i]); 122 CompiledMethod cm = CompiledMethods.getCompiledMethod(cmids[i]); 123 VM.sysWrite(counts[i] + " (" + percent + "%) "); 124 if (cm == null) { 125 VM.sysWriteln("OBSOLETE"); // Compiled Method Obsolete 126 } else { 127 if (cm.getCompilerType() == CompiledMethod.TRAP) { 128 VM.sysWriteln("<Hardware Trap Frame>"); 129 } else { 130 RVMMethod m = cm.getMethod(); 131 VM.sysWrite(m); 132 if (m.getDeclaringClass().isInBootImage()) { 133 VM.sysWrite("\tBOOT"); 134 } 135 } 136 VM.sysWriteln(); 137 } 138 } 139 RVMThread.dumpLock.unlock(); 140 } 141 142 /** 143 * @return the total number of samples taken 144 */ 145 public double getTotalNumberOfSamples() { 146 return totalCountsTaken; 147 } 148 149 /** 150 * Reset (clear) the method counts 151 */ 152 @Override 153 public synchronized void reset() { 154 initialize(); 155 } 156 157 /** 158 * @param cmid compiled method id 159 * @return the current count for a given compiled method id. 160 */ 161 public synchronized double getData(int cmid) { 162 int index = findHeapIdx(cmid); 163 if (index > 0) { 164 return counts[index]; 165 } else { 166 return 0.0; 167 } 168 } 169 170 /** 171 * Reset (set to 0.0) the count for a given compiled method id. 172 * 173 * @param cmid compiled method id 174 */ 175 public synchronized void reset(int cmid) { 176 int index = findHeapIdx(cmid); 177 if (index > 0) { 178 // Cmid does have a value in the heap. 179 // (1) clear map[cmid]. 180 // (2) shrink the heap by one slot. 181 // (a) If index is the last element in the heap we have nothing 182 // to do after we decrement nextIndex. 183 // (b) If index is not the last element in the heap, then move the 184 // last heap element to index and heapify. 185 map[cmid] = 0; 186 nextIndex--; 187 if (index < nextIndex) { 188 double oldValue = counts[index]; 189 counts[index] = counts[nextIndex]; 190 cmids[index] = cmids[nextIndex]; 191 map[cmids[index]] = index; 192 if (counts[index] > oldValue) { 193 heapifyUp(index); 194 } else { 195 heapifyDown(index); 196 } 197 } 198 } 199 if (DEBUG) validityCheck(); 200 } 201 202 /** 203 * Augment the data associated with a given cmid by the specified number of samples 204 * 205 * @param cmid compiled method id 206 * @param addVal samples to add 207 */ 208 public synchronized void augmentData(int cmid, double addVal) { 209 if (addVal == 0) return; // nothing to do 210 int index = findOrCreateHeapIdx(cmid); 211 counts[index] += addVal; 212 if (addVal > 0) { 213 heapifyUp(index); 214 } else { 215 heapifyDown(index); 216 } 217 if (DEBUG) validityCheck(); 218 } 219 220 /** 221 * Enqueue events describing the "hot" methods on the organizer's event queue. 222 * 223 * @param filterOptLevel filter out all methods already compiled at 224 * this opt level (or higher) 225 * @param threshold hotness value above which the method is considered 226 * to be hot. (0.0 to 1.0) 227 */ 228 public synchronized void insertHotMethods(int filterOptLevel, double threshold) { 229 if (DEBUG) validityCheck(); 230 insertHotMethodsInternal(1, filterOptLevel, hotnessToCounts(threshold)); 231 } 232 233 /** 234 * Collect the hot methods that have been compiled at the given opt level. 235 * 236 * @param optLevel target opt level 237 * @param threshold hotness value above which the method is considered to 238 * be hot. (0.0 to 1.0) 239 * @return a MethodCountSet containing an 240 * array of compiled methods and an array of their counts. 241 */ 242 public synchronized MethodCountSet collectHotMethods(int optLevel, double threshold) { 243 if (DEBUG) validityCheck(); 244 ArrayList<HotMethodRecompilationEvent> collect = new ArrayList<HotMethodRecompilationEvent>(); 245 collectHotOptMethodsInternal(1, collect, hotnessToCounts(threshold), optLevel); 246 247 // now package the data into the form the caller expects. 248 int numHotMethods = collect.size(); 249 double[] numCounts = new double[numHotMethods]; 250 CompiledMethod[] hotMethods = new CompiledMethod[numHotMethods]; 251 for (int i = 0; i < numHotMethods; i++) { 252 HotMethodEvent event = collect.get(i); 253 hotMethods[i] = event.getCompiledMethod(); 254 numCounts[i] = event.getNumSamples(); 255 } 256 return new MethodCountSet(hotMethods, numCounts); 257 } 258 259 /** 260 * Convert from a [0.0...1.0] hotness value to the number of counts 261 * that represents that fraction of hotness 262 * 263 * @param hotness a value [0.0...1.0] 264 * @return a number of counts 265 */ 266 private double hotnessToCounts(double hotness) { 267 return totalCountsTaken * hotness; 268 } 269 270 /** 271 * Convert a value to a [0.0...1.0] fractional hotness value 272 * 273 * @param numCounts number of counts 274 * @return a value [0.0...1.0] 275 */ 276 private double countsToHotness(double numCounts) { 277 if (VM.VerifyAssertions) VM._assert(numCounts <= totalCountsTaken); 278 return numCounts / totalCountsTaken; 279 } 280 281 /** 282 * Recursive implementation of insertHotMethods. Exploit heap property. 283 * Note threshold has been converted into a count value by my caller! 284 * 285 * @param index count array index 286 * @param filterOptLevel filter out all methods already compiled at 287 * this opt level (or higher) 288 * @param threshold hotness value above which the method is considered 289 * to be hot. (0.0 to 1.0) 290 */ 291 private void insertHotMethodsInternal(int index, int filterOptLevel, double threshold) { 292 if (index < nextIndex) { 293 if (counts[index] > threshold) { 294 int cmid = cmids[index]; 295 CompiledMethod cm = CompiledMethods.getCompiledMethod(cmid); 296 if (cm == null) { // obsolete and deleted 297 reset(cmid); // free up this slot 298 // Visit new one in the slot 299 insertHotMethodsInternal(index, filterOptLevel, threshold); 300 } else { 301 int compilerType = cm.getCompilerType(); 302 // Enqueue it unless it's either a trap method or already 303 // opt compiled at filterOptLevel or higher. 304 if (!(compilerType == CompiledMethod.TRAP || 305 (compilerType == CompiledMethod.OPT && 306 (((OptCompiledMethod) cm).getOptLevel() >= filterOptLevel)))) { 307 double ns = counts[index]; 308 HotMethodRecompilationEvent event = new HotMethodRecompilationEvent(cm, ns); 309 Controller.controllerInputQueue.insert(ns, event); 310 AOSLogging.logger.controllerNotifiedForHotness(cm, ns); 311 } 312 313 // Since I was hot enough, also consider my children. 314 insertHotMethodsInternal(index * 2, filterOptLevel, threshold); 315 insertHotMethodsInternal(index * 2 + 1, filterOptLevel, threshold); 316 } 317 } 318 } 319 } 320 321 /** 322 * Recursive implementation of collectHotOptNMethods. 323 * Exploit heap property. 324 * Constraint: threshold has been converted into a count value by my caller! 325 * 326 * @param index count array index 327 * @param collect vector used to collect output. 328 * @param threshold hotness value above which the method is considered 329 * to be hot. (0.0 to 1.0) 330 * @param optLevel target opt level to look for. 331 */ 332 private void collectHotOptMethodsInternal(int index, List<HotMethodRecompilationEvent> collect, double threshold, 333 int optLevel) { 334 if (index < nextIndex) { 335 if (counts[index] > threshold) { 336 int cmid = cmids[index]; 337 CompiledMethod cm = CompiledMethods.getCompiledMethod(cmid); 338 if (cm == null) { // obsolete and deleted 339 reset(cmid); // free up this slot 340 // Visit new one in the slot 341 collectHotOptMethodsInternal(index, collect, threshold, optLevel); 342 } else { 343 int compilerType = cm.getCompilerType(); 344 if (compilerType == CompiledMethod.OPT && ((OptCompiledMethod) cm).getOptLevel() == optLevel) { 345 double ns = counts[index]; 346 collect.add(new HotMethodRecompilationEvent(cm, ns)); 347 } 348 349 // Since I was hot enough, also consider my children. 350 collectHotOptMethodsInternal(index * 2, collect, threshold, optLevel); 351 collectHotOptMethodsInternal(index * 2 + 1, collect, threshold, optLevel); 352 } 353 } 354 } 355 } 356 357 /** 358 * Either find the index that is already being used to hold the counts 359 * for cmid or allocate a new entry in the heap for cmid. 360 * 361 * @param cmid compiled method id 362 * @return count array index 363 */ 364 private int findOrCreateHeapIdx(int cmid) { 365 if (cmid >= map.length) { 366 growHeapMap(cmid); 367 } 368 int index = map[cmid]; 369 if (index == 0) { 370 // A new cmid. Allocate a heap entry for it. 371 index = nextIndex++; 372 if (index >= counts.length) { 373 growHeap(); 374 } 375 counts[index] = 0.0; 376 cmids[index] = cmid; 377 map[cmid] = index; 378 } 379 return index; 380 } 381 382 /** 383 * Finds the index that is already being used to hold the counts for cmid. 384 * 385 * @param cmid compiled method id 386 * @return 0 if no index exists, the index otherwise 387 */ 388 private int findHeapIdx(int cmid) { 389 if (cmid < map.length) { 390 int index = map[cmid]; 391 return index; 392 } else { 393 return 0; 394 } 395 } 396 397 /** 398 * Grow the map to be at least as large as would be required to map cmid 399 * 400 * @param cmid compiled method id 401 */ 402 private void growHeapMap(int cmid) { 403 int[] newMap = new int[Math.max((int) (map.length * 1.25), cmid + 1)]; 404 for (int j = 0; j < map.length; j++) { 405 newMap[j] = map[j]; 406 } 407 map = newMap; 408 } 409 410 /** 411 * Increase the size of the count's backing arrays 412 */ 413 private void growHeap() { 414 double[] tmp1 = new double[counts.length * 2]; 415 for (int i = 1; i < counts.length; i++) { 416 tmp1[i] = counts[i]; 417 } 418 counts = tmp1; 419 int[] tmp2 = new int[cmids.length * 2]; 420 for (int i = 1; i < cmids.length; i++) { 421 tmp2[i] = cmids[i]; 422 } 423 cmids = tmp2; 424 } 425 426 /** 427 * Restore the heap property after increasing a count array entry's value 428 * 429 * @param index of count array entry 430 */ 431 private void heapifyUp(int index) { 432 int current = index; 433 int parent = index / 2; 434 while (parent > 0 && counts[parent] < counts[current]) { 435 swap(parent, current); 436 current = parent; 437 parent = parent / 2; 438 } 439 } 440 441 /** 442 * Restore the heap property after decreasing a count array entry's value 443 * 444 * @param index of count array entry 445 */ 446 private void heapifyDown(int index) { 447 int current = index; 448 int child1 = current * 2; 449 while (child1 < nextIndex) { 450 int child2 = current * 2 + 1; 451 int larger = (child2 < nextIndex && counts[child2] > counts[child1]) ? child2 : child1; 452 if (counts[current] >= counts[larger]) break; // done 453 swap(current, larger); 454 current = larger; 455 child1 = current * 2; 456 } 457 } 458 459 /** 460 * Swap the heap entries at i and j. 461 * 462 * @param i count array index 463 * @param j count array index 464 */ 465 private void swap(int i, int j) { 466 double tmpS = counts[i]; 467 counts[i] = counts[j]; 468 counts[j] = tmpS; 469 int tmpC = cmids[i]; 470 cmids[i] = cmids[j]; 471 cmids[j] = tmpC; 472 map[cmids[i]] = i; 473 map[cmids[j]] = j; 474 } 475 476 /** 477 * Validate that internal fields are consistent. 478 * This is very expensive. Only use for debugging purposes. 479 */ 480 private void validityCheck() { 481 if (DEBUG && VM.VerifyAssertions) { 482 // (1) Verify map and cmids are in synch 483 for (int i = 0; i < map.length; i++) { 484 VM._assert(map[i] == 0 || cmids[map[i]] == i); 485 } 486 for (int i = 1; i < nextIndex; i++) { 487 VM._assert(map[cmids[i]] == i); 488 } 489 490 // Verify that heap property holds on data. 491 for (int i = 2; i < nextIndex; i++) { 492 VM._assert(counts[i] <= counts[i / 2]); 493 } 494 } 495 } 496} 497