blob: cce6c85c533df41fb09ff1b2cc6eb08bfbd9d812 [file] [log] [blame]
/*
* This is adapted from a benchmark written by John Ellis and Pete Kovac
* of Post Communications.
* It was modified by Hans Boehm of Silicon Graphics.
*
* This is no substitute for real applications. No actual application
* is likely to behave in exactly this way. However, this benchmark was
* designed to be more representative of real applications than other
* Java GC benchmarks of which we are aware.
* It attempts to model those properties of allocation requests that
* are important to current GC techniques.
* It is designed to be used either to obtain a single overall performance
* number, or to give a more detailed estimate of how collector
* performance varies with object lifetimes. It prints the time
* required to allocate and collect balanced binary trees of various
* sizes. Smaller trees result in shorter object lifetimes. Each cycle
* allocates roughly the same amount of memory.
* Two data structures are kept around during the entire process, so
* that the measured performance is representative of applications
* that maintain some live in-memory data. One of these is a tree
* containing many pointers. The other is a large array containing
* double precision floating point numbers. Both should be of comparable
* size.
*
* The results are only really meaningful together with a specification
* of how much memory was used. It is possible to trade memory for
* better time performance. This benchmark should be run in a 32 MB
* heap, though we don't currently know how to enforce that uniformly.
* Unlike the original Ellis and Kovac benchmark, we do not attempt
* measure pause times. This facility should eventually be added back
* in. There are several reasons for omitting it for now. The original
* implementation depended on assumptions about the thread scheduler
* that don't hold uniformly. The results really measure both the
* scheduler and GC. Pause time measurements tend to not fit well with
* current benchmark suites. As far as we know, none of the current
* commercial Java implementations seriously attempt to minimize GC pause
* times.
*
* Known deficiencies:
* - No way to check on memory use
* - No cyclic data structures
* - No attempt to measure variation with object size
* - Results are sensitive to locking cost, but we dont
* check for proper locking
*/
package org.zeroxlab.gc;
import org.zeroxlab.benchmark.TesterGC;
import android.os.Message;
class Node {
Node left, right;
int i, j;
Node(Node l, Node r) { left = l; right = r; }
Node() { }
}
public class GCBenchmark {
public static final int kStretchTreeDepth = 16; // about 8Mb
public static final int kLongLivedTreeDepth = 14; // about 2Mb
public static final int kArraySize = 125000; // about 2Mb
public static final int kMinTreeDepth = 2;
public static final int kMaxTreeDepth = 8;
public static StringBuffer out = new StringBuffer();
static void update(String s){
out.append(s+"\n");
Message m = new Message();
m.what = TesterGC.GUINOTIFIER;
TesterGC.mHandler.sendMessage(m);
}
// Nodes used by a tree of a given size
static int TreeSize(int i) {
return ((1 << (i + 1)) - 1);
}
// Number of iterations to use for a given tree depth
static int NumIters(int i) {
return 2 * TreeSize(kStretchTreeDepth) / TreeSize(i);
}
// Build tree top down, assigning to older objects.
static void Populate(int iDepth, Node thisNode) {
if (iDepth<=0) {
return;
} else {
iDepth--;
thisNode.left = new Node();
thisNode.right = new Node();
Populate (iDepth, thisNode.left);
Populate (iDepth, thisNode.right);
}
}
// Build tree bottom-up
static Node MakeTree(int iDepth) {
if (iDepth<=0) {
return new Node();
} else {
return new Node(MakeTree(iDepth-1),
MakeTree(iDepth-1));
}
}
static void PrintDiagnostics() {
long lFreeMemory = Runtime.getRuntime().freeMemory();
long lTotalMemory = Runtime.getRuntime().totalMemory();
update("*Total memory:"
+ lTotalMemory + " bytes");
update("*Free memory:" + lFreeMemory + " bytes\n");
}
static void TimeConstruction(int depth) {
Node root;
long tStart, tFinish;
int iNumIters = NumIters(depth);
Node tempTree;
update("Create " + iNumIters +
" trees of depth " + depth);
tStart = System.currentTimeMillis();
for (int i = 0; i < iNumIters; ++i) {
tempTree = new Node();
Populate(depth, tempTree);
tempTree = null;
}
tFinish = System.currentTimeMillis();
update("- Top down: "
+ (tFinish - tStart) + "msecs");
tStart = System.currentTimeMillis();
for (int i = 0; i < iNumIters; ++i) {
tempTree = MakeTree(depth);
tempTree = null;
}
tFinish = System.currentTimeMillis();
update("- Bottom up: "
+ (tFinish - tStart) + "msecs");
}
public static void benchmark() {
out = new StringBuffer();
Node root;
Node longLivedTree;
Node tempTree;
long tStart, tFinish;
long tElapsed;
// Debug.startMethodTracing("gcbench");
// update("Garbage Collector Test");
update(
"Stretching memory:\n binary tree of depth "
+ kStretchTreeDepth);
PrintDiagnostics();
tStart = System.currentTimeMillis();
// Stretch the memory space quickly
tempTree = MakeTree(kStretchTreeDepth);
tempTree = null;
// Create a long lived object
update(
"Creating:\n long-lived binary tree of depth " +
kLongLivedTreeDepth);
longLivedTree = new Node();
Populate(kLongLivedTreeDepth, longLivedTree);
// Create long-lived array, filling half of it
update(
" long-lived array of "
+ kArraySize + " doubles");
double array[] = new double[kArraySize];
for (int i = 0; i < kArraySize/2; ++i) {
array[i] = 1.0/i;
}
PrintDiagnostics();
for (int d = kMinTreeDepth; d <= kMaxTreeDepth; d += 2) {
TimeConstruction(d);
}
if (longLivedTree == null || array[1000] != 1.0/1000)
update("Failed");
// fake reference to LongLivedTree
// and array
// to keep them from being optimized away
tFinish = System.currentTimeMillis();
tElapsed = tFinish-tStart;
PrintDiagnostics();
update("Completed in " + tElapsed + "ms.");
TesterGC.time = tElapsed;
//Debug.stopMethodTracing();
}
}