/**
* Licensed to Cloudera, Inc. under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Cloudera, Inc. licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.cloudera.util;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import com.google.common.base.Preconditions;
/**
* Simple class to consolidate benchmarking and timing code. This will help me
* find the bottlenecks.
*
* Usage: create new benchmark. call mark with comments whenever wanted. call
* done to flush print writer.
*
* std out is used for human readable results, stderr is used for csv formatted
* data.
*/
public class Benchmark {
long start;
long last;
PrintWriter out;
PrintWriter log;
List<String> values = new ArrayList<String>();
String name;
public Benchmark(String name, PrintWriter o, PrintWriter l) {
Preconditions.checkNotNull(o);
Preconditions.checkNotNull(l);
this.name = name;
out = o;
log = l;
// do this first because of potential dns lookup delay.
try {
String host = InetAddress.getLocalHost().getHostName();
values.add(host);
} catch (UnknownHostException e) {
values.add("localhost");
e.printStackTrace();
}
start = System.nanoTime();
last = start;
// do a GC cleanup before each benchmark test.
// Runtime rt = Runtime.getRuntime();
// rt.gc();
flushMemory();
Runtime r = Runtime.getRuntime();
long fmem = r.freeMemory();
long tmem = r.totalMemory();
long umem = tmem - fmem; // memory used
out.printf("[%,18dus, %,18d b mem]\tStarting (after gc) \n", 0, umem);
}
// default to standard out.
public Benchmark() {
this(null, new PrintWriter(new OutputStreamWriter(System.out)),
new PrintWriter(new OutputStreamWriter(System.err)));
}
// this was named, make the values go to std err.
public Benchmark(String name) {
this(name, new PrintWriter(new OutputStreamWriter(System.out)),
new PrintWriter(new OutputStreamWriter(System.err)));
}
public void mark(Object... logs) {
StringBuffer b = new StringBuffer();
boolean first = true;
for (Object o : logs) {
if (!first)
b.append(',');
b.append(o.toString());
first = false;
values.add(o.toString());
}
_mark(b.toString());
}
void _mark(String s) {
// record time before we incur gc
long now = System.nanoTime();
long delta = (now - last);
long cumulative = (now - start);
// force a GC to get memory usage estimate
flushMemory();
Runtime r = Runtime.getRuntime();
long fmem = r.freeMemory();
long tmem = r.totalMemory();
long umem = tmem - fmem; // memory used
// results
out.printf("[%,18dns d %,18dns %,18d b mem]\t%s\n", cumulative, delta,
umem, s);
// values.add(s);
values.add("" + delta);
values.add("" + umem);
// skip over gc time
last = System.nanoTime(); // don't count gc time.
}
/**
* Dumps results in human readable form.
*/
public void done() {
out.flush();
printCsvLog(log);
}
public PrintWriter getOut() {
return out;
}
public PrintWriter getLog() {
return log;
}
/**
* In case I want to print the csv report summary
*
* @param pw
*/
public void printCsvLog(PrintWriter pw) {
Preconditions.checkNotNull(pw);
boolean first = true;
if (name != null) {
pw.print(name);
first = false;
}
for (String s : values) {
if (!first)
pw.print(",");
pw.print(s);
first = false;
}
pw.println();
pw.flush();
}
// Javadoc says Runtime.gc is just a hint! Sun's JDK with default GC actually
// triggers a gc.
public static void flushMemory() {
System.gc();
}
/**
* alternate method to force GC. It allocates memory until it gets and
* OutOfMemoryError, release the data, and then allocates one more time to
* guarantee a forced GC.
*
* With sun's JDK on default setting System.gc() is apparently sufficient.
*/
public static void flushMemoryExhaust() {
// Use a vector to hold the memory.
Vector<byte[]> v = new Vector<byte[]>();
int count = 0;
// increment in megabyte chunks initially
int size = 1048576;
// Keep going until we would be requesting
// chunks of 1 byte
while (size > 1) {
try {
for (; true; count++) {
// request and hold onto more memory
v.addElement(new byte[size]);
}
}
// If we encounter an OutOfMemoryError, keep
// trying to get more memory, but asking for
// chunks half as big.
catch (OutOfMemoryError bounded) {
size = size / 2;
}
}
// Now release everything for GC
v = null;
// and ask for a new Vector as a new small object
// to make sure garbage collection kicks in before
// we exit the method.
v = new Vector<byte[]>();
}
}