blob: 99b2495c03c9886f7f05f7b481e22b8b48ce3d0e [file] [log] [blame]
package perf;
import java.io.*;
import java.nio.charset.Charset;
import java.util.*;
import org.codehaus.jackson.*;
import org.codehaus.jackson.io.IOContext;
import org.codehaus.jackson.map.*;
import org.codehaus.jackson.smile.SmileFactory;
import org.codehaus.jackson.util.BufferRecycler;
// json.org's reference implementation
import org.json.*;
// jackson-4-org.json
import com.fasterxml.jackson.module.jsonorg.JsonOrgModule;
// json-smart
import net.minidev.json.JSONObject;
import net.minidev.json.JSONValue;
@SuppressWarnings("unused")
public final class TestJsonPerf
{
private final int REPS;
private final static int TEST_PER_GC = 15;
final JsonFactory _jsonFactory;
final ObjectMapper _mapper;
final ObjectMapper _smileMapper;
final SmileFactory _smileFactory;
final byte[] _jsonData;
final String _jsonString;
final byte[] _smileData;
protected int _batchSize;
public TestJsonPerf(File f) throws IOException
{
_jsonFactory = new JsonFactory();
_mapper = new ObjectMapper(_jsonFactory);
_mapper.registerModule(new JsonOrgModule());
_smileFactory = new SmileFactory();
_smileMapper = new ObjectMapper(_smileFactory);
_jsonData = readData(f);
_jsonString = new String(_jsonData, "UTF-8");
_smileData = convertToSmile(_jsonData);
// Let's try to guestimate suitable size... to get to 25 megs parsed
REPS = (int) ((double) (25 * 1000 * 1000) / (double) _jsonData.length);
System.out.println("Read "+_jsonData.length+" bytes (smile: "+_smileData.length+") from '"+f+"'; will do "+REPS+" reps");
System.out.println();
}
public void test()
throws Exception
{
int i = 0;
int sum = 0;
int round = 0;
while (true) {
try { Thread.sleep(100L); } catch (InterruptedException ie) { }
++i;
// round = i % 7;
// int round = 2 + (i % 2);
round = 6;
long curr = System.currentTimeMillis();
String msg;
boolean lf = (round == 0);
switch (round) {
case 0:
msg = "Jackson, stream/byte[]"; // byte
sum += testJacksonStream(REPS, _jsonFactory, _jsonData);
break;
case 1:
msg = "Jackson, stream/String"; // String
sum += testJacksonStream(REPS, _jsonFactory, _jsonString);
break;
case 2:
msg = "Jackson, tree/byte[]";
sum += testJacksonTree(REPS, _mapper, _jsonData);
break;
case 3:
msg = "Jackson, tree/String";
sum += testJacksonTree(REPS, _mapper, _jsonString);
break;
case 4:
msg = "Jackson, Object/byte[]";
sum += testJacksonObject(REPS, _mapper, _jsonData);
break;
case 5:
msg = "Jackson, Object/String";
sum += testJacksonObject(REPS, _mapper, _jsonString);
break;
case 6:
msg = "Json-smart/String";
sum += testJsonSmart(REPS, _jsonData, _jsonString);
break;
/*
case 3:
msg = "Jackson + module-org-json";
sum += testJsonOrgViaJackson(REPS);
break;
case 4:
msg = "Json.org";
sum += testJsonOrg(REPS);
break;
case 0:
msg = "Jackson/smile, stream";
sum += testJacksonStream(REPS, _smileFactory, _smileData, true);
break;
case 0:
msg = "Smile/data-bind";
sum += testJacksonDatabind(_smileMapper, _smileData, REPS);
break;
case 1:
msg = "Jackson/data-bind";
sum += testJacksonDatabind(_mapper, _jsonData, REPS);
break;
case 1:
msg = "Jackson, stream/byte";
sum += testJacksonStream(REPS, _jsonFactory, _jsonData, true);
break;
case 2:
msg = "Jackson, stream/char";
sum += testJacksonStream(REPS, _jsonFactory, _jsonData, false);
break;
*/
default:
throw new Error("Internal error");
}
curr = System.currentTimeMillis() - curr;
if (lf) {
System.out.println();
}
System.out.println("Test '"+msg+"' -> "+curr+" msecs"
// +"("+(sum & 0xFF)+")"
);
if ((i % TEST_PER_GC) == 0) {
System.out.println("[GC]");
try { Thread.sleep(100L); } catch (InterruptedException ie) { }
System.gc();
try { Thread.sleep(100L); } catch (InterruptedException ie) { }
}
}
}
private final byte[] readData(File f)
throws IOException
{
int len = (int) f.length();
byte[] data = new byte[len];
int offset = 0;
FileInputStream fis = new FileInputStream(f);
while (len > 0) {
int count = fis.read(data, offset, len-offset);
offset += count;
len -= count;
}
return data;
}
private byte[] convertToSmile(byte[] json) throws IOException
{
JsonParser jp = _jsonFactory.createJsonParser(json);
ByteArrayOutputStream out = new ByteArrayOutputStream(200);
System.out.println("Converting and verifying Smile data...");
JsonGenerator jg = _smileFactory.createJsonGenerator(out);
while (jp.nextToken() != null) {
jg.copyCurrentEvent(jp);
}
jp.close();
jg.close();
byte[] smileBytes = out.toByteArray();
System.out.println("Written as "+smileBytes.length+" Smile bytes from "+json.length+" JSON bytes; will verify correctness");
// One more thing: let's actually verify correctness!
JsonParser sp = _smileFactory.createJsonParser(new ByteArrayInputStream(smileBytes));
jp = _jsonFactory.createJsonParser(json);
while (true) {
JsonToken t1 = jp.nextToken();
JsonToken t2;
try {
t2 = sp.nextToken();
} catch (IOException ioe) {
System.err.println("WARN: problem for token matching input token "+t1+" at "+jp.getCurrentLocation());
throw ioe;
}
if (t1 != t2) {
throw new IllegalArgumentException("Error: tokens differ (json: "+t1+", smile "+t2+") at "+jp.getCurrentLocation());
}
if (t1 == null) break;
if (t1.isScalarValue() || t1 == JsonToken.FIELD_NAME) {
String str1 = jp.getText();
String str2 = jp.getText();
if (str1 == null) {
throw new IllegalArgumentException("Error: token texts differ (json: null, smile '"+str2+"') at "+jp.getCurrentLocation());
} else if (!str1.equals(str2)) {
throw new IllegalArgumentException("Error: token texts differ (json: '"+str1+"', smile '"+str2+"') at "+jp.getCurrentLocation());
}
}
}
System.out.println("Verified Smile data ("+smileBytes.length+"): same as JSON ("+json.length+")");
return smileBytes;
}
protected int testJsonOrg(int reps)
throws Exception
{
Object ob = null;
// Json.org's code only accepts Strings:
String input = new String(_jsonData, "UTF-8");
for (int i = 0; i < reps; ++i) {
JSONTokener tok = new JSONTokener(input);
ob = tok.nextValue();
}
return ob.hashCode();
}
protected int testJsonOrgViaJackson(int reps) throws Exception
{
JSONObject ob = null;
for (int i = 0; i < reps; ++i) {
ob = _mapper.readValue(_jsonData, JSONObject.class);
}
return ob.hashCode();
}
private int testJacksonStream(int reps, JsonFactory factory, byte[] data) throws Exception
{
int sum = 0;
Object ob = null;
for (int i = 0; i < reps; ++i) {
JsonParser jp = factory.createJsonParser(data, 0, data.length);
ob = mapStreamToObject(jp, jp.nextToken());
jp.close();
}
return ob.hashCode();
}
private int testJacksonStream(int reps, JsonFactory factory, String data) throws Exception
{
int sum = 0;
Object ob = null;
for (int i = 0; i < reps; ++i) {
JsonParser jp = factory.createJsonParser(data);
ob = mapStreamToObject(jp, jp.nextToken());
jp.close();
}
return ob.hashCode();
}
private Object mapStreamToObject(JsonParser jp, JsonToken t) throws IOException
{
switch (t) {
case VALUE_FALSE:
return Boolean.FALSE;
case VALUE_TRUE:
return Boolean.TRUE;
case VALUE_STRING:
return jp.getText();
case VALUE_NUMBER_INT:
case VALUE_NUMBER_FLOAT:
return jp.getNumberValue();
case VALUE_EMBEDDED_OBJECT:
return jp.getEmbeddedObject();
case START_ARRAY:
{
ArrayList<Object> kids = new ArrayList<Object>(4);
while ((t = jp.nextToken()) != JsonToken.END_ARRAY) {
kids.add(mapStreamToObject(jp, t));
}
return kids;
}
case START_OBJECT:
{
// HashMap<String, Object> kids = new LinkedHashMap<String, Object>();
HashMap<String, Object> kids = new HashMap<String, Object>(8);
while ((t = jp.nextToken()) != JsonToken.END_OBJECT) {
kids.put(jp.getCurrentName(), mapStreamToObject(jp, jp.nextToken()));
}
return kids;
}
default:
throw new RuntimeException("Unexpected token: "+t);
}
}
private int testJacksonDatabind(int reps, ObjectMapper mapper, byte[] data)
throws Exception
{
Object ob = null;
for (int i = 0; i < reps; ++i) {
// This is "untyped"... Maps, Lists etc
ob = mapper.readValue(data, Object.class);
}
return ob.hashCode(); // just to get some non-optimizable number
}
private int testJacksonTree(int reps, ObjectMapper mapper, byte[] data)
throws Exception
{
Object ob = null;
for (int i = 0; i < reps; ++i) {
ob = mapper.readValue(data, JsonNode.class);
/*
ByteArrayInputStream in = new ByteArrayInputStream(data);
ob = mapper.readValue(in, JsonNode.class);
*/
}
return ob.hashCode(); // just to get some non-optimizable number
}
private int testJacksonTree(int reps, ObjectMapper mapper, String data)
throws Exception
{
JsonNode tree = null;
// Object ob = null;
for (int i = 0; i < reps; ++i) {
// ob = mapper.readValue(data, JsonNode.class);
tree = mapper.readTree(data);
}
return tree.hashCode(); // just to get some non-optimizable number
}
private int testJacksonObject(int reps, ObjectMapper mapper, byte[] data)
throws Exception
{
Object ob = null;
for (int i = 0; i < reps; ++i) {
ob = mapper.readValue(data, Object.class);
}
return ob.hashCode(); // just to get some non-optimizable number
}
private int testJacksonObject(int reps, ObjectMapper mapper, String data)
throws Exception
{
Object ob = null;
for (int i = 0; i < reps; ++i) {
ob = mapper.readValue(data, Object.class);
}
return ob.hashCode(); // just to get some non-optimizable number
}
private int testJsonSmart(int reps, byte[] dataBytes, String dataString)
throws Exception
{
JSONObject root = null;
final Charset utf8 = Charset.forName("UTF-8");
for (int i = 0; i < reps; ++i) {
// ob = mapper.readValue(data, JsonNode.class);
String str = new String(dataBytes, utf8);
root = (JSONObject) JSONValue.parse(str);
}
return root.hashCode();
}
public static void main(String[] args)
throws Exception
{
if (args.length != 1) {
System.err.println("Usage: java ... <file>");
System.exit(1);
}
new TestJsonPerf(new File(args[0])).test();
}
}