1 package org.prevayler.demos.scalability; 2 3 import java.util.*; 4 import java.text.DecimalFormat ; 5 import org.prevayler.foundation.*; 6 7 9 abstract class ScalabilityTestRun { 10 11 static private final long ROUND_DURATION_MILLIS = 1000 * 20; 12 13 private final ScalabilityTestSubject subject; 14 protected final int numberOfObjects; 15 16 private double bestRoundOperationsPerSecond; 17 private int bestRoundThreads; 18 19 private final List connectionCache = new LinkedList(); 20 21 private long operationCount = 0; 22 private long lastOperation = 0; 23 private boolean isRoundFinished; 24 private int activeRoundThreads = 0; 25 26 27 29 public String getResult() { 30 return toResultString(bestRoundOperationsPerSecond, bestRoundThreads); 31 } 32 33 34 public double getOperationsPerSecond() { 35 return bestRoundOperationsPerSecond; 36 } 37 38 39 protected ScalabilityTestRun(ScalabilityTestSubject subject, int numberOfObjects, int minThreads, int maxThreads) { 40 if (minThreads > maxThreads) throw new IllegalArgumentException ("The minimum number of threads cannot be greater than the maximum number."); 41 if (minThreads < 1) throw new IllegalArgumentException ("The minimum number of threads cannot be smaller than one."); 42 43 this.subject = subject; 44 this.numberOfObjects = numberOfObjects; 45 46 out("\n\n========= Running " + name() + " (" + (maxThreads - minThreads + 1) + " rounds). Subject: " + subject.name() + "..."); 47 prepare(); 48 49 out("Each round will take approx. " + ROUND_DURATION_MILLIS / 1000 + " seconds to run..."); 50 performTest(minThreads, maxThreads); 51 out("\n----------- BEST ROUND: " + getResult()); 52 } 53 54 protected void prepare() { 55 subject.replaceAllRecords(numberOfObjects); 56 System.gc(); 57 } 58 59 60 62 protected abstract String name(); 63 64 65 private void performTest(int minThreads, int maxThreads) { 66 67 int threads = minThreads; 68 while (threads <= maxThreads) { 69 double operationsPerSecond = performRound(threads); 70 71 if (operationsPerSecond > bestRoundOperationsPerSecond) { 72 bestRoundOperationsPerSecond = operationsPerSecond; 73 bestRoundThreads = threads; 74 } 75 76 threads++; 77 } 78 } 79 80 81 83 private double performRound(int threads) { 84 long initialOperationCount = operationCount; 85 StopWatch stopWatch = StopWatch.start(); 86 87 startThreads(threads); 88 sleep(); 89 stopThreads(); 90 91 double secondsEllapsed = stopWatch.secondsEllapsed(); 92 double operationsPerSecond = (operationCount - initialOperationCount) / secondsEllapsed; 93 94 out("\nMemory used: " + Runtime.getRuntime().totalMemory()); 95 out("Seconds ellapsed: " + secondsEllapsed); 96 out("--------- Round Result: " + toResultString(operationsPerSecond, threads)); 97 98 return operationsPerSecond; 99 } 100 101 102 private void startThreads(int threads) { 103 isRoundFinished = false; 104 105 int i = 1; 106 while(i <= threads) { 107 startThread(lastOperation + i, threads); 108 i++; 109 } 110 } 111 112 113 private void startThread(final long startingOperation, final int operationIncrement) { 114 (new Thread () { 115 public void run() { 116 try { 117 Object connection = acquireConnection(); 118 119 long operation = startingOperation; 120 while (!isRoundFinished) { 121 executeOperation(connection, operation); 122 operation += operationIncrement; 123 } 124 125 synchronized (connectionCache) { 126 connectionCache.add(connection); 127 operationCount += (operation - startingOperation) / operationIncrement; 128 if (lastOperation < operation) lastOperation = operation; 129 activeRoundThreads--; 130 } 131 132 } catch (OutOfMemoryError err) { 133 outOfMemory(); 134 } 135 } 136 }).start(); 137 138 activeRoundThreads++; 139 } 140 141 142 protected abstract void executeOperation(Object connection, long operation); 143 144 145 private Object acquireConnection() { 146 synchronized (connectionCache) { 147 return connectionCache.isEmpty() 148 ? subject.createTestConnection() 149 : connectionCache.remove(0); 150 } 151 } 152 153 154 private void stopThreads() { 155 isRoundFinished = true; 156 while (activeRoundThreads != 0) { 157 Thread.yield(); 158 } 159 } 160 161 162 static private String toResultString(double operationsPerSecond, int threads) { 163 String operations = new DecimalFormat ("0.00").format(operationsPerSecond); 164 return "" + operations + " operations/second (" + threads + " threads)"; 165 } 166 167 static void outOfMemory() { 168 System.gc(); 169 out( 170 "\n\nOutOfMemoryError.\n" + 171 "===========================================================\n" + 172 "The VM must be started with a sufficient maximum heap size.\n" + 173 "Example for Linux and Windows: java -Xmx512000000 ...\n\n" 174 ); 175 } 176 177 static private void sleep() { 178 try { 179 Thread.sleep(ROUND_DURATION_MILLIS); 180 } catch (InterruptedException ix) { 181 throw new RuntimeException ("Unexpected InterruptedException."); 182 } 183 } 184 185 186 static private void out(Object obj) { 187 System.out.println(obj); 188 } 189 } 190 | Popular Tags |