test.TestTypedSpeed Maven / Gradle / Ivy
Show all versions of aalto-xml Show documentation
package test;
import java.io.*;
import javax.xml.stream.*;
import org.codehaus.stax2.*;
/**
* Simple typed information access stress test, useful for profiling, as well
* as for
* quickly checking high-level performance effects of changes (albeit
* not very accurately, obviously -- need longer running composite
* tests for such verifications).
*
* Type of data is auto-detected, and is assumed to be homogenous. Basically,
* data is either within attributes, or as element content, but not both.
* In either case structure should be shallow, with the root and only
* immediate leaf-level elements containing attribute or element data.
* Type of this data is auto-detected from the first instance; data must
* be in canonical format to be properly recognized (non-first values
* can be non-canonical).
*/
public class TestTypedSpeed
implements XMLStreamConstants
{
/**
* Number of repetitions to run per test. Dynamically variable,
* based on observed runtime, to try to keep it high enough.
*/
private int REPS;
private final static int TEST_PER_GC = 7;
final static int TYPE_BOOLEAN = 1;
final static int TYPE_INT = 1;
/**
* Let's keep per-run times above 50 milliseconds
*/
//final static int MIN_RUN_TIME = 50;
final static int MIN_RUN_TIME = 5;
/**
* Let's keep per-run times below 300 milliseconds
*/
//final static int MAX_RUN_TIME = 300;
final static int MAX_RUN_TIME = 1000;
final XMLInputFactory mInputFactory;
final ByteArrayInputStream mIn;
/**
* Data in attributes? If true, yes; if no, in elements
*/
boolean mUseAttr;
int mType;
private TestTypedSpeed(byte[] data)
{
mInputFactory = new com.fasterxml.aalto.stax.InputFactoryImpl();
mInputFactory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.FALSE);
mInputFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.TRUE);
// Just in case:
mInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
mIn = new ByteArrayInputStream(data);
// Ok how should we guestimate speed... perhaps from data size?
REPS = 100 + ((8 * 1000 * 1000) / data.length);
System.out.println("Based on size, will use "+REPS+" repetitions");
}
protected int test()
throws Exception
{
/* First things first: let's determine how data is stored,
* as well as what's the first (canonical) value
*/
mIn.reset();
String firstValue = findFirstValue(mIn); // also sets/clear 'mUseAttr' flag
if (mUseAttr) {
REPS += REPS;
System.out.println("(data stored as attributes: doubling REPS to "+REPS+")");
} else {
System.out.println("(data stored as element content)");
}
// But how about type?
mType = 0;
if ("true".equals(firstValue) || "false".equals(firstValue)) {
mType = TYPE_BOOLEAN;
System.out.println("Type detected as: BOOLEAN");
} else {
try {
/*int nr =*/ Integer.parseInt(firstValue);
// Ok, was a valid int
mType = TYPE_BOOLEAN;
System.out.println("Type detected as: INT");
} catch (NumberFormatException nex) { }
}
if (mType == 0) {
throw new IllegalArgumentException("Can not auto-detect type from value '"+firstValue+"'");
}
return test2();
}
private String findFirstValue(InputStream in)
throws XMLStreamException
{
XMLStreamReader sr = mInputFactory.createXMLStreamReader(in);
sr.nextTag();
sr.nextTag();
String value;
if (sr.getAttributeCount() > 0) { // attributes
mUseAttr = true;
value = sr.getAttributeValue(0);
} else {
mUseAttr = false;
value = sr.getElementText();
}
sr.close();
return value;
}
private int test2()
throws Exception
{
int i = 0;
int total = 0;
final int TEST_CASES = 2;
while (true) {
try { Thread.sleep(150L); } catch (InterruptedException ie) { }
int round = (i++ % TEST_CASES);
long now = System.currentTimeMillis();
String msg;
int sum = 0;
switch (round) {
case 0:
msg = "Access using Stax 1.0";
switch (mType) {
case TYPE_BOOLEAN:
sum = mUseAttr ? testUntypedBooleanAttr(REPS) : testUntypedBooleanElem(REPS);
break;
/*
case TYPE_INT:
sum = mUseAttr ? testUntypedIntAttr(REPS) : testUntypedIntElem(REPS);
break;
*/
default:
throw new Error("Internal error");
}
break;
case 1:
msg = "Access using Stax2 Typed API";
switch (mType) {
case TYPE_BOOLEAN:
sum = mUseAttr ? testTypedBooleanAttr(REPS) : testTypedBooleanElem(REPS);
break;
/*
case TYPE_INT:
sum = mUseAttr ? testTypedIntAttr(REPS) : testTypedIntElem(REPS);
break;
*/
default:
throw new Error("Internal error");
}
break;
default:
throw new Error("Internal error");
}
now = System.currentTimeMillis() - now;
if (round == 0) {
System.out.println();
}
System.out.println("Test '"+msg+"' -> "+now+" msecs ("
+sum+" -> "+(total & 0xFF)+").");
total += sum;
if ((i % TEST_PER_GC) == 0) {
System.out.println("[GC]");
try { Thread.sleep(100L); } catch (InterruptedException ie) { }
System.gc();
try { Thread.sleep(200L); } catch (InterruptedException ie) { }
/* One more tweak: let's add load if things start
* running too fast or slow, to try to get sweet range
* of 50 to 250 millisseconds
*/
if (now < MIN_RUN_TIME) {
REPS += (REPS / 5); // 20% up
System.out.println("[NOTE: increasing reps, now: "+REPS+"]");
try { Thread.sleep(200L); } catch (InterruptedException ie) { }
} else if (now > MAX_RUN_TIME && i > 20) {
/* Let's reduce load slower than increase; also,
* due to initial warmup, let's not adjust until
* we've gone through a few cycles
*/
REPS -= (REPS / 10); // 10% down
System.out.println("[NOTE: decreasing reps, now: "+REPS+"]");
try { Thread.sleep(200L); } catch (InterruptedException ie) { }
}
}
}
}
/*
/////////////////////////////////////////////////////////
// Actual value type access, ones via Stax 1.0
/////////////////////////////////////////////////////////
*/
protected int testUntypedBooleanAttr(int reps)
throws Exception
{
int total = 0;
for (int i = 0; i < reps; ++i) {
XMLStreamReader2 sr = constructAndFindRoot();
while (sr.nextTag() == START_ELEMENT) {
int c = sr.getAttributeCount();
while (--c >= 0) {
String str = sr.getAttributeValue(c).trim();
if (str.equals("true")) {
++total;
} else if (str.equals("false") || str.equals("0")) {
;
} else if (str.equals("1")) {
++total;
} else {
throw new XMLStreamException("Illegal value '"+str+"', not boolean");
}
}
}
sr.close();
}
return total;
}
protected int testUntypedBooleanElem(int reps)
throws Exception
{
int total = 0;
for (int i = 0; i < reps; ++i) {
XMLStreamReader2 sr = constructAndFindRoot();
while (sr.nextTag() == START_ELEMENT) {
String str = sr.getElementText().trim();
if (str.equals("true")) {
++total;
} else if (str.equals("false") || str.equals("0")) {
;
} else if (str.equals("1")) {
++total;
} else {
throw new XMLStreamException("Illegal value '"+str+"', not boolean");
}
}
sr.close();
}
return total;
}
/*
/////////////////////////////////////////////////////////
// Actual value type access, ones via Stax2 Typed API
/////////////////////////////////////////////////////////
*/
protected int testTypedBooleanAttr(int reps)
throws Exception
{
int total = 0;
for (int i = 0; i < reps; ++i) {
XMLStreamReader2 sr = constructAndFindRoot();
while (sr.nextTag() == START_ELEMENT) {
int c = sr.getAttributeCount();
while (--c >= 0) {
if (sr.getAttributeAsBoolean(c)) {
++total;
}
}
}
sr.close();
}
return total;
}
protected int testTypedBooleanElem(int reps)
throws Exception
{
int total = 0;
for (int i = 0; i < reps; ++i) {
XMLStreamReader2 sr = constructAndFindRoot();
while (sr.nextTag() == START_ELEMENT) {
if (sr.getElementAsBoolean()) {
++total;
}
}
sr.close();
}
return total;
}
/*
/////////////////////////////////////////////////////////
// Helper methods
/////////////////////////////////////////////////////////
*/
XMLStreamReader2 constructAndFindRoot()
throws XMLStreamException
{
mIn.reset();
XMLStreamReader sr = mInputFactory.createXMLStreamReader(mIn);
if (sr.nextTag() != START_ELEMENT) {
throw new XMLStreamException("Couldn't locate root");
}
; // points to root now
return (XMLStreamReader2) sr;
}
static byte[] readData(File file)
throws IOException
{
InputStream fin = new FileInputStream(file);
byte[] buf = new byte[4000];
ByteArrayOutputStream bos = new ByteArrayOutputStream(4000);
int count;
while ((count = fin.read(buf)) > 0) {
bos.write(buf, 0, count);
}
fin.close();
return bos.toByteArray();
}
public static void main(String[] args)
throws Exception
{
if (args.length != 1) {
System.err.println("Usage: java ... ");
System.exit(1);
}
byte[] data = readData(new File(args[0]));
System.out.println(" -> "+data.length+" bytes read.");
new TestTypedSpeed(data).test();
}
}