001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.performance;
042:
043: import java.lang.reflect.Method;
044:
045: import junit.framework.Test;
046: import junit.framework.TestSuite;
047: import junit.framework.TestResult;
048: import junit.framework.Assert;
049: import junit.framework.AssertionFailedError;
050:
051: /**
052: *
053: * @author Petr Nejedly
054: * @version 0.9
055: */
056: public class Benchmark extends Assert implements Test {
057:
058: /** the reporter that will be notified about results of measurements **/
059: private static Reporter reporter;
060:
061: /** The name the the actually tested method */
062: private String name;
063:
064: /** The name of the actually running Benchmark class */
065: private String className;
066:
067: private static final Object[] emptyObjectArray = new Object[0];
068: private static final Class[] emptyClassArray = new Class[0];
069:
070: /** Tells whether we'return doing sharp measurement or some preliminary
071: * autocalibration runs.
072: */
073: private boolean realRun = false;
074:
075: /** How many iterations to perform in the test method */
076: private int iterations;
077:
078: /** The actual value of the argument */
079: private Object argument;
080:
081: /** The full set of arguments used for this test */
082: private Object[] arguments;
083:
084: /** ptr to BenchmarkSuite for DataManagers */
085: private BenchmarkSuite bsuite;
086:
087: /** Creates new Benchmark without arguments for given test method
088: * @param name the name fo the testing method
089: */
090: public Benchmark(String name) {
091: this (name, new Object[] { new Object() {
092: public String toString() {
093: return "";
094: }
095: } });
096: }
097:
098: /** Creates new Benchmark for given test method with given set of arguments
099: * @param name the name fo the testing method
100: * @param args the array of objects describing arguments to testing method
101: */
102: public Benchmark(String name, Object[] args) {
103: this .name = name;
104: className = getClass().getName();
105: arguments = args; // should we clone it?
106: }
107:
108: // things to override by the implementation of a particular Benchmark
109:
110: /** This method is called before the actual test method to allow
111: * the benchmark to prepare accordingly to informations available
112: * through {@link #getIterationCount}, {@link #getArgument} and {@link #getName}.
113: * This method can use assertions to signal failure of the test.
114: * @throws Exception This method can throw any exception which is treated as a error in the testing code
115: * or testing enviroment.
116: */
117: protected void setUp() throws Exception {
118: }
119:
120: /** This method is called after every finished test method.
121: * It is intended to be used to free all the resources allocated
122: * during {@link #setUp} or the test itself.
123: * This method can use assertions to signal failure of the test.
124: * @throws Exception This method can throw any exception which is treated as a error in the testing code
125: * or testing enviroment.
126: */
127: protected void tearDown() throws Exception {
128: }
129:
130: /** This method can be overriden by a benchmark that can be resource
131: * intensive when set up for more iterations to limit the number
132: * of iterations.
133: * @return the maximal iteration count the benchmark is able to handle
134: * without loss of precision due swapping, gc()ing and so on.
135: * Its return value can depend on values returned by {@link #getName}
136: * and {@link #getParameter}.
137: */
138: protected int getMaxIterationCount() {
139: return 50000;
140: }
141:
142: // things to call from a particular benchmark
143:
144: /** How many iterations to perform.
145: * @return the iteration count the benchmark should perform or
146: * the {@link #setUp} should prepare the benchmark for.
147: */
148: protected final int getIterationCount() {
149: return iterations;
150: }
151:
152: /** Which test is to be performed.
153: * @return the name of the test method that will be performed.
154: * Benchmark writers could use this information during the
155: * {@link #setUp} to prepare different conditions for different tests.
156: */
157: protected final String getName() {
158: return name;
159: }
160:
161: /** For which argument the test runs
162: * @return the object describing the argument.
163: * It will be one of the objects specified in {@link #Benchmark(String,Object[])}
164: * constructor or {@link #setArgumentArray(Object[])}
165: * It will be <CODE>null</CODE> for tests that didn't specify any argument.
166: */
167: protected final Object getArgument() {
168: return argument;
169: }
170:
171: /** Sets the set of arguments for this test.
172: * @param args the array of objects describing arguments to testing method
173: */
174: protected final void setArgumentArray(Object[] args) {
175: arguments = args; //do clone ??
176: }
177:
178: // the rest is implemetation
179: /** How many tests should this Test perform
180: * @return the number of tests this Test should perform during
181: * {@link #run} method
182: */
183: public int countTestCases() {
184: return arguments.length;
185: }
186:
187: public final void run(TestResult result) {
188: try {
189: Method testMethod = getClass().getMethod(name,
190: emptyClassArray);
191: for (int a = 0; a < arguments.length; a++) {
192:
193: result.startTest(this );
194:
195: try {
196: doOneArgument(testMethod, arguments[a]);
197: } catch (AssertionFailedError err) {
198: result.addFailure(this , err);
199: } catch (ThreadDeath td) {
200: throw td; // need to propagate this
201: } catch (Throwable t) {
202: result.addError(this , t);
203: }
204:
205: result.endTest(this );
206: }
207: } catch (Exception e) {
208: e.printStackTrace();
209: }
210: }
211:
212: private void doOneArgument(Method testMethod, Object argument)
213: throws Exception {
214: setArgument(argument);
215: checkSetUpData();
216:
217: // class loading and so on...
218: realRun = false;
219: doOneMeasurement(testMethod, 1);
220:
221: // Do the adaptive measurement, the test have to take at least 200ms
222: // to be taken representative enough
223: long time = 0;
224: int iters = 1;
225: int maxIters = getMaxIterationCount();
226:
227: for (;;) {
228: realRun = false;
229: for (;;) {
230: time = doOneMeasurement(testMethod, iters);
231: if (time >= 300)
232: break;
233: if (2 * iters > maxIters)
234: break; // fuse
235: iters *= 2;
236: }
237:
238: if (2 * iters > maxIters)
239: break; // additional round won't help
240:
241: // A check it the calibrated iteration count is sufficient
242: // when running with realRun == true
243: realRun = true;
244: time = doOneMeasurement(testMethod, iters);
245: if (time > 200)
246: break;
247: iters *= 2;
248: }
249:
250: // do the real measurement
251: realRun = true;
252: for (int run = 0; run < 3; run++) {
253: time = doOneMeasurement(testMethod, iters);
254: reportSample(((float) time) / 1000 / iters);
255: }
256: }
257:
258: private void reportSample(float time) {
259: getReporter().addSample(className, name, argument, time);
260: }
261:
262: private static Reporter getReporter() {
263: if (reporter == null)
264: reporter = new PlainReporter();
265: return reporter;
266: }
267:
268: /** Set the Reporter to be used by the Benchmark
269: */
270: public static void setReporter(Reporter rep) {
271: reporter = rep;
272: }
273:
274: /** Helper method to be called from possible main that will take care
275: * of wrapping the test with TestSuite an running all the test methods
276: * of the testClass. It will also flush the reporter at the end.
277: */
278: protected static void simpleRun(Class testClass) {
279: junit.textui.TestRunner.run(new TestSuite(testClass));
280: getReporter().flush();
281: }
282:
283: private void checkSetUpData() throws Exception {
284: if (this instanceof DataManager) {
285: bsuite.setUpDataFor(this );
286: }
287: }
288:
289: final void setSuite(BenchmarkSuite bs) {
290: this .bsuite = bs;
291: }
292:
293: private long doOneMeasurement(Method testMethod, int iterations)
294: throws Exception {
295: setIterationCount(iterations);
296: setUp();
297: cooling();
298:
299: long time = System.currentTimeMillis();
300: testMethod.invoke(this , emptyObjectArray);
301: time = System.currentTimeMillis() - time;
302:
303: tearDown();
304: return time;
305: }
306:
307: private void setIterationCount(int count) {
308: iterations = count;
309: }
310:
311: void setArgument(Object arg) {
312: argument = arg;
313: }
314:
315: private void cooling() {
316: if (!realRun)
317: return;
318: System.gc();
319: System.gc();
320: try {
321: Thread.sleep(500);
322: } catch (InterruptedException exc) {
323: }
324: System.gc();
325: try {
326: Thread.sleep(300);
327: } catch (InterruptedException exc) {
328: }
329: }
330: }
|