All Downloads are FREE. Search and download functionalities are using the official Maven repository.

il.benchmark.Benchmark Maven / Gradle / Ivy

package util.benchmark;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * Benchmark is a small utility to quickly and easily run simple
 * Benchmarks on Java code. The code is based entirely on the benchmark
 * utility from Go (see http://golang.org/pkg/testing/).
 * 

* In order to write benchmarks, you need to provide a class that has * methods with signatures of the form: *

*

 public void methodNameIrrelevant (Benchmark b) {...} 
* * The benchmarks in the class you wrote will be executed when the * following command is run: * *
 java -jar Benchmark
 *  {name.of.class.containing.my.BenchmarkMethods} 
*

* ( The above seems to have some classloader issues ... ) *

* Alternatively, you can run benchmarks within Java code by calling: *

*

 Benchmark.runBenchmark(MyBenchMark.class); 
* * The Benchmark utility will vary the number of times to run the * benchmark until it runs long enough to be measured accurately. * * The number of times that a benchmark is supposed to run is signaled * to your benchmark function by the field N in the * Benchmark argument to the function. * * N.B. it is the responsibility of the benchmark function you provide * to run the code N number of times and not up to the * Benchmark utility! A typical benchmark function will look like this: * *
 *      public static void string (Benchmark b) {
 *        for (int n =0; n!= b.N; ++n) {
 *          String sum = "";
 *          for (int i = 1; i!= 1000; ++i) {
 *            sum += i;
 *          }
 *          if (n == 0) {
 *            b.setBytes(sum.getBytes().length);
 *          }
 *        }
 *      }
 *
 *  
*

* If setBytes(...) is called in the benchmark test, the * benchmarks will display information about the throughput (MB/s) of the code * as well as the the average time per operation. The value passed to * setBytes(...) is the number of bytes processed in a * single iteration, not how many bytes were processed in * b.N loop iterations. *

* In case the benchmark test requires lengthy setup that should be * ignored, you can stop and restart the internal timer like this: *

*

 *    public void myBenchmark (Benchmark b) {
 *      b.stopTimer();
 *      reallyLengthySetup();
 *      b.startTimer();
 *      for (int i =0; i!= b.N, ++i) {
 *        ... whatever ...
 *      }
 *    }
 *  
*

* This class contains three sample benchmarks measuring the * performance of string concatenation of String, * StringBuffer and StringBuilder. These can * be run as follows: *

*

 *     $ java -jar lib/benchmark.jar benchmark.Benchmark
 *            string	     500	   6523698 ns/op	   0.44 MB/s
 *      stringBuffer	   50000	     38646 ns/op	  74.76 MB/s
 *     stringBuilder	   50000	     32237 ns/op	  89.62 MB/s
 *  
*

* The results printed are: *

*

    *
  • name of the benchmark method
  • *
  • number of times the utility ran the benchmark
  • *
  • number of ns per loop
  • *
  • number of MB processed per second
  • *
*

* The tool may also be call as follows: *

*

 *     $ java -jar lib/benchmark.jar benchmark.Benchmark stringB.*
 *      stringBuffer	   50000	     38646 ns/op	  74.76 MB/s
 *     stringBuilder	   50000	     32237 ns/op	  89.62 MB/s
 *  
*

* The final argument is an optional regular expression, if provided, * only benchmark methods matching this expression are run. */ public class Benchmark { /** * the number of times the Benchmark utility expects you to perform * whatever it is you're benchmarking. *

* A typical benchmark method will look like this: *

*

     *   public void benchmarkMethod (Benchmark b) {
     *      for (int i = 0; i != b.N; ++i) {
     *        ... my benchmark code ...
     *      }
     *   }
     * 
*

* The responsibility of running the benchmark N is up to * you, the the utility just tells you the value of N... */ public int N; InternalBenchmark internal; long ns; long bytes; long start; static long min(long x, long y) { return x > y ? y : x; } static long max(long x, long y) { return x < y ? y : x; } // roundDown10 rounds a number down to the nearest power of 10. static int roundDown10(int n) { int tens = 0; while (n > 10) { n /= 10; ++tens; } int result = 1; for (int i = 0; i < tens; ++i) { result *= 10; } return result; } // roundUp rounds x up to a number of the form [1eX, 2eX, 5eX]. static int roundUp(int n) { int base = roundDown10(n); if (n < (2 * base)) { return 2 * base; } if (n < (5 * base)) { return 5 * base; } return 10 * base; } static boolean hasBenchmarkParam(Method m) { Class[] paramTypes = m.getParameterTypes(); if (paramTypes == null || paramTypes.length != 1) { return false; } return paramTypes[0].equals(Benchmark.class); } public static void runBenchmark(final Class c, String regexp) throws InstantiationException, IllegalAccessException { Pattern pattern = null; if (null != regexp) { pattern = Pattern.compile(regexp); } List list = new ArrayList(); int maxName = 0; for (Method m : c.getMethods()) { if (m.getReturnType() != Void.TYPE || !hasBenchmarkParam(m)) { continue; } if (null != pattern) { Matcher matcher = pattern.matcher(m.getName()); if (!matcher.matches()) { continue; } } list.add(m); maxName = m.getName().length() > maxName ? m.getName().length() : maxName; } for (final Method m : list) { InternalBenchmark ib = new InternalBenchmark() { Object o = c.newInstance(); void runBenchmark(Benchmark b) { try { m.invoke(o, b); } catch (Throwable iae) { throw new RuntimeException(iae); } } }; ib.name = m.getName(); Benchmark b = new Benchmark(); b.internal = ib; BenchmarkResult res = b.run(); System.out.println(String.format("%" + maxName + "s\t%s", ib.name, res)); } } public static void runBenchmark(final Class c) throws InstantiationException, IllegalAccessException { runBenchmark(c, null); } /** * Example: *

*

     *   public static void stringBuilder (Benchmark b) {
     *     for (int n =0; n!= b.N; ++n) {
     *       StringBuilder sum = new StringBuilder();
     *       for (int i = 1; i!= 1000; ++i) {
     *         sum.append(i);
     *       }
     *       if (n == 0) {
     *         b.setBytes(sum.length());
     *       }
     *     }
     *   }
     * 
*/ public static void stringBuilder(Benchmark b) { for (int n = 0; n != b.N; ++n) { StringBuilder sum = new StringBuilder(); for (int i = 1; i != 1000; ++i) { sum.append(i); } if (n == 0) { b.setBytes(sum.length()); } } } /** * Example: *

*

     *
     *   public static void string (Benchmark b) {
     *     for (int n =0; n!= b.N; ++n) {
     *       String sum = "";
     *       for (int i = 1; i!= 1000; ++i) {
     *         sum += i;
     *       }
     *       if (n == 0) {
     *         b.setBytes(sum.getBytes().length);
     *       }
     *     }
     *   }
     * 
*/ public static void string(Benchmark b) { for (int n = 0; n != b.N; ++n) { String sum = ""; for (int i = 1; i != 1000; ++i) { sum += i; } if (n == 0) { b.setBytes(sum.getBytes().length); } } } static void usage() { System.err.println("usage: [jre] Benchmark [optional regexp]"); System.exit(1); } public static void main(String[] args) throws Throwable { String regexp = null; if (args.length != 1 && args.length != 2) { usage(); } if (args.length == 2) { regexp = args[1]; } Class c = Class.forName(args[0]); runBenchmark(c, regexp); } // // Reflection stuff to load benchmarks // /** * starts timing a test. This function is called automatically * before a benchmark starts, but it can also used to resume timing after * a call to StopTimer. */ public void startTimer() { this.start = System.nanoTime(); } /** * stops timing a test. This can be used to pause the timer * while performing complex initialization that you don't * want to measure. */ public void stopTimer() { if (this.start > 0) { this.ns += System.nanoTime() - this.start; } this.start = 0; } /** * stops the timer and sets the elapsed benchmark time to zero. */ public void resetTimer() { this.start = 0; this.ns = 0; } // Test Benchmarks ... /** * records the number of bytes processed in a single operation. * If this is called, the benchmark will report ns/op and MB/s. */ public void setBytes(long n) { this.bytes = n; } long nsPerOp() { if (this.N <= 0) { return 0; } return this.ns / this.N; } // runN runs a single benchmark for the specified number of iterations. void runN(int n) { this.N = n; this.resetTimer(); this.startTimer(); this.internal.runBenchmark(this); this.stopTimer(); } /** * run times the benchmark function. It gradually increases the number * of benchmark iterations until the benchmark runs for a second in order * to get a reasonable measurement. It prints timing information in this form: *

*

     * 		testing.BenchmarkHello	100000		19 ns/op
     * 
*/ public BenchmarkResult run() { int n = 1; // Run the benchmark for a single iteration in case it's expensive. this.runN(n); // Run the benchmark for at least a second. while (this.ns < 1e9 && n < 1e9) { long last = n; // Predict iterations/sec. if (this.nsPerOp() == 0) { n = (int) 1e9; } else { n = (int) (1e9 / this.nsPerOp()); } // Run more iterations than we think we'll need for a second (1.5x). // Don't grow too fast in case we had timing errors previously. // Be sure to run at least one more than last time. n = (int) max(min(n + n / 2, 100 * last), last + 1); // Round up to something easy to read. n = roundUp(n); this.runN(n); } return new BenchmarkResult(this.N, this.ns, this.bytes); } /** * Example: *

*

     *   public void stringBuffer (Benchmark b) {
     *     for (int n =0; n!= b.N; ++n) {
     *       StringBuffer sum = new StringBuffer();
     *       for (int i = 1; i!= 1000; ++i) {
     *         sum.append(i);
     *       }
     *       if (n == 0) {
     *         b.setBytes(sum.length());
     *       }
     *     }
     *   }
     * 
*/ public void stringBuffer(Benchmark b) { for (int n = 0; n != b.N; ++n) { StringBuffer sum = new StringBuffer(); for (int i = 1; i != 1000; ++i) { sum.append(i); } if (n == 0) { b.setBytes(sum.length()); } } } } abstract class InternalBenchmark { String name; abstract void runBenchmark(Benchmark b); } class BenchmarkResult { int n; long ns; long bytes; BenchmarkResult(int n, long ns, long bytes) { this.n = n; this.ns = ns; this.bytes = bytes; } long nsPerOp() { return this.n <= 0 ? 0 : this.ns / this.n; } public String toString() { long ns = this.nsPerOp(); String mb = ""; if (ns > 0 && this.bytes > 0) { mb = String.format("\t%7.2f MB/s", (this.bytes / 1e6) / (ns / 1e9)); } return String.format("%8d\t%10d ns/op%s", this.n, ns, mb); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy