blob: 2d951bb9d9af62e9984aececa19bc8c1754723b6 [file] [log] [blame]
cowtowncoderba364612008-03-24 05:59:43 +00001import java.io.*;
2
3import org.codehaus.jackson.*;
4import org.codehaus.jackson.io.IOContext;
cowtowncoder3f1dee82009-02-19 20:11:55 +00005import org.codehaus.jackson.map.*;
cowtowncoder2f4d63b2010-06-20 04:07:16 +00006import org.codehaus.jackson.smile.SmileFactory;
cowtowncoderba364612008-03-24 05:59:43 +00007import org.codehaus.jackson.util.BufferRecycler;
8
9// json.org's reference implementation
10import org.json.*;
cowtowncoderba364612008-03-24 05:59:43 +000011// Jsontool implementation
12import com.sdicons.json.parser.JSONParser;
13// Noggit:
14//import org.apache.noggit.JSONParser;
15
cowtowncoder87b24812010-07-01 07:00:21 +000016@SuppressWarnings("unused")
cowtowncoderba364612008-03-24 05:59:43 +000017public final class TestJsonPerf
18{
cowtowncoder6c77e7d2009-01-14 00:41:12 +000019 private final int REPS;
cowtowncoderba364612008-03-24 05:59:43 +000020
cowtowncoder6c77e7d2009-01-14 00:41:12 +000021 private final static int TEST_PER_GC = 15;
cowtowncoderba364612008-03-24 05:59:43 +000022
cowtowncoder2f4d63b2010-06-20 04:07:16 +000023 final JsonFactory _jsonFactory;
24
25 final ObjectMapper _mapper;
cowtowncoderba364612008-03-24 05:59:43 +000026
cowtowncoder2ed3a192010-06-30 17:13:07 +000027 final ObjectMapper _smileMapper;
28
cowtowncoder2f4d63b2010-06-20 04:07:16 +000029 final SmileFactory _smileFactory;
30
31 final byte[] _jsonData;
cowtowncoderba364612008-03-24 05:59:43 +000032
cowtowncoder2f4d63b2010-06-20 04:07:16 +000033 final byte[] _smileData;
34
cowtowncoderba364612008-03-24 05:59:43 +000035 protected int mBatchSize;
36
cowtowncoder2f4d63b2010-06-20 04:07:16 +000037 public TestJsonPerf(File f) throws IOException
cowtowncoderba364612008-03-24 05:59:43 +000038 {
cowtowncoder2f4d63b2010-06-20 04:07:16 +000039 _jsonFactory = new JsonFactory();
40 _mapper = new ObjectMapper(_jsonFactory);
41 _smileFactory = new SmileFactory();
cowtowncoder2ed3a192010-06-30 17:13:07 +000042 _smileMapper = new ObjectMapper(_smileFactory);
cowtowncoder2f4d63b2010-06-20 04:07:16 +000043 _jsonData = readData(f);
44 _smileData = convertToSmile(_jsonData);
cowtowncoderba364612008-03-24 05:59:43 +000045
cowtowncoderc8cf6ad2009-01-14 23:52:52 +000046 // Let's try to guestimate suitable size... to get to 50 megs parsed
cowtowncoder2f4d63b2010-06-20 04:07:16 +000047 REPS = (int) ((double) (50 * 1000 * 1000) / (double) _jsonData.length);
cowtowncoder6c77e7d2009-01-14 00:41:12 +000048
cowtowncoder2f4d63b2010-06-20 04:07:16 +000049 System.out.println("Read "+_jsonData.length+" bytes (smile: "+_smileData.length+") from '"+f+"'; will do "+REPS+" reps");
cowtowncoderba364612008-03-24 05:59:43 +000050 System.out.println();
51 }
52
53 public void test()
54 throws Exception
55 {
56 int i = 0;
57 int sum = 0;
58
59 while (true) {
60 try { Thread.sleep(100L); } catch (InterruptedException ie) { }
cowtowncoder6c77e7d2009-01-14 00:41:12 +000061 // Use 9 to test all...
cowtowncodercb0f3222010-07-23 05:56:05 +000062 int round = (i++ % 2);
cowtowncodercf85f3b2008-04-07 05:20:29 +000063
cowtowncoderba364612008-03-24 05:59:43 +000064 long curr = System.currentTimeMillis();
65 String msg;
cowtowncoder6c77e7d2009-01-14 00:41:12 +000066 boolean lf = (round == 0);
cowtowncoderba364612008-03-24 05:59:43 +000067
68 switch (round) {
cowtowncoderdea5fad2009-02-18 06:34:49 +000069
cowtowncoderba364612008-03-24 05:59:43 +000070 case 0:
cowtowncoder2ed3a192010-06-30 17:13:07 +000071 msg = "Smile/data-bind";
72 sum += testJacksonDatabind(_smileMapper, _smileData, REPS);
73 break;
74
75 case 1:
76 msg = "Jackson/data-bind";
77 sum += testJacksonDatabind(_mapper, _jsonData, REPS);
78 break;
79
80 /*
81 case 0:
cowtowncoder2f4d63b2010-06-20 04:07:16 +000082 msg = "Jackson/smile, stream";
83 sum += testJacksonStream(REPS, _smileFactory, _smileData, true);
84 break;
cowtowncoder1f4cc722010-06-21 03:18:07 +000085 case 1:
86 msg = "Jackson, stream/byte";
87 sum += testJacksonStream(REPS, _jsonFactory, _jsonData, true);
88 break;
89 case 2:
90 msg = "Jackson, stream/char";
91 sum += testJacksonStream(REPS, _jsonFactory, _jsonData, false);
cowtowncoderba364612008-03-24 05:59:43 +000092 break;
cowtowncoder6c77e7d2009-01-14 00:41:12 +000093
cowtowncoder1f4cc722010-06-21 03:18:07 +000094 case 3:
cowtowncoderba364612008-03-24 05:59:43 +000095 msg = "Jackson, Java types";
cowtowncoder2ed3a192010-06-30 17:13:07 +000096 sum += testJacksonDatabind(_mapper, REPS);
cowtowncoderba364612008-03-24 05:59:43 +000097 break;
cowtowncoder6c77e7d2009-01-14 00:41:12 +000098
cowtowncoder1f4cc722010-06-21 03:18:07 +000099 case 4:
cowtowncoder2f4d63b2010-06-20 04:07:16 +0000100 msg = "Jackson, JSON types";
101 sum += testJacksonJsonTypes(_mapper, REPS);
102 break;
cowtowncoder1f4cc722010-06-21 03:18:07 +0000103
104 case 5:
105 msg = "Noggit";
106 sum += testNoggit(REPS);
107 break;
108
cowtowncoder2f4d63b2010-06-20 04:07:16 +0000109 case 6:
cowtowncoderba364612008-03-24 05:59:43 +0000110 msg = "Json.org";
111 sum += testJsonOrg(REPS);
112 break;
cowtowncoder2f4d63b2010-06-20 04:07:16 +0000113 case 7:
cowtowncoder6c77e7d2009-01-14 00:41:12 +0000114 msg = "Json-simple";
115 sum += testJsonSimple(REPS);
116 break;
cowtowncoder2f4d63b2010-06-20 04:07:16 +0000117 case 8:
cowtowncoderba364612008-03-24 05:59:43 +0000118 msg = "JSONTools (berlios.de)";
119 sum += testJsonTools(REPS);
120 break;
cowtowncoder2ed3a192010-06-30 17:13:07 +0000121 */
cowtowncoderba364612008-03-24 05:59:43 +0000122 default:
123 throw new Error("Internal error");
124 }
125
126 curr = System.currentTimeMillis() - curr;
127 if (lf) {
128 System.out.println();
129 }
130 System.out.println("Test '"+msg+"' -> "+curr+" msecs ("
131 +(sum & 0xFF)+").");
132
133
134 if ((i % TEST_PER_GC) == 0) {
135 System.out.println("[GC]");
136 try { Thread.sleep(100L); } catch (InterruptedException ie) { }
137 System.gc();
138 try { Thread.sleep(100L); } catch (InterruptedException ie) { }
139 }
140 }
141 }
142
143 private final byte[] readData(File f)
144 throws IOException
145 {
146 int len = (int) f.length();
147 byte[] data = new byte[len];
148 int offset = 0;
149 FileInputStream fis = new FileInputStream(f);
150
151 while (len > 0) {
152 int count = fis.read(data, offset, len-offset);
153 offset += count;
154 len -= count;
155 }
156
157 return data;
158 }
159
cowtowncoder2f4d63b2010-06-20 04:07:16 +0000160 private byte[] convertToSmile(byte[] json) throws IOException
161 {
162 JsonParser jp = _jsonFactory.createJsonParser(json);
163 ByteArrayOutputStream out = new ByteArrayOutputStream(200);
cowtowncodera4f58652010-06-28 04:42:07 +0000164 System.out.println("Converting and verifying Smile data...");
cowtowncoder2f4d63b2010-06-20 04:07:16 +0000165 JsonGenerator jg = _smileFactory.createJsonGenerator(out);
166 while (jp.nextToken() != null) {
cowtowncodera4f58652010-06-28 04:42:07 +0000167 jg.copyCurrentEvent(jp);
cowtowncoder2f4d63b2010-06-20 04:07:16 +0000168 }
169 jp.close();
170 jg.close();
cowtowncodera4f58652010-06-28 04:42:07 +0000171 byte[] smileBytes = out.toByteArray();
cowtowncoder12e98552010-07-25 03:38:27 +0000172 System.out.println("Written as "+smileBytes.length+" Smile bytes from "+json.length+" JSON bytes; will verify correctness");
cowtowncodera4f58652010-06-28 04:42:07 +0000173
174 // One more thing: let's actually verify correctness!
175 JsonParser sp = _smileFactory.createJsonParser(new ByteArrayInputStream(smileBytes));
176 jp = _jsonFactory.createJsonParser(json);
177 while (true) {
178 JsonToken t1 = jp.nextToken();
179 JsonToken t2;
180 try {
181 t2 = sp.nextToken();
182 } catch (IOException ioe) {
183 System.err.println("WARN: problem for token matching input token "+t1+" at "+jp.getCurrentLocation());
184 throw ioe;
185 }
186 if (t1 != t2) {
187 throw new IllegalArgumentException("Error: tokens differ (json: "+t1+", smile "+t2+") at "+jp.getCurrentLocation());
188 }
189 if (t1 == null) break;
190 if (t1.isScalarValue() || t1 == JsonToken.FIELD_NAME) {
191 String str1 = jp.getText();
192 String str2 = jp.getText();
193 if (str1 == null) {
194 throw new IllegalArgumentException("Error: token texts differ (json: null, smile '"+str2+"') at "+jp.getCurrentLocation());
195 } else if (!str1.equals(str2)) {
196 throw new IllegalArgumentException("Error: token texts differ (json: '"+str1+"', smile '"+str2+"') at "+jp.getCurrentLocation());
197 }
198 }
199 }
200 System.out.println("Verified Smile data ("+smileBytes.length+"): same as JSON ("+json.length+")");
201 return smileBytes;
cowtowncoder2f4d63b2010-06-20 04:07:16 +0000202 }
203
cowtowncoderba364612008-03-24 05:59:43 +0000204 protected int testJsonOrg(int reps)
205 throws Exception
206 {
207 Object ob = null;
cowtowncoder6c77e7d2009-01-14 00:41:12 +0000208 // Json.org's code only accepts Strings:
cowtowncoder2f4d63b2010-06-20 04:07:16 +0000209 String input = new String(_jsonData, "UTF-8");
cowtowncoderba364612008-03-24 05:59:43 +0000210 for (int i = 0; i < reps; ++i) {
cowtowncoderba364612008-03-24 05:59:43 +0000211 JSONTokener tok = new JSONTokener(input);
212 ob = tok.nextValue();
213 }
214 return ob.hashCode();
215 }
216
cowtowncoder2f4d63b2010-06-20 04:07:16 +0000217 private int testJsonTools(int reps)
cowtowncoderba364612008-03-24 05:59:43 +0000218 throws Exception
219 {
220 Object ob = null;
221 for (int i = 0; i < reps; ++i) {
222 // Json-tools accepts streams, yay!
cowtowncoder2f4d63b2010-06-20 04:07:16 +0000223 JSONParser jp = new JSONParser(new ByteArrayInputStream(_jsonData), "byte stream");
cowtowncoderba364612008-03-24 05:59:43 +0000224 /* Hmmmh. Will we get just one object for the whole thing?
225 * Or a stream? Seems like just one
226 */
227 //while ((ob = jp.nextValue()) != null) { ; }
228 ob = jp.nextValue();
229 }
230 return ob.hashCode();
231 }
232
cowtowncoder2f4d63b2010-06-20 04:07:16 +0000233 private int testJsonSimple(int reps)
cowtowncoder6c77e7d2009-01-14 00:41:12 +0000234 throws Exception
235 {
236 // Json.org's code only accepts Strings:
cowtowncoder2f4d63b2010-06-20 04:07:16 +0000237 String input = new String(_jsonData, "UTF-8");
cowtowncoder6c77e7d2009-01-14 00:41:12 +0000238 Object ob = null;
239 for (int i = 0; i < reps; ++i) {
240 ob = org.json.simple.JSONValue.parse(input);
241 }
242 return ob.hashCode();
243 }
244
cowtowncoder2f4d63b2010-06-20 04:07:16 +0000245 private int testNoggit(int reps)
cowtowncoderba364612008-03-24 05:59:43 +0000246 throws Exception
247 {
cowtowncoder2f4d63b2010-06-20 04:07:16 +0000248 ByteArrayInputStream bin = new ByteArrayInputStream(_jsonData);
cowtowncoderba364612008-03-24 05:59:43 +0000249
cowtowncoder2f4d63b2010-06-20 04:07:16 +0000250 char[] cbuf = new char[_jsonData.length];
cowtowncoderba364612008-03-24 05:59:43 +0000251
cowtowncoder62f87112008-11-26 21:04:19 +0000252 IOContext ctxt = new IOContext(new BufferRecycler(), this, false);
cowtowncoderba364612008-03-24 05:59:43 +0000253 int sum = 0;
254
255 for (int i = 0; i < reps; ++i) {
256 /* This may be unfair advantage (allocating buffer of exact
257 * size)? But let's do that for now
258 */
259 //char[] cbuf = new char[mData.length];
260 //InputStreamReader r = new InputStreamReader(bin, "UTF-8");
261 byte[] bbuf = ctxt.allocReadIOBuffer();
cowtowncoder6c77e7d2009-01-14 00:41:12 +0000262 /* 13-Jan-2009, tatu: Note: Noggit doesn't use our turbo-charged
263 * UTF8 codec by default. But let's make it as fast as we
264 * possibly can...
265 */
cowtowncoderba364612008-03-24 05:59:43 +0000266 UTF8Reader r = new UTF8Reader(ctxt, bin, bbuf, 0, 0);
267
268 bin.reset();
269 org.apache.noggit.JSONParser jp = new org.apache.noggit.JSONParser(r, cbuf);
270 int type;
271 while ((type = jp.nextEvent()) != org.apache.noggit.JSONParser.EOF) {
272 if (type == org.apache.noggit.JSONParser.STRING) {
273 sum += jp.getString().length();
274 }
275 }
276 }
277 return sum;
278 }
279
cowtowncoder2f4d63b2010-06-20 04:07:16 +0000280 private int testJacksonStream(int reps, JsonFactory factory, byte[] data, boolean fast)
cowtowncoderba364612008-03-24 05:59:43 +0000281 throws Exception
282 {
283 int sum = 0;
284 for (int i = 0; i < reps; ++i) {
cowtowncoder41829362008-08-06 03:49:14 +0000285 // note: fast is not used any more
cowtowncoderc8cf6ad2009-01-14 23:52:52 +0000286 JsonParser jp;
287
288 if (fast) {
cowtowncoder2f4d63b2010-06-20 04:07:16 +0000289 jp = factory.createJsonParser(data, 0, data.length);
cowtowncoderc8cf6ad2009-01-14 23:52:52 +0000290 } else {
cowtowncoder2f4d63b2010-06-20 04:07:16 +0000291 jp = factory.createJsonParser(new ByteArrayInputStream(data));
cowtowncoderc8cf6ad2009-01-14 23:52:52 +0000292 }
cowtowncoderba364612008-03-24 05:59:43 +0000293 JsonToken t;
294 while ((t = jp.nextToken()) != null) {
cowtowncodera4f58652010-06-28 04:42:07 +0000295/*
296if (t == JsonToken.FIELD_NAME) System.err.println("'"+jp.getCurrentName()+"'");
297else System.err.println(""+t);
298*/
cowtowncoderba364612008-03-24 05:59:43 +0000299 // Field names are always constructed
cowtowncodercf85f3b2008-04-07 05:20:29 +0000300 if (t == JsonToken.VALUE_STRING
301 //|| t == JsonToken.FIELD_NAME
302 ) {
cowtowncoderba364612008-03-24 05:59:43 +0000303 sum += jp.getText().length();
304 }
305 }
306 jp.close();
307 }
308 return sum;
309 }
310
cowtowncoder2ed3a192010-06-30 17:13:07 +0000311 private int testJacksonDatabind(ObjectMapper mapper, byte[] data, int reps)
cowtowncoderba364612008-03-24 05:59:43 +0000312 throws Exception
313 {
314 Object ob = null;
cowtowncoderba364612008-03-24 05:59:43 +0000315 for (int i = 0; i < reps; ++i) {
cowtowncoder6c77e7d2009-01-14 00:41:12 +0000316 // This is "untyped"... Maps, Lists etc
cowtowncoder2ed3a192010-06-30 17:13:07 +0000317 ob = mapper.readValue(data, 0, data.length, Object.class);
cowtowncoderba364612008-03-24 05:59:43 +0000318 }
319 return ob.hashCode(); // just to get some non-optimizable number
320 }
321
cowtowncoder2ed3a192010-06-30 17:13:07 +0000322 private int testJacksonTree(ObjectMapper mapper, byte[] data, int reps)
cowtowncoderba364612008-03-24 05:59:43 +0000323 throws Exception
324 {
325 Object ob = null;
cowtowncoderba364612008-03-24 05:59:43 +0000326 for (int i = 0; i < reps; ++i) {
cowtowncoder2ed3a192010-06-30 17:13:07 +0000327 ob = mapper.readValue(data, 0, data.length, JsonNode.class);
cowtowncoderba364612008-03-24 05:59:43 +0000328 }
329 return ob.hashCode(); // just to get some non-optimizable number
330 }
331
332 public static void main(String[] args)
333 throws Exception
334 {
335 if (args.length != 1) {
336 System.err.println("Usage: java ... <file>");
337 System.exit(1);
338 }
339 new TestJsonPerf(new File(args[0])).test();
340 }
341}
342