2 Copyright 2010 Sun Microsystems, Inc.
3 All rights reserved. Use is subject to license terms.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; version 2 of the License.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 package com.mysql.clusterj.jpatest;
21 import java.util.Properties;
22 import java.util.List;
24 import java.util.HashSet;
25 import java.util.ArrayList;
26 import java.util.Date;
27 import java.text.SimpleDateFormat;
29 import java.io.FileInputStream;
30 import java.io.FileWriter;
31 import java.io.IOException;
32 import java.io.PrintWriter;
33 import java.io.InputStream;
37 * This class is part of the CRUND benchmark that measures standard database operations
38 * over a series of transactions on an increasing data set.
40 * The abstract database operations are variations of: Create,
41 * Read, Update, Navigate, and Delete -- hence, the benchmark's name: CRUND.
43 * The actual operations are defined by subclasses to allow measuring the
44 * operation performance across different datastore implementations.
46 * @see <a href="http://www.urbandictionary.com/define.php?term=crund">Urban Dictionary: crund</a>
48 * <li> used to debase people who torture others with their illogical
49 * attempts to make people laugh;
50 * <li> reference to cracking obsolete jokes;
52 * <li> to hit hard or smash.
55 abstract public class Driver {
58 * The stream to write messages to.
60 static protected final PrintWriter out = new PrintWriter(System.out, true);
63 * The stream to write error messages to.
65 static protected final PrintWriter err = new PrintWriter(System.err, true);
68 * Shortcut to the end-of-line character sequence.
70 static protected final String endl = System.getProperty("line.separator");
73 * Shortcut to the Runtime.
75 static private final Runtime rt = Runtime.getRuntime();
77 // command-line arguments
78 static private final List<String> propFileNames = new ArrayList<String>();
79 static private String logFileName
81 + new SimpleDateFormat("yyyyMMdd_HHMMss").format(new Date())
84 // the data output writer
85 private PrintWriter log;
88 protected final Properties props = new Properties();
89 protected String descr = "";
90 protected boolean logRealTime = false;
91 protected boolean logMemUsage = false;
92 protected boolean includeFullGC = false;
93 protected boolean logSumOfOps = false;
94 protected boolean renewOperations = false;
95 protected boolean renewConnection = false;
96 protected boolean allowExtendedPC = false;
97 protected int aStart = (1 << 8), aEnd = (1 << 12), aIncr = (1 << 2);
98 protected int bStart = (1 << 8), bEnd = (1 << 12), bIncr = (1 << 2);
99 protected int maxStringLength = 100;
100 protected int warmupRuns = 0;
101 protected int hotRuns = 0;
102 protected final Set<String> exclude = new HashSet<String>();
104 // ----------------------------------------------------------------------
107 * A database operation to be benchmarked.
109 protected abstract class Op {
110 final protected String name;
112 public Op(String name) {
116 public String getName() {
120 public abstract void run(int countA, int countB) throws Exception;
124 * The list of database operations to be benchmarked.
125 * While the list instance is final, its content is managed by methods
126 * initOperations() and closeOperations() as defined by subclasses.
128 protected final List<Op> ops = new ArrayList<Op>();
130 // buffers collecting the header and data lines written to log
132 private StringBuilder header;
133 private StringBuilder rtimes;
134 private StringBuilder musage;
136 // benchmark data fields
137 private long t0 = 0, t1 = 0, ta = 0;
138 private long m0 = 0, m1 = 0, ma = 0;
140 // benchmark methods to be defined by subclasses
141 abstract protected void initConnection() throws Exception;
142 abstract protected void closeConnection() throws Exception;
143 abstract protected void initOperations() throws Exception;
144 abstract protected void closeOperations() throws Exception;
145 abstract protected void clearPersistenceContext() throws Exception;
146 abstract protected void clearData() throws Exception;
147 abstract protected void beginTransaction() throws Exception;
148 abstract protected void commitTransaction() throws Exception;
149 abstract protected void rollbackTransaction() throws Exception;
152 * Reports an error if a condition is not met.
154 * An invariant method to ensure the consistent application
155 * of verifying read results.
157 static protected final void verify(boolean cond) {
160 throw new RuntimeException("wrong data; verification failed");
164 * Loads a dynamically linked system library and reports any failures.
166 static protected void loadSystemLibrary(String name) {
167 out.print("loading libary ...");
170 System.loadLibrary(name);
171 } catch (UnsatisfiedLinkError e) {
174 path = System.getProperty("java.library.path");
175 } catch (Exception ex) {
176 path = "<exception caught: " + ex.getMessage() + ">";
178 err.println("NdbBase: failed loading library '"
179 + name + "'; java.library.path='" + path + "'");
181 } catch (SecurityException e) {
182 err.println("NdbBase: failed loading library '"
183 + name + "'; caught exception: " + e);
186 out.println(" [" + name + "]");
189 // ----------------------------------------------------------------------
192 * Runs the entire benchmark.
199 for (int i = 0; i < warmupRuns; i++)
202 // truncate log file, reset log buffers
204 out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
205 out.println("start logging results ...");
206 out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
208 header = new StringBuilder();
209 rtimes = new StringBuilder();
210 musage = new StringBuilder();
215 for (int i = 0; i < hotRuns; i++)
220 log.println(descr + ", rtime[ms]"
221 + header.toString() + endl
222 + rtimes.toString() + endl + endl + endl);
225 log.println(descr + ", net musage[KiB]"
226 + header.toString() + endl
227 + musage.toString() + endl + endl + endl);
231 } catch (Exception ex) {
232 // end the program regardless of threads
233 out.println("caught " + ex);
234 ex.printStackTrace();
235 System.exit(2); // return an error code
240 * Initializes the benchmark's resources.
242 protected void init() throws Exception {
250 header = new StringBuilder();
251 rtimes = new StringBuilder();
252 musage = new StringBuilder();
256 * Releases the benchmark's resources.
258 protected void close() throws Exception {
268 * Loads the benchmark's properties from properties files.
269 * Keys might appear multiple times in the same or different files.
270 * If there are duplicate keys, the last key definition overrides any
271 * previous value for the same key in the same or different file.
273 private void loadProperties() throws IOException {
274 if (propFileNames.size() == 0) {
275 propFileNames.add("crund.properties");
279 for (String fn : propFileNames) {
280 out.println("reading properties file: " + fn);
281 InputStream is = null;
283 is = new FileInputStream(fn);
285 } catch (Exception e) {
286 out.println("error reading file.");
295 * Retrieves a property's value and parses it as a boolean.
297 protected boolean parseBoolean(String k) {
298 return Boolean.parseBoolean(props.getProperty(k));
302 * Retrieves a property's value and parses it as a signed decimal integer.
303 * @throws NumberFormatException with a descriptive error message
305 protected int parseInt(String k, int vdefault) {
306 final String v = props.getProperty(k);
308 return (v == null ? vdefault : Integer.parseInt(v));
309 } catch (NumberFormatException e) {
310 final NumberFormatException nfe = new NumberFormatException(
311 "invalid value of benchmark property ('" + k + "', '"
319 * Initializes the benchmark properties.
321 protected void initProperties() {
322 // initialize boolean/numeric properties
323 logRealTime = parseBoolean("logRealTime");
324 logMemUsage = parseBoolean("logMemUsage");
325 includeFullGC = parseBoolean("includeFullGC");
326 logSumOfOps = parseBoolean("logSumOfOps");
327 renewOperations = parseBoolean("renewOperations");
328 renewConnection = parseBoolean("renewConnection");
329 allowExtendedPC = parseBoolean("allowExtendedPC");
330 aStart = parseInt("aStart", 1 << 8);
331 aEnd = parseInt("aEnd", 1 << 12);
332 aIncr = parseInt("aIncr", 1 << 2);
333 bStart = parseInt("bStart", 1 << 8);
334 bEnd = parseInt("bEnd", 1 << 12);
335 bIncr = parseInt("bIncr", 1 << 2);
336 maxStringLength = parseInt("maxStringLength", 100);
337 warmupRuns = parseInt("warmupRuns", 0);
338 hotRuns = parseInt("hotRuns", 1);
340 // initialize exclude set
341 final String[] e = props.getProperty("exclude", "").split(",");
342 for (int i = 0; i < e.length; i++) {
348 * Prints the benchmark's properties.
350 protected void printProperties() {
353 out.println("main settings:");
354 out.println("logRealTime: " + logRealTime);
355 out.println("logMemUsage: " + logMemUsage);
356 out.println("includeFullGC: " + includeFullGC);
357 out.println("logSumOfOps: " + logSumOfOps);
358 out.println("renewOperations: " + renewOperations);
359 out.println("renewConnection: " + renewConnection);
360 out.println("allowExtendedPC: " + allowExtendedPC);
361 out.println("aStart: " + aStart);
362 out.println("aEnd: " + aEnd);
363 out.println("aIncr: " + aIncr);
364 out.println("bStart: " + bStart);
365 out.println("bEnd: " + bEnd);
366 out.println("bIncr: " + bIncr);
367 out.println("maxStringLength: " + maxStringLength);
368 out.println("warmupRuns: " + warmupRuns);
369 out.println("hotRuns: " + hotRuns);
370 out.println("exclude: " + exclude);
374 * Opens the benchmark's data log file.
376 private void openLogFile() throws IOException {
378 out.println("writing results to file: " + logFileName);
379 log = new PrintWriter(new FileWriter(logFileName, false));
383 * Closes the benchmark's data log file.
385 private void closeLogFile() throws IOException {
386 out.print("closing files ... ");
392 out.println(" [ok]");
395 // ----------------------------------------------------------------------
398 * Runs a series of benchmark operations on scaled-up data.
400 protected void runTests() throws Exception {
404 for (int i = aStart; i <= aEnd; i *= aIncr) {
405 //for (int j = bBeg; j <= bEnd; j *= bIncr)
406 for (int j = (i > bStart ? i : bStart); j <= bEnd; j *= bIncr) {
409 } catch (Exception ex) {
410 // already in rollback for database/orm exceptions
411 //rollbackTransaction();
418 out.println("------------------------------------------------------------");
427 * Runs a series of benchmark operations.
429 protected void runOperations(int countA, int countB) throws Exception {
431 out.println("------------------------------------------------------------");
432 out.println("countA = " + countA + ", countB = " + countB);
437 rtimes.append("A=" + countA + ", B=" + countB);
441 musage.append("A=" + countA + ", B=" + countB);
446 if (renewConnection) {
451 } else if (renewOperations) {
460 if (!allowExtendedPC) {
461 // effectively prevent caching beyond Tx scope by clearing
462 // any data/result caches before the next transaction
463 clearPersistenceContext();
465 runOp(op, countA, countB);
469 header.append("\ttotal");
476 rtimes.append("\t" + ta);
478 out.println("total");
479 out.println("tx real time = " + ta + "\tms [begin..commit]");
485 musage.append("\t" + ma);
487 out.println("total");
488 out.println("net mem usage = " + (ma >= 0 ? "+" : "") + ma
496 * Runs a benchmark operation.
498 protected void runOp(Op op, int countA, int countB) throws Exception {
499 final String name = op.getName();
500 if (!exclude.contains(name)) {
502 op.run(countA, countB);
508 * Begins a benchmarked transaction.
510 protected void begin(String name) throws Exception {
514 // attempt max GC, before tx
518 m0 = rt.totalMemory() - rt.freeMemory();
522 //t0 = System.currentTimeMillis();
523 t0 = System.nanoTime() / 1000000;
530 * Closes a benchmarked transaction.
532 protected void commit(String name) throws Exception {
535 // attempt one full GC, before timing tx end
541 //t1 = System.currentTimeMillis();
542 t1 = System.nanoTime() / 1000000;
543 final long t = t1 - t0;
544 out.println("tx real time = " + t + "\tms [begin..commit]");
545 //rtimes.append("\t" + (Math.round(t / 100.0) / 10.0));
546 rtimes.append("\t" + t);
551 // attempt max GC, after tx
553 m1 = rt.totalMemory() - rt.freeMemory();
554 final long m0K = (m0 / 1024);
555 final long m1K = (m1 / 1024);
556 final long mK = m1K - m0K;
557 out.println("net mem usage = " + (mK >= 0 ? "+" : "") + mK
558 + "\tKiB [" + m0K + "K->" + m1K + "K]");
560 out.println("allocated memory = "
561 + m1 + "\tK after commit");
562 out.println("total memory = "
563 + (rt.totalMemory() / 1024) + "\tK after commit");
564 out.println("max memory = "
565 + (rt.maxMemory() / 1024) + "\tK after commit");
567 musage.append("\t" + mK);
572 header.append("\t" + name);
576 * Attempts to run the JVM's Garbage Collector.
578 static private void gc() {
579 // empirically determined limit after which no further
580 // reduction in memory usage has been observed
581 //final int nFullGCs = 5;
582 final int nFullGCs = 10;
583 for (int i = 0; i < nFullGCs; i++) {
586 long newfree = rt.freeMemory();
589 rt.runFinalization();
591 newfree = rt.freeMemory();
593 } while (newfree > oldfree);
598 // ----------------------------------------------------------------------
601 * Prints a command-line usage message and exits.
603 static private void exitUsage() {
604 out.println("usage: [options]");
605 out.println(" [-p <file name>]... a properties file name");
606 out.println(" [-l <file name>] log file name for data output");
607 out.println(" [-h|--help] print usage message and exit");
609 System.exit(1); // return an error code
613 * Parses the benchmark's command-line arguments.
615 static public void parseArguments(String[] args) {
616 for (int i = 0; i < args.length; i++) {
617 final String arg = args[i];
618 if (arg.equals("-p")) {
619 if (i >= args.length) {
622 propFileNames.add(args[++i]);
623 } else if (arg.equals("-l")) {
624 if (i >= args.length) {
627 logFileName = args[++i];
628 } else if (arg.equals("-h") || arg.equals("--help")) {
631 out.println("unknown option: " + arg);
637 /** Clears the propFileNames from a previous run
640 public static void clearPropFileNames() {
641 propFileNames.clear();