package test.org.helios.apmrouter.performance; import org.helios.apmrouter.util.SystemClock; import org.helios.apmrouter.util.SystemClock.ElapsedTime; import org.junit.Ignore; import test.org.helios.apmrouter.metric.BaseTestCase; import java.lang.management.ManagementFactory; import java.lang.management.MemoryUsage; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; /** * <p>Title: BasePerformanceTestCase</p> * <p>Description: Base class for performance tests</p> * <p>Some useful JVM init options:<b><code>-server -XX:+PrintCompilation -XX:CompileThreshold=1000 -Xbatch -XX:CICompilerCount=1</code></b> * <p>Useful links:<ul> * <li><a href="https://wikis.oracle.com/display/HotSpotInternals/MicroBenchmarks">Oracle's Java MicroBenchmarks Wiki</a></li> * <li><a href="http://www.ibm.com/developerworks/java/library/j-jtp02225/index.html">Brian Goetz's Java MicroBenchmark article</a></li> * <li><a href="http://blog.joda.org/2011/08/printcompilation-jvm-flag.html">Blog explaining compilation printouts</a></li> * <li><a href="https://gist.github.com/1165804#file_notes.md">Gist with more info on compilation printouts</a></li> * </ul></p> * <p>Company: ICE Futures US</p> * @author Whitehead (nicholas.whitehead@theice.com) * @version $LastChangedRevision$ * <p><code>test.org.helios.apmrouter.performance.BasePerformanceTestCase</code></p> */ @Ignore public class BasePerformanceTestCase extends BaseTestCase { /** Indicates if this JVM supports current cpu time per thread */ public static final boolean CPU_TIME_SUPPORTED = ManagementFactory.getThreadMXBean().isCurrentThreadCpuTimeSupported(); /** Indicates if this JVM supports thread contention monitoring */ public static final boolean THREAD_CONTENTION_SUPPORTED = ManagementFactory.getThreadMXBean().isThreadContentionMonitoringSupported(); /** Numbers and letters */ protected static final char[] WORD_CHARS; /** The number of Numbers and letters */ protected static final int WORD_CHARS_LENGTH; /** Tets random instance */ protected static final Random RANDOM = new Random(System.nanoTime()); static { List<Character> chars = new ArrayList<Character>(); for(int i = 48; i < 122; i++) { if((i >= 58 && i <=64) || (i >= 91 && i <=96)) continue; chars.add((char)i); } WORD_CHARS = new char[chars.size()]; for(int i = 0; i < chars.size(); i++) { WORD_CHARS[i] = chars.get(i); } WORD_CHARS_LENGTH = WORD_CHARS.length; } protected static void log(Object obj) { System.out.println(obj); } protected static void loge(Object obj) { System.err.println(obj); } /** * Returns the next positive random int * @return the next positive random int */ public static int nextRandomInt() { return Math.abs(RANDOM.nextInt()); } /** * Returns the next positive random int * @param upto The upper range of the next random int * @return the next positive random int */ public static int nextRandomInt(int upto) { return Math.abs(RANDOM.nextInt(upto)); } /** * Generates a random character * @return a random character */ public static char randomChar() { return WORD_CHARS[Math.abs(RANDOM.nextInt(WORD_CHARS_LENGTH-1))]; } /** * Generates an array of random words * @param numberOfWords The number of words to generate * @param wordLength The length of ther words to generate * @return an array of random words */ public static String[] randomWords(int numberOfWords, int wordLength) { String[] words = new String[numberOfWords]; for(int i = 0; i < numberOfWords; i++) { char[] wordChars = new char[wordLength]; for(int x = 0; x < wordLength; x++) { wordChars[x] = randomChar(); } words[i] = new String(wordChars); } return words; } /** * Collects the memory usage, optionally calling a gc first * @param gcFirst If true, gc will called first * @return an array of heap memory and non-heap memory usages */ public MemoryUsage[] memoryUsage(boolean gcFirst) { if(gcFirst) { System.runFinalization(); ManagementFactory.getMemoryMXBean().gc(); sleep(500); ManagementFactory.getMemoryMXBean().gc(); } return new MemoryUsage[]{ManagementFactory.getMemoryMXBean().getHeapMemoryUsage(), ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage()}; } /** * Causes the calling thread to join for the specified time * @param time The time to join * @param unit The unit of the time */ public void sleep(long time, TimeUnit unit) { try { Thread.currentThread().join(TimeUnit.MILLISECONDS.convert(time, unit)); } catch (Exception e) { throw new RuntimeException("Failed to sleep", e); } } /** * Causes the calling thread to join for the specified time * @param time The time to join in ms. */ public void sleep(long time) { sleep(time, TimeUnit.MILLISECONDS); } public static class ExceptionCountingThreadFactory implements ThreadFactory, Thread.UncaughtExceptionHandler { /** Serial number for thread names */ private static final AtomicLong serial = new AtomicLong(0); /** The name of the thread pool */ private final String name; /** Counter for exceptions */ private static final AtomicInteger exceptionCount = new AtomicInteger(0); /** * Creates a new ExceptionCountingThreadFactory * @param name The name of the pool we're working for */ public ExceptionCountingThreadFactory(String name) { this.name = name; } /** * {@inheritDoc} * @see java.lang.Thread.UncaughtExceptionHandler#uncaughtException(java.lang.Thread, java.lang.Throwable) */ @Override public void uncaughtException(Thread t, Throwable e) { exceptionCount.incrementAndGet(); } /** * {@inheritDoc} * @see java.util.concurrent.ThreadFactory#newThread(java.lang.Runnable) */ @Override public Thread newThread(Runnable r) { Thread t = new Thread(r, name + "#" + serial.incrementAndGet()); t.setDaemon(true); t.setUncaughtExceptionHandler(this); return t; } /** * Returns the number of unhandled exception counts from this factory's threads * @return the number of unhandled exception counts from this factory's threads */ public int getExceptionCount() { return exceptionCount.get(); } } /** * Creates a benchmark thread pool * @param name The name of the thread pool * @param threadCount The number of threads * @return a fully started thread pool executor */ public ThreadPoolExecutor newExecutorService(final String name, int threadCount) { ExceptionCountingThreadFactory ectf = new ExceptionCountingThreadFactory(name); ThreadPoolExecutor executorService = new ThreadPoolExecutor(threadCount, threadCount, 1L, TimeUnit.NANOSECONDS, new ArrayBlockingQueue<Runnable>(threadCount, false), ectf); executorService.prestartAllCoreThreads(); return executorService; } /** * Returns the exception count for the passed thread pool executor * @param es The thread pool executor to get the exception count for * @return the number of exceptions counted */ public int getThreadPoolExceptionCount(ThreadPoolExecutor es) { return ((ExceptionCountingThreadFactory)es.getThreadFactory()).getExceptionCount(); } public static void main(String[] args) { BasePerformanceTestCase tc = new BasePerformanceTestCase(); for(int i = 0; i < 2000; i++) { tc.testRandomWords(); } } protected void testRandomWords() { log("Testing RandomWords"); int loopCount = 100000; int wordCount = 20; int wordSize = 12; long dummy = 0L; for(int i = 0; i < loopCount; i++) { dummy += randomWords(wordCount, wordSize).length; } log("Warmup Complete. Dummy:" + dummy); dummy = 0L; SystemClock.startTimer(); for(int i = 0; i < loopCount; i++) { dummy += randomWords(wordCount, wordSize).length; } ElapsedTime et = SystemClock.endTimer(); log("Dummy:" + dummy + "\nElapsed Time:" + et); log("Ns per array:" + et.avgNs(loopCount)); } }