[477] | 1 | package test.stress; |
---|
| 2 | |
---|
| 3 | import java.io.BufferedReader; |
---|
| 4 | import java.io.File; |
---|
| 5 | import java.io.FileInputStream; |
---|
| 6 | import java.io.FileOutputStream; |
---|
| 7 | import java.io.IOException; |
---|
| 8 | import java.io.InputStreamReader; |
---|
| 9 | import java.lang.management.MemoryMXBean; |
---|
| 10 | import java.util.ArrayList; |
---|
| 11 | import java.util.Collections; |
---|
| 12 | import java.util.List; |
---|
| 13 | import java.util.Properties; |
---|
| 14 | import java.awt.Toolkit; |
---|
| 15 | |
---|
| 16 | import org.apache.commons.logging.Log; |
---|
| 17 | import org.apache.commons.logging.LogFactory; |
---|
| 18 | |
---|
| 19 | import simulator.DataCenterWorkloadSimulator; |
---|
| 20 | |
---|
| 21 | /** |
---|
| 22 | * Class responsible for performin memory and time analysis of experiments |
---|
| 23 | * execution. |
---|
| 24 | * |
---|
| 25 | * @author Jarek Szymczak |
---|
| 26 | * @version $Id$ |
---|
| 27 | */ |
---|
| 28 | public class MemoryAndTimeAnalyzer { |
---|
| 29 | |
---|
| 30 | /** The class logger. */ |
---|
| 31 | private static final Log LOGGER = |
---|
| 32 | LogFactory.getLog(MemoryAndTimeAnalyzer.class); |
---|
| 33 | |
---|
| 34 | /** The properties. */ |
---|
| 35 | private static Properties properties = new Properties(); |
---|
| 36 | |
---|
| 37 | /** |
---|
| 38 | * Variables determinig what type of memories should be meassured and should |
---|
| 39 | * it be performed in detailed or simple way. |
---|
| 40 | */ |
---|
| 41 | private static boolean heap, nonHeap, jvm, heapDet, nonHeapDet, jvmDet; |
---|
| 42 | |
---|
| 43 | /** |
---|
| 44 | * Determines if the time is meassured precisly (it's not disturb with |
---|
| 45 | * memory meassuring). |
---|
| 46 | */ |
---|
| 47 | private static boolean timePrec; |
---|
| 48 | |
---|
| 49 | /** Memory details output stream. */ |
---|
| 50 | private static FileOutputStream outDetails; |
---|
| 51 | |
---|
| 52 | /** The current number of iterations - used for progress calculation. */ |
---|
| 53 | private static int currentNumberOfIterations; |
---|
| 54 | |
---|
| 55 | /** The total number of iterations - used for progress calculation. */ |
---|
| 56 | private static int totalNumberOfIterations; |
---|
| 57 | |
---|
| 58 | /** Number of repeats of each experiments. */ |
---|
| 59 | private static int repeats; |
---|
| 60 | |
---|
| 61 | /** |
---|
| 62 | * It stores information about amount of system memory in a certain machine |
---|
| 63 | * (in megabytes). |
---|
| 64 | */ |
---|
| 65 | private static int memoryInMB; |
---|
| 66 | |
---|
| 67 | /** The interval between memory checks (in miliseconds). */ |
---|
| 68 | private static int interval; |
---|
| 69 | |
---|
| 70 | /** |
---|
| 71 | * The object used for gathering the information about heap and non-heap |
---|
| 72 | * memory. |
---|
| 73 | */ |
---|
| 74 | private static MemoryMXBean memoryMXBean = |
---|
| 75 | java.lang.management.ManagementFactory.getMemoryMXBean(); |
---|
| 76 | |
---|
| 77 | /** |
---|
| 78 | * The GSSIM object which is analyzed with certain experiment (for each |
---|
| 79 | * experiment new object is created, and the old one is dereferenced and |
---|
| 80 | * deleted (probably). |
---|
| 81 | */ |
---|
| 82 | private static DataCenterWorkloadSimulator gssim = |
---|
| 83 | new DataCenterWorkloadSimulator(); |
---|
| 84 | |
---|
| 85 | /** |
---|
| 86 | * Variables helpful during time and memory consumption analysis (they're |
---|
| 87 | * not local to simplify working with the thread responsible for memory |
---|
| 88 | * analysis and to write the details to outDetails output stream. |
---|
| 89 | */ |
---|
| 90 | private static long timestart, hmemstart, hmemmax, nmemmax, jvmmemmax, |
---|
| 91 | timend; |
---|
| 92 | |
---|
| 93 | /** |
---|
| 94 | * Performs memory and time analysis of a single file. |
---|
| 95 | * |
---|
| 96 | * @param filename |
---|
| 97 | * experiment file path |
---|
| 98 | * @param iteration |
---|
| 99 | * iteration number |
---|
| 100 | * @return array with results: time, heap memory, non-heap memory and JVM |
---|
| 101 | * memory |
---|
| 102 | * @throws InterruptedException |
---|
| 103 | * the interrupted exception when thread was interrupted during |
---|
| 104 | * sleep |
---|
| 105 | */ |
---|
| 106 | private static double[] singleTask(String filename, int iteration) |
---|
| 107 | throws InterruptedException { |
---|
| 108 | double[] result = new double[4]; |
---|
| 109 | |
---|
| 110 | Thread t = new Thread() { |
---|
| 111 | public void run() { |
---|
| 112 | long htemp, ntemp, jvmtemp; |
---|
| 113 | hmemmax = hmemstart; |
---|
| 114 | nmemmax = 0; |
---|
| 115 | jvmmemmax = 0; |
---|
| 116 | try { |
---|
| 117 | while (!interrupted()) { |
---|
| 118 | |
---|
| 119 | sleep(interval); |
---|
| 120 | |
---|
| 121 | htemp = |
---|
| 122 | heap ? memoryMXBean.getHeapMemoryUsage() |
---|
| 123 | .getUsed() : 0; |
---|
| 124 | ntemp = |
---|
| 125 | nonHeap ? memoryMXBean.getNonHeapMemoryUsage() |
---|
| 126 | .getUsed() : 0; |
---|
| 127 | jvmtemp = jvm ? getJVMMemory() : 0; |
---|
| 128 | |
---|
| 129 | if (htemp > hmemmax) |
---|
| 130 | hmemmax = htemp; |
---|
| 131 | if (ntemp > nmemmax) |
---|
| 132 | nmemmax = ntemp; |
---|
| 133 | if (htemp > jvmmemmax) |
---|
| 134 | jvmmemmax = jvmtemp; |
---|
| 135 | |
---|
| 136 | if (heapDet) |
---|
| 137 | outDetails.write(String.format("%.3f\t", |
---|
| 138 | (hmemmax - hmemstart) / (1024.0 * 1024.0)) |
---|
| 139 | .getBytes("UTF-8")); |
---|
| 140 | |
---|
| 141 | if (nonHeapDet) |
---|
| 142 | outDetails.write(String.format("%.3f\t", |
---|
| 143 | nmemmax / (1024.0 * 1024.0)).getBytes( |
---|
| 144 | "UTF-8")); |
---|
| 145 | |
---|
| 146 | if (jvmDet) |
---|
| 147 | outDetails.write(String.format("%.3f\t", |
---|
| 148 | jvmmemmax / (1024.0 * 1024.0)).getBytes( |
---|
| 149 | "UTF-8")); |
---|
| 150 | |
---|
| 151 | if (heapDet || nonHeapDet || jvmDet) |
---|
| 152 | outDetails.write("\n".getBytes("UTF-8")); |
---|
| 153 | } |
---|
| 154 | } catch (InterruptedException e) { |
---|
| 155 | return; |
---|
| 156 | } catch (IOException e) { |
---|
| 157 | // if this happens it's very bad,,, but it's totally |
---|
| 158 | // unexpected - only if there's a problem with closing |
---|
| 159 | // stream from ps process |
---|
| 160 | throw new RuntimeException(e); |
---|
| 161 | } |
---|
| 162 | } |
---|
| 163 | }; |
---|
| 164 | |
---|
| 165 | try { |
---|
| 166 | if (heapDet || nonHeapDet || jvmDet) |
---|
| 167 | outDetails.write(("\n\n" + filename + " (iteration: " |
---|
| 168 | + Integer.toString(iteration) + ") \n\n") |
---|
| 169 | .getBytes("UTF-8")); |
---|
| 170 | if (heapDet) |
---|
| 171 | outDetails.write("Hmem\t".getBytes("UTF-8")); |
---|
| 172 | if (nonHeapDet) |
---|
| 173 | outDetails.write("Nmem\t".getBytes("UTF-8")); |
---|
| 174 | if (jvmDet) |
---|
| 175 | outDetails.write("JVMmem\t".getBytes("UTF-8")); |
---|
| 176 | if (heapDet || nonHeapDet || jvmDet) |
---|
| 177 | outDetails.write("\n".getBytes("UTF-8")); |
---|
| 178 | } catch (IOException e) { |
---|
| 179 | // if this happens it's very bad,,, but it's totally unexpected |
---|
| 180 | throw new RuntimeException(e); |
---|
| 181 | } |
---|
| 182 | |
---|
| 183 | LOGGER.info("Current progress in number of experiments performed: " |
---|
| 184 | + Integer.toString(currentNumberOfIterations) |
---|
| 185 | + " / " |
---|
| 186 | + Integer.toString(totalNumberOfIterations) |
---|
| 187 | + " (" |
---|
| 188 | + Integer.toString((currentNumberOfIterations) * 100 |
---|
| 189 | / totalNumberOfIterations) + "%)"); |
---|
| 190 | |
---|
| 191 | currentNumberOfIterations++; |
---|
| 192 | |
---|
| 193 | gssim = null; |
---|
| 194 | |
---|
| 195 | for (int i = 0; i < 5; i++) { |
---|
| 196 | Thread.sleep(600); |
---|
| 197 | System.gc(); |
---|
| 198 | } |
---|
| 199 | |
---|
| 200 | hmemstart = memoryMXBean.getHeapMemoryUsage().getUsed(); |
---|
| 201 | gssim = new DataCenterWorkloadSimulator(); |
---|
| 202 | |
---|
| 203 | if (heap || nonHeap || jvm) |
---|
| 204 | t.start(); |
---|
| 205 | |
---|
| 206 | timestart = System.currentTimeMillis(); |
---|
| 207 | gssim.run(new String[] { filename }); |
---|
| 208 | timend = System.currentTimeMillis(); |
---|
| 209 | |
---|
| 210 | if (heap || nonHeap || jvm) { |
---|
| 211 | t.interrupt(); |
---|
| 212 | t.join(); |
---|
| 213 | |
---|
| 214 | if (timePrec) { |
---|
| 215 | gssim = null; |
---|
| 216 | for (int i = 0; i < 5; i++) |
---|
| 217 | System.gc(); |
---|
| 218 | gssim = new DataCenterWorkloadSimulator(); |
---|
| 219 | |
---|
| 220 | timestart = System.currentTimeMillis(); |
---|
| 221 | gssim.run(new String[] { filename }); |
---|
| 222 | timend = System.currentTimeMillis(); |
---|
| 223 | } |
---|
| 224 | } |
---|
| 225 | |
---|
| 226 | result[0] = (timend - timestart) / 1000.0; |
---|
| 227 | result[1] = (hmemmax - hmemstart) / (1024.0 * 1024.0); |
---|
| 228 | result[2] = nmemmax / (1024.0 * 1024.0); |
---|
| 229 | result[3] = jvmmemmax / (1024.0 * 1024.0); |
---|
| 230 | |
---|
| 231 | return result; |
---|
| 232 | } |
---|
| 233 | |
---|
| 234 | /** |
---|
| 235 | * Repeats task number specified in variable "repeats" times |
---|
| 236 | * |
---|
| 237 | * @param filename |
---|
| 238 | * experiment filename |
---|
| 239 | * @return the formatted line with results (content depends on |
---|
| 240 | * configuration) |
---|
| 241 | * @throws InterruptedException |
---|
| 242 | * thrown when thread is interrupted during sleep |
---|
| 243 | */ |
---|
| 244 | private static String repeatTask(String filename) |
---|
| 245 | throws InterruptedException { |
---|
| 246 | |
---|
| 247 | double[] temp; |
---|
| 248 | |
---|
| 249 | double tavg = 0; |
---|
| 250 | double tstdev = 0; |
---|
| 251 | double hmavg = 0; |
---|
| 252 | double hmstdev = 0; |
---|
| 253 | double nmstdev = 0; |
---|
| 254 | double nmavg = 0; |
---|
| 255 | double jvmmavg = 0; |
---|
| 256 | double jvmmstdev = 0; |
---|
| 257 | |
---|
| 258 | for (int i = 0; i < repeats; i++) { |
---|
| 259 | temp = singleTask(filename, i); |
---|
| 260 | tavg += temp[0]; |
---|
| 261 | tstdev += temp[0] * temp[0]; |
---|
| 262 | hmavg += temp[1]; |
---|
| 263 | hmstdev += temp[1] * temp[1]; |
---|
| 264 | nmavg += temp[2]; |
---|
| 265 | nmstdev += temp[2] * temp[2]; |
---|
| 266 | jvmmavg += temp[3]; |
---|
| 267 | jvmmstdev += temp[3] * temp[3]; |
---|
| 268 | } |
---|
| 269 | |
---|
| 270 | tavg /= repeats; |
---|
| 271 | tstdev /= repeats; |
---|
| 272 | hmavg /= repeats; |
---|
| 273 | hmstdev /= repeats; |
---|
| 274 | nmavg /= repeats; |
---|
| 275 | nmstdev /= repeats; |
---|
| 276 | jvmmavg /= repeats; |
---|
| 277 | jvmmstdev /= repeats; |
---|
| 278 | |
---|
| 279 | tstdev = Math.sqrt(tstdev - tavg * tavg); |
---|
| 280 | hmstdev = Math.sqrt(hmstdev - hmavg * hmavg); |
---|
| 281 | nmstdev = Math.sqrt(nmstdev - nmavg * nmavg); |
---|
| 282 | jvmmstdev = Math.sqrt(jvmmstdev - jvmmavg * jvmmavg); |
---|
| 283 | |
---|
| 284 | StringBuilder result = |
---|
| 285 | new StringBuilder(String.format("%.3f\t%.3f\t", tavg, tstdev)); |
---|
| 286 | |
---|
| 287 | if (heap) |
---|
| 288 | result.append(String.format("%.3f\t%.3f\t", hmavg, hmstdev)); |
---|
| 289 | if (nonHeap) |
---|
| 290 | result.append(String.format("%.3f\t%.3f\t", nmavg, nmstdev)); |
---|
| 291 | if (jvm) |
---|
| 292 | result.append(String.format("%.3f\t%.3f\t", jvmmavg, jvmmstdev)); |
---|
| 293 | |
---|
| 294 | return result.toString(); |
---|
| 295 | } |
---|
| 296 | |
---|
| 297 | /** |
---|
| 298 | * Gets the JVM memory. It works only under linux and only if the process |
---|
| 299 | * name begins with "java" (so, for instance, it doesn't work when the |
---|
| 300 | * application is run under Eclipse SDK). It depends on {@link #memoryInMB} |
---|
| 301 | * variable |
---|
| 302 | * |
---|
| 303 | * @return amount of memory used by JVM process (in bytes) |
---|
| 304 | * @throws IOException |
---|
| 305 | * Signals that an I/O exception has occurred. |
---|
| 306 | */ |
---|
| 307 | public static Long getJVMMemory() throws IOException { |
---|
| 308 | BufferedReader reader = null; |
---|
| 309 | try { |
---|
| 310 | Process proc = |
---|
| 311 | Runtime |
---|
| 312 | .getRuntime() |
---|
| 313 | .exec( |
---|
| 314 | new String[] { "bash", "-c", |
---|
| 315 | "ps u --width=5 | grep java | tr -s ' ' | cut -d ' ' -f 4" }); |
---|
| 316 | reader = |
---|
| 317 | new BufferedReader(new InputStreamReader(proc |
---|
| 318 | .getInputStream())); |
---|
| 319 | |
---|
| 320 | String line; |
---|
| 321 | if (null != (line = reader.readLine())) { |
---|
| 322 | return Math.round(Double.parseDouble(line) / 100.0 * memoryInMB |
---|
| 323 | * 1024.0 * 1024.0); |
---|
| 324 | } |
---|
| 325 | return null; |
---|
| 326 | } catch (IOException e) { |
---|
| 327 | System.out.println(e.getMessage()); |
---|
| 328 | return null; |
---|
| 329 | } finally { |
---|
| 330 | if (reader != null) |
---|
| 331 | reader.close(); |
---|
| 332 | } |
---|
| 333 | |
---|
| 334 | } |
---|
| 335 | |
---|
| 336 | /** |
---|
| 337 | * The main method. It reads the properties of analysis from file |
---|
| 338 | * "properties/performance.properties" - this path is relative so it's |
---|
| 339 | * important from which folder the application is run. Meaning of each |
---|
| 340 | * property is described in the .properties file, please do not change it's |
---|
| 341 | * content other than parameters' values |
---|
| 342 | * |
---|
| 343 | * @param args |
---|
| 344 | * the arguments of main methods are relative paths to |
---|
| 345 | * .properties files with experiments or directories containing |
---|
| 346 | * these files (it's not recursive) |
---|
| 347 | * @throws IOException |
---|
| 348 | * Signals that an I/O exception has occurred. |
---|
| 349 | * @throws InterruptedException |
---|
| 350 | * fall-through exception which is not possible to occur |
---|
| 351 | */ |
---|
| 352 | public static void main(String[] args) throws IOException, |
---|
| 353 | InterruptedException { |
---|
| 354 | |
---|
| 355 | LOGGER.info("Reading the properties"); |
---|
| 356 | |
---|
| 357 | properties.load(new FileInputStream(new File( |
---|
| 358 | "properties/performance.properties"))); |
---|
| 359 | |
---|
| 360 | int noOfBeeps = |
---|
| 361 | Integer.parseInt(properties.getProperty("NO_OF_BEEPS", "0")); |
---|
| 362 | |
---|
| 363 | interval = Integer.parseInt(properties.getProperty("INTERVAL")); |
---|
| 364 | repeats = Integer.parseInt(properties.getProperty("REPEATS")); |
---|
| 365 | memoryInMB = Integer.parseInt(properties.getProperty("MEMORY_IN_MB")); |
---|
| 366 | |
---|
| 367 | heap = |
---|
| 368 | "simple".equals(properties.getProperty("HEAP_MEMORY")) |
---|
| 369 | || "details".equals(properties |
---|
| 370 | .getProperty("HEAP_MEMORY")); |
---|
| 371 | heapDet = "details".equals(properties.getProperty("HEAP_MEMORY")); |
---|
| 372 | nonHeap = |
---|
| 373 | "simple".equals(properties.getProperty("NON_HEAP_MEMORY")) |
---|
| 374 | || "details".equals(properties |
---|
| 375 | .getProperty("NON_HEAP_MEMORY")); |
---|
| 376 | nonHeapDet = |
---|
| 377 | "details".equals(properties.getProperty("NON_HEAP_MEMORY")); |
---|
| 378 | jvm = |
---|
| 379 | "simple".equals(properties.getProperty("JVM_MEMORY")) |
---|
| 380 | || "details".equals(properties |
---|
| 381 | .getProperty("JVM_MEMORY")); |
---|
| 382 | jvmDet = "details".equals(properties.getProperty("JVM_MEMORY")); |
---|
| 383 | |
---|
| 384 | timePrec = (!jvm && !heap && !nonHeap); |
---|
| 385 | |
---|
| 386 | if ("precisly".equals(properties.getProperty("TIME_MEASURING"))) |
---|
| 387 | timePrec = true; |
---|
| 388 | |
---|
| 389 | LOGGER.info("Preparing file list to perform experiment"); |
---|
| 390 | |
---|
| 391 | List<String> records = new ArrayList<String>(); |
---|
| 392 | |
---|
| 393 | File temp; |
---|
| 394 | |
---|
| 395 | if (args.length < 1) |
---|
| 396 | return; |
---|
| 397 | |
---|
| 398 | for (int i = 0; i < args.length; i++) { |
---|
| 399 | if ((new File(args[i])).exists()) |
---|
| 400 | if (args[i].endsWith(".properties")) |
---|
| 401 | records.add(args[i]); |
---|
| 402 | else { |
---|
| 403 | temp = new File(args[i]); |
---|
| 404 | String[] files = temp.list(); |
---|
| 405 | if (files != null) { |
---|
| 406 | for (String file : files) { |
---|
| 407 | if ((new File(args[i] + "/" + file).exists() && file |
---|
| 408 | .endsWith(".properties"))) |
---|
| 409 | records.add(args[i] + "/" + file); |
---|
| 410 | } |
---|
| 411 | } |
---|
| 412 | } |
---|
| 413 | } |
---|
| 414 | |
---|
| 415 | totalNumberOfIterations = repeats * records.size(); |
---|
| 416 | |
---|
| 417 | if (records.size() == 0) |
---|
| 418 | return; |
---|
| 419 | |
---|
| 420 | int counter = 0; |
---|
| 421 | File file = null; |
---|
| 422 | File fileDetails = null; |
---|
| 423 | |
---|
| 424 | do { |
---|
| 425 | counter++; |
---|
| 426 | file = |
---|
| 427 | new File("performance_result_" + Integer.toString(counter) |
---|
| 428 | + ".txt"); |
---|
| 429 | fileDetails = |
---|
| 430 | new File("performance_result_" + Integer.toString(counter) |
---|
| 431 | + "_details.txt"); |
---|
| 432 | } while (file.exists() || fileDetails.exists()); |
---|
| 433 | |
---|
| 434 | FileOutputStream out = new FileOutputStream(file); |
---|
| 435 | |
---|
| 436 | if (heapDet || nonHeapDet || jvmDet) |
---|
| 437 | outDetails = new FileOutputStream(fileDetails); |
---|
| 438 | |
---|
| 439 | StringBuilder header = new StringBuilder("Measuring time precisly: "); |
---|
| 440 | |
---|
| 441 | if (timePrec) |
---|
| 442 | header.append("yes\n"); |
---|
| 443 | else |
---|
| 444 | header.append("no\n"); |
---|
| 445 | |
---|
| 446 | header.append(String.format("Number of repets: %d\n", repeats)); |
---|
| 447 | header.append(String.format( |
---|
| 448 | "Interval between memory checking: %d ms\n", interval)); |
---|
| 449 | header.append(String.format("System memory: %d MB\n\n", memoryInMB)); |
---|
| 450 | |
---|
| 451 | header.append("Name\tTavg\tTstdev\t"); |
---|
| 452 | |
---|
| 453 | if (heap) |
---|
| 454 | header.append("HMavg\tHMstdev\t"); |
---|
| 455 | if (nonHeap) |
---|
| 456 | header.append("NMavg\tNMstdev\t"); |
---|
| 457 | if (jvm) |
---|
| 458 | header.append("JVMMavg\tJVMMstdev\t"); |
---|
| 459 | header.append('\n'); |
---|
| 460 | |
---|
| 461 | LOGGER.info("Measuring of performance begun"); |
---|
| 462 | |
---|
| 463 | try { |
---|
| 464 | |
---|
| 465 | out.write(header.toString().getBytes("UTF-8")); |
---|
| 466 | if (heapDet || nonHeapDet || jvmDet) |
---|
| 467 | outDetails |
---|
| 468 | .write(("Detailed analysis of memory usage with interval: " |
---|
| 469 | + interval + "ms\n\n").getBytes("UTF-8")); |
---|
| 470 | Collections.sort(records); |
---|
| 471 | for (String record : records) { |
---|
| 472 | out.write((record + "\t" + repeatTask(record) + "\n") |
---|
| 473 | .getBytes("UTF-8")); |
---|
| 474 | } |
---|
| 475 | } finally { |
---|
| 476 | if (out != null) |
---|
| 477 | out.close(); |
---|
| 478 | if (outDetails != null) |
---|
| 479 | outDetails.close(); |
---|
| 480 | } |
---|
| 481 | |
---|
| 482 | LOGGER.info("Measuring of performance ended succesfully"); |
---|
| 483 | |
---|
| 484 | for (int i = 0; i < noOfBeeps; i++) { |
---|
| 485 | Toolkit.getDefaultToolkit().beep(); |
---|
| 486 | Thread.sleep(1000); |
---|
| 487 | } |
---|
| 488 | } |
---|
| 489 | |
---|
| 490 | } |
---|