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();