/* * @(#)PerformanceTest .java 2008/01/16 * * Copyright 2008 li haiyang. All rights reserved. */ import java.io.PrintWriter; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.Time; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * * @author li haiyang * */ public abstract class PerformanceTest { /** * * @param methodName * @param args * @param threadNum * @param loops * @return * @see #run(String, List, int, int, int, boolean, boolean) */ public LoopsStatistics run(String methodName, List args, final int threadNum, final int loops) { return run(methodName, args, threadNum, 0, loops, true, true); } /** * * @param methodNames * @param args * @param threadNum * @param loops * @return * @see #run(String, List, int, int, int, boolean, boolean) */ public LoopsStatistics run(List methodNames, List args, final int threadNum, final int loops) { return run(methodNames, args, threadNum, 0, loops, true, true); } /** * * @param methodName * @param args * @param threadNum * @param threadDelay * @param loops * @param loopSetUpAndTearDown * @param callSetUpAndTearDown * @return * * @see #run(List, List, int, int, int, boolean, boolean) */ public LoopsStatistics run(String methodName, List args, final int threadNum, final int threadDelay, final int loops, final boolean loopSetUpAndTearDown, final boolean callSetUpAndTearDown) { List methodNames = new ArrayList(); methodNames.add(methodName); return run(methodNames, args, threadNum, threadDelay, loops, loopSetUpAndTearDown, callSetUpAndTearDown); } /** * * <br>The test method must have following format: * <br><code>Object testMethod(Object arg)</code> * <br>The input parameter <code>arg</code> is come from the previous method returned value, * <br> or from this loop argument if this test method is the first test method. * <br>The <code>returned value</code> is the initial method argument for next test method if this test method is not the last one. * <br>The last test method returned value is not signed as next loop initial argument. * <br> * <br> * <b>The argument flow:</b> * <br>(0) --> loop initial argument = thread initial argument * <br>(1) --> loop initial argument = loopSetup(loop initial argument) // if callLoopSetUpAndTearDown == true * <br>(2) --> method initial argument = loop initial argument * <br>(3) --> method initial argument = methodSetUp(method initial argument) // if callMethodSetUpAndTearDown == true. * <br>(4) --> method initial argument = testMethod(method initial argument) // if the test method doesn't throw exception. * <br>(5) --> method initial argument = methodTearDown(method initial argument) // if callMethodSetUpAndTearDown == true. * <br>(6) --> go to (3) for each method. * <br>(7) --> loop initial argument = loopTearDown(loop initial argument) // if callLoopSetUpAndTearDown == true * <br>(8) --> go to (1) for each loop. * * @param methodNames * @param args initial parameters for each test thread. Each thread has one parameter. * <br>So the <code>args</code>'s size must be equals with <code>threadNum</code>. * @param threadNum * @param threadDelay waiting time to start next thread. Start next thread right now if <code>threadDelay==0</code>. * @param loops loop times for each thread. * @param callLoopSetUpAndTearDown run <code>loopSetUp</code> before each loop * <br> and run <code>loopTearDown</code> after loop if <code>true</code> * @param callMethodSetUpAndTearDown run <code>methodSetUp</code> before each test method * <br> and run <code>methodTearDown</code> after test method if <code>true</code> * @return */ public LoopsStatistics run(List methodNames, final List args, final int threadNum, final int threadDelay, final int loops, final boolean callLoopSetUpAndTearDown, final boolean callMethodSetUpAndTearDown) { final Method[] methods = getMethodByName(methodNames); final Counter threadCounter = new Counter(); final List loopStatisticsList = Collections.synchronizedList(new ArrayList()); long startTime = System.currentTimeMillis(); if (threadNum == 0) { } else if (threadNum == 1) { runLoop(methods, args.get(0), loops, callLoopSetUpAndTearDown, callMethodSetUpAndTearDown, loopStatisticsList); } else { for (int threadIndex = 0; threadIndex < threadNum; threadIndex++) { final Object threadArg = args.get(threadIndex); Thread th = new Thread() { public void run() { try { runLoop(methods, threadArg, loops, callLoopSetUpAndTearDown, callMethodSetUpAndTearDown, loopStatisticsList); } finally { threadCounter.decreaseAndGet(); } } public synchronized void start() { threadCounter.increaseAndGet(); super.start(); } }; th.start(); if (threadDelay > 0) { sleep(threadDelay); } } while (threadCounter.getActiveThreadNum() > 0) { sleep(10); } } long endTime = System.currentTimeMillis(); return new LoopsStatistics(startTime, endTime, threadNum, loopStatisticsList); } protected void runLoop(final Method[] methods, final Object threadArg, final int loops, final boolean callLoopSetUpAndTearDown, final boolean callMethodSetUpAndTearDown, final List loopStatisticsList) { Object loopArg = threadArg; for (int loopIndex = 0; loopIndex < loops; loopIndex++) { if (callLoopSetUpAndTearDown) { try { loopArg = loopSetUp(threadArg); } catch (Exception e) { e.printStackTrace(); // ignore; } } long loopStartTime = System.currentTimeMillis(); List methodStatisticsList = new ArrayList(); Object methodArg = loopArg; for (int methodIndex = 0; methodIndex < methods.length; methodIndex++) { if (callMethodSetUpAndTearDown) { try { methodArg = methodSetUp(methodArg); } catch (Exception e) { e.printStackTrace(); // ignore; } } Object result = null; long methodStartTime = System.currentTimeMillis(); try { result = invokeMethod(methods[methodIndex], methodArg); methodArg = result; } catch (Exception e) { result = e; e.printStackTrace(); // ignore; } long methodEndTime = System.currentTimeMillis(); if (callMethodSetUpAndTearDown) { try { methodArg = methodTearDown(methodArg); } catch (Exception e) { e.printStackTrace(); // ignore; } } MethodStatistics methodStatistics = new MethodStatistics(methods[methodIndex] .getName(), methodStartTime, methodEndTime, result); methodStatisticsList.add(methodStatistics); } long loopEndTime = System.currentTimeMillis(); if (callLoopSetUpAndTearDown) { try { loopArg = loopTearDown(loopArg); } catch (Exception e) { e.printStackTrace(); // ignore; } } Loop loopStatistics = new LoopStatistics(loopStartTime, loopEndTime, methodStatisticsList); loopStatisticsList.add(loopStatistics); } } protected Object loopSetUp(Object arg) { return arg; } protected Object loopTearDown(Object arg) { return arg; } protected Object methodSetUp(Object arg) { return arg; } protected Object methodTearDown(Object arg) { return arg; } protected Method getMethodByName(String methodName) { try { return this.getClass().getMethod(methodName, new Class[0]); } catch (SecurityException e) { e.printStackTrace(); throw new RuntimeException(e); } catch (NoSuchMethodException e) { e.printStackTrace(); throw new RuntimeException(e); } } protected Method[] getMethodByName(List methodNames) { try { Method[] result = new Method[methodNames.size()]; for (int index = 0; index < methodNames.size(); index++) { result[index] = this.getClass() .getMethod((String) methodNames.get(index), new Class[] { Object.class }); } return result; } catch (SecurityException e) { e.printStackTrace(); throw new RuntimeException(e); } catch (NoSuchMethodException e) { e.printStackTrace(); throw new RuntimeException(e); } } protected Object invokeMethod(Method method, Object arg) { try { return method.invoke(this, new Object[] { arg }); } catch (IllegalArgumentException e) { e.printStackTrace(); throw new RuntimeException(e); } catch (IllegalAccessException e) { e.printStackTrace(); throw new RuntimeException(e); } catch (InvocationTargetException e) { e.printStackTrace(); throw new RuntimeException(e); } } protected void sleep(long time) { try { Thread.sleep(time); } catch (InterruptedException e) { e.printStackTrace(); throw new RuntimeException(e); } } public interface Loop { List getMethodNames(); MethodStatistics getSpendMinTimeMethod(); MethodStatistics getSpendMaxTimeMethod(); long getTotalMethodSpendedTime(); List getFailedMethodList(); public long getMethodSpendedTotalTime(String methodName); public long getMethodSpendedMaxTime(String methodName); public long getMethodSpendedMinTime(String methodName); public float getMethodSpendedAvgTime(String methodName); public List getMethodFailed(String methodName); public int getMethodSize(String methodName); } public static class Statistics { private Object result; private long startTime; private long endTime; public Statistics() { this.startTime = 0; this.endTime = 0; } public Statistics(long startTime, long endTime) { this.startTime = startTime; this.endTime = endTime; } public Object getResult() { return result; } public void setResult(Object result) { this.result = result; } public long getSpendedTime() { return endTime - startTime; } public long getStartTime() { return startTime; } public void setStartTime(long startTime) { this.startTime = startTime; } public long getEndTime() { return endTime; } public void setEndTime(long endTime) { this.endTime = endTime; } } public static class LoopStatistics extends Statistics implements Loop { private List methodStatisticsList; public LoopStatistics() { super(); } public LoopStatistics(long startTime, long endTime) { super(startTime, endTime); } public LoopStatistics(long startTime, long endTime, List methodStatisticsList) { super(startTime, endTime); this.methodStatisticsList = methodStatisticsList; } public List getMethodNames() { List result = new ArrayList(); if (methodStatisticsList != null) { for (int index = 0; index < methodStatisticsList.size(); index++) { MethodStatistics methodStatistics = (MethodStatistics) methodStatisticsList.get(index); result.add(methodStatistics.getMethodName()); } } return result; } public List getMethodStatisticsList() { if (methodStatisticsList == null) { methodStatisticsList = new ArrayList(); } return methodStatisticsList; } public void setMethodStatisticsList(List methodStatisticsList) { this.methodStatisticsList = methodStatisticsList; } public void addMethodStatisticsList(MethodStatistics methodStatistics) { getMethodStatisticsList().add(methodStatistics); } public long getTotalMethodSpendedTime() { long result = 0; if (methodStatisticsList != null) { for (int index = 0; index < methodStatisticsList.size(); index++) { MethodStatistics methodStatistics = (MethodStatistics) methodStatisticsList.get(index); result += methodStatistics.getSpendedTime(); } } return result; } public MethodStatistics getSpendMaxTimeMethod() { MethodStatistics result = null; if (methodStatisticsList != null) { for (int index = 0; index < methodStatisticsList.size(); index++) { MethodStatistics methodStatistics = (MethodStatistics) methodStatisticsList.get(index); if ((result == null) || (methodStatistics.getSpendedTime() > result.getSpendedTime())) { result = methodStatistics; } } } return result; } public MethodStatistics getSpendMinTimeMethod() { MethodStatistics result = null; if (methodStatisticsList != null) { for (int index = 0; index < methodStatisticsList.size(); index++) { MethodStatistics methodStatistics = (MethodStatistics) methodStatisticsList.get(index); if ((result == null) || (methodStatistics.getSpendedTime() < result.getSpendedTime())) { result = methodStatistics; } } } return result; } public List getFailedMethodList() { List result = new ArrayList(); if (methodStatisticsList != null) { for (int index = 0; index < methodStatisticsList.size(); index++) { MethodStatistics methodStatistics = (MethodStatistics) methodStatisticsList.get(index); if (methodStatistics.isFailed()) { result.add(methodStatistics); } } } return result; } public long getMethodSpendedTotalTime(String methodName) { long result = 0; if (methodStatisticsList != null) { for (int index = 0; index < methodStatisticsList.size(); index++) { MethodStatistics methodStatistics = (MethodStatistics) methodStatisticsList.get(index); if (methodStatistics.getMethodName().equals(methodName)) { result += methodStatistics.getSpendedTime(); } } } return result; } public long getMethodSpendedMaxTime(String methodName) { long result = 0; if (methodStatisticsList != null) { for (int index = 0; index < methodStatisticsList.size(); index++) { MethodStatistics methodStatistics = (MethodStatistics) methodStatisticsList.get(index); if (methodStatistics.getMethodName().equals(methodName)) { if (methodStatistics.getSpendedTime() > result) { result = methodStatistics.getSpendedTime(); } } } } return result; } public long getMethodSpendedMinTime(String methodName) { long result = Integer.MAX_VALUE; if (methodStatisticsList != null) { for (int index = 0; index < methodStatisticsList.size(); index++) { MethodStatistics methodStatistics = (MethodStatistics) methodStatisticsList.get(index); if (methodStatistics.getMethodName().equals(methodName)) { if (methodStatistics.getSpendedTime() < result) { result = methodStatistics.getSpendedTime(); } } } } return result; } public float getMethodSpendedAvgTime(String methodName) { float totalTime = 0; int methodNum = 0; if (methodStatisticsList != null) { for (int index = 0; index < methodStatisticsList.size(); index++) { MethodStatistics methodStatistics = (MethodStatistics) methodStatisticsList.get(index); if (methodStatistics.getMethodName().equals(methodName)) { totalTime += methodStatistics.getSpendedTime(); methodNum++; } } } return (methodNum > 0) ? (totalTime / methodNum) : totalTime; } public List getMethodFailed(String methodName) { List result = new ArrayList(); if (methodStatisticsList != null) { for (int index = 0; index < methodStatisticsList.size(); index++) { MethodStatistics methodStatistics = (MethodStatistics) methodStatisticsList.get(index); if (methodStatistics.isFailed() && methodStatistics.equals(methodName)) { result.add(methodStatistics); } } } return result; } public int getMethodSize(String methodName) { int methodSize = 0; if (methodStatisticsList != null) { for (int index = 0; index < methodStatisticsList.size(); index++) { MethodStatistics methodStatistics = (MethodStatistics) methodStatisticsList.get(index); if (methodStatistics.getMethodName().equals(methodName)) { methodSize++; } } } return methodSize; } } public static class LoopsStatistics extends Statistics implements Loop { private final int threadNum; private List loopStatisticsList; public LoopsStatistics(long startTime, long endTime, int threadNum) { super(startTime, endTime); this.threadNum = threadNum; } public LoopsStatistics(long startTime, long endTime, int threadNum, List loopStatisticsList) { this(startTime, endTime, threadNum); this.loopStatisticsList = loopStatisticsList; } public int getThreadNum() { return threadNum; } public List getMethodNames() { List result = null; if (loopStatisticsList == null) { result = new ArrayList(); } else { result = ((Loop) loopStatisticsList.get(0)).getMethodNames(); } return result; } public List getLoopStatisticsList() { if (loopStatisticsList == null) { loopStatisticsList = new ArrayList(); } return loopStatisticsList; } public void setLoopStatisticsList(List loopStatisticsList) { this.loopStatisticsList = loopStatisticsList; } public void addMethodStatisticsList(Loop loopStatistics) { getLoopStatisticsList().add(loopStatistics); } public long getTotalMethodSpendedTime() { long result = 0; if (loopStatisticsList != null) { for (int index = 0; index < loopStatisticsList.size(); index++) { Loop loopStatistics = (Loop) loopStatisticsList.get(index); result += loopStatistics.getTotalMethodSpendedTime(); } } return result; } public MethodStatistics getSpendMaxTimeMethod() { MethodStatistics result = null; if (loopStatisticsList != null) { for (int index = 0; index < loopStatisticsList.size(); index++) { Loop loopStatistics = (Loop) loopStatisticsList.get(index); MethodStatistics methodStatistics = loopStatistics.getSpendMaxTimeMethod(); if ((result == null) || (methodStatistics.getSpendedTime() > result.getSpendedTime())) { result = methodStatistics; } } } return result; } public MethodStatistics getSpendMinTimeMethod() { MethodStatistics result = null; if (loopStatisticsList != null) { for (int index = 0; index < loopStatisticsList.size(); index++) { Loop loopStatistics = (Loop) loopStatisticsList.get(index); MethodStatistics methodStatistics = loopStatistics.getSpendMinTimeMethod(); if ((result == null) || (methodStatistics.getSpendedTime() < result.getSpendedTime())) { result = methodStatistics; } } } return result; } public List getFailedMethodList() { List result = new ArrayList(); if (loopStatisticsList != null) { for (int index = 0; index < loopStatisticsList.size(); index++) { Loop loopStatistics = (Loop) loopStatisticsList.get(index); result.addAll(loopStatistics.getFailedMethodList()); } } return result; } public long getMethodSpendedTotalTime(String methodName) { long result = 0; if (loopStatisticsList != null) { for (int index = 0; index < loopStatisticsList.size(); index++) { Loop loopStatistics = (Loop) loopStatisticsList.get(index); result += loopStatistics.getMethodSpendedTotalTime(methodName); } } return result; } public long getMethodSpendedMaxTime(String methodName) { long result = 0; if (loopStatisticsList != null) { for (int index = 0; index < loopStatisticsList.size(); index++) { Loop loopStatistics = (Loop) loopStatisticsList.get(index); long loopMethodMaxTime = loopStatistics.getMethodSpendedMaxTime(methodName); if (loopMethodMaxTime > result) { result = loopMethodMaxTime; } } } return result; } public long getMethodSpendedMinTime(String methodName) { long result = Integer.MAX_VALUE; if (loopStatisticsList != null) { for (int index = 0; index < loopStatisticsList.size(); index++) { Loop loopStatistics = (Loop) loopStatisticsList.get(index); long loopMethodMinTime = loopStatistics.getMethodSpendedMinTime(methodName); if (loopMethodMinTime < result) { result = loopMethodMinTime; } } } return result; } public float getMethodSpendedAvgTime(String methodName) { float totalTime = 0; int methodNum = 0; if (loopStatisticsList != null) { for (int index = 0; index < loopStatisticsList.size(); index++) { Loop loopStatistics = (Loop) loopStatisticsList.get(index); float loopMethodTotalTime = loopStatistics.getMethodSpendedTotalTime(methodName); int loopMethodSize = loopStatistics.getMethodSize(methodName); totalTime += loopMethodTotalTime; methodNum += loopMethodSize; } } return (methodNum > 0) ? (totalTime / methodNum) : totalTime; } public List getMethodFailed(String methodName) { List result = new ArrayList(); if (loopStatisticsList != null) { for (int index = 0; index < loopStatisticsList.size(); index++) { Loop loopStatistics = (Loop) loopStatisticsList.get(index); result.addAll(loopStatistics.getMethodFailed(methodName)); } } return result; } public int getMethodSize(String methodName) { int result = 0; if (loopStatisticsList != null) { for (int index = 0; index < loopStatisticsList.size(); index++) { Loop loopStatistics = (Loop) loopStatisticsList.get(index); result += loopStatistics.getMethodSize(methodName); } } return result; } public void printResult() { System.out.println( "================================================================================"); writeResult(new PrintWriter(System.out)); System.out.println( "================================================================================"); } public void writeResult(PrintWriter writer) { writer.println(); writer.println("threadNum=" + threadNum + "; totalLoops=" + loopStatisticsList.size() + "."); writer.println("<startTime>: " + getStartTime() + "(" + new Time(getStartTime()) + ")"); writer.println("<endTime>: " + getEndTime() + "(" + new Time(getEndTime()) + ")"); writer.println("<totalSpendedTime>: " + getSpendedTime() + "ms"); writer.write("<eachLoopSpendedTime>: " + (((float) getSpendedTime()) / loopStatisticsList.size()) + "ms"); writer.println(); MethodStatistics methodStatistics = getSpendMaxTimeMethod(); if (methodStatistics != null) { writer.println("<singleMethodSpendedMaxTime>: method=" + methodStatistics.getMethodName() + ", spendedTime=" + methodStatistics.getSpendedTime() + "ms"); } methodStatistics = getSpendMinTimeMethod(); if (methodStatistics != null) { writer.println("<singleMethodSpendedMinTime>: method=" + methodStatistics.getMethodName() + ", spendedTime=" + methodStatistics.getSpendedTime() + "ms"); } String methodNameTitil = "<method name>, "; List methodNameList = getMethodNames(); int maxMethodNameLength = methodNameTitil.length(); if (methodNameList.size() > 0) { for (int index = 0; index < methodNameList.size(); index++) { String methodName = (String) methodNameList.get(index); if ((methodName.length() + 3) > maxMethodNameLength) { maxMethodNameLength = methodName.length() + 3; } } } writer.println(); writer.println(fillSpace(methodNameTitil, maxMethodNameLength) + "<arg time>, <max time>, <min time>, <total time>"); for (int index = 0; index < methodNameList.size(); index++) { String methodName = (String) methodNameList.get(index); float avgTime = getMethodSpendedAvgTime(methodName); String st = String.valueOf(avgTime); int periodIndex = st.indexOf('.'); if ((periodIndex > 0) && ((st.length() - periodIndex) > 3)) { st = st.substring(0, periodIndex + 3); avgTime = Float.valueOf(st).floatValue(); } long maxTime = getMethodSpendedMaxTime(methodName); long minTime = getMethodSpendedMinTime(methodName); long totalTime = getMethodSpendedTotalTime(methodName); writer.println(fillSpace(methodName + ", ", maxMethodNameLength) + fillSpace(" " + avgTime + ", ", 12) + fillSpace(" " + maxTime + ", ", 12) + fillSpace(" " + minTime + ", ", 12) + fillSpace(" " + totalTime + ", ", 11)); } writer.println("(unit: millisecond)"); writer.println(); writeError(writer); writer.println(); writer.flush(); } protected void writeError(PrintWriter writer) { MethodStatistics methodStatistics; List failedMethodList = getFailedMethodList(); if (failedMethodList.size() > 0) { writer.println(); writer.println("Errors: "); for (int index = 0; index < failedMethodList.size(); index++) { writer.println("<<---------------------------------------------------------"); methodStatistics = (MethodStatistics) failedMethodList.get(index); writer.println(methodStatistics.toString()); // Exception e = (Exception) methodStatistics.getResult(); // e.printStackTrace(); writer.println("--------------------------------------------------------->>"); writer.println(); } } } private String fillSpace(String st, int length) { while (st.length() < length) { st += " "; } return st; } } public static class MethodStatistics extends Statistics { private String methodName; private Object result; public MethodStatistics(String methodName) { super(); this.methodName = methodName; } public MethodStatistics(String methodName, long startTime, long endTime) { super(startTime, endTime); this.methodName = methodName; } public MethodStatistics(String methodName, long startTime, long endTime, Object result) { super(startTime, endTime); this.methodName = methodName; this.result = result; } public String getMethodName() { return methodName; } public Object getResult() { return result; } public void setResult(Object result) { this.result = result; } public boolean isFailed() { return (result != null) && result instanceof Exception; } public String toString() { return "method=" + methodName + ", startTime=" + getStartTime() + ", endTime=" + getEndTime() + ", result=" + result + ". "; } } public static class Counter { private volatile int value = 0; public synchronized int getActiveThreadNum() { return value; } public synchronized int increaseAndGet() { return ++value; } public synchronized int getAndIncrease() { return value++; } public synchronized int decreaseAndGet() { return --value; } public synchronized int getAndDecrease() { return value--; } } }