1/* 2 * Copyright (C) 2011 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package benchmarks.regression; 18 19import com.google.caliper.Param; 20import com.google.caliper.Runner; 21import com.google.caliper.SimpleBenchmark; 22import java.io.IOException; 23import java.io.InputStream; 24import java.io.InputStreamReader; 25import java.io.Reader; 26import java.io.StringReader; 27import java.io.StringWriter; 28import javax.xml.parsers.DocumentBuilderFactory; 29import javax.xml.parsers.SAXParserFactory; 30import org.json.JSONArray; 31import org.json.JSONObject; 32import org.xml.sax.InputSource; 33import org.xml.sax.helpers.DefaultHandler; 34import org.xmlpull.v1.XmlPullParser; 35 36/** 37 * Measure throughput of various parsers. 38 * 39 * <p>This benchmark requires that ParseBenchmarkData.zip is on the classpath. 40 * That file contains Twitter feed data, which is representative of what 41 * applications will be parsing. 42 */ 43public final class ParseBenchmark extends SimpleBenchmark { 44 45 @Param Document document; 46 @Param Api api; 47 48 private enum Document { 49 TWEETS, 50 READER_SHORT, 51 READER_LONG 52 } 53 54 private enum Api { 55 ANDROID_STREAM("json") { 56 @Override Parser newParser() { 57 return new AndroidStreamParser(); 58 } 59 }, 60 ORG_JSON("json") { 61 @Override Parser newParser() { 62 return new OrgJsonParser(); 63 } 64 }, 65 XML_PULL("xml") { 66 @Override Parser newParser() { 67 return new GeneralXmlPullParser(); 68 } 69 }, 70 XML_DOM("xml") { 71 @Override Parser newParser() { 72 return new XmlDomParser(); 73 } 74 }, 75 XML_SAX("xml") { 76 @Override Parser newParser() { 77 return new XmlSaxParser(); 78 } 79 }; 80 81 final String extension; 82 83 private Api(String extension) { 84 this.extension = extension; 85 } 86 87 abstract Parser newParser(); 88 } 89 90 private String text; 91 private Parser parser; 92 93 @Override protected void setUp() throws Exception { 94 text = resourceToString("/" + document.name() + "." + api.extension); 95 parser = api.newParser(); 96 } 97 98 public void timeParse(int reps) throws Exception { 99 for (int i = 0; i < reps; i++) { 100 parser.parse(text); 101 } 102 } 103 104 public static void main(String... args) throws Exception { 105 Runner.main(ParseBenchmark.class, args); 106 } 107 108 private static String resourceToString(String path) throws Exception { 109 InputStream in = ParseBenchmark.class.getResourceAsStream(path); 110 if (in == null) { 111 throw new IllegalArgumentException("No such file: " + path); 112 } 113 114 Reader reader = new InputStreamReader(in, "UTF-8"); 115 char[] buffer = new char[8192]; 116 StringWriter writer = new StringWriter(); 117 int count; 118 while ((count = reader.read(buffer)) != -1) { 119 writer.write(buffer, 0, count); 120 } 121 reader.close(); 122 return writer.toString(); 123 } 124 125 interface Parser { 126 void parse(String data) throws Exception; 127 } 128 129 private static class AndroidStreamParser implements Parser { 130 @Override public void parse(String data) throws Exception { 131 android.util.JsonReader jsonReader 132 = new android.util.JsonReader(new StringReader(data)); 133 readToken(jsonReader); 134 jsonReader.close(); 135 } 136 137 public void readObject(android.util.JsonReader reader) throws IOException { 138 reader.beginObject(); 139 while (reader.hasNext()) { 140 reader.nextName(); 141 readToken(reader); 142 } 143 reader.endObject(); 144 } 145 146 public void readArray(android.util.JsonReader reader) throws IOException { 147 reader.beginArray(); 148 while (reader.hasNext()) { 149 readToken(reader); 150 } 151 reader.endArray(); 152 } 153 154 private void readToken(android.util.JsonReader reader) throws IOException { 155 switch (reader.peek()) { 156 case BEGIN_ARRAY: 157 readArray(reader); 158 break; 159 case BEGIN_OBJECT: 160 readObject(reader); 161 break; 162 case BOOLEAN: 163 reader.nextBoolean(); 164 break; 165 case NULL: 166 reader.nextNull(); 167 break; 168 case NUMBER: 169 reader.nextLong(); 170 break; 171 case STRING: 172 reader.nextString(); 173 break; 174 default: 175 throw new IllegalArgumentException("Unexpected token" + reader.peek()); 176 } 177 } 178 } 179 180 private static class OrgJsonParser implements Parser { 181 @Override public void parse(String data) throws Exception { 182 if (data.startsWith("[")) { 183 new JSONArray(data); 184 } else if (data.startsWith("{")) { 185 new JSONObject(data); 186 } else { 187 throw new IllegalArgumentException(); 188 } 189 } 190 } 191 192 private static class GeneralXmlPullParser implements Parser { 193 @Override public void parse(String data) throws Exception { 194 XmlPullParser xmlParser = android.util.Xml.newPullParser(); 195 xmlParser.setInput(new StringReader(data)); 196 xmlParser.nextTag(); 197 while (xmlParser.next() != XmlPullParser.END_DOCUMENT) { 198 xmlParser.getName(); 199 xmlParser.getText(); 200 } 201 } 202 } 203 204 private static class XmlDomParser implements Parser { 205 @Override public void parse(String data) throws Exception { 206 DocumentBuilderFactory.newInstance().newDocumentBuilder() 207 .parse(new InputSource(new StringReader(data))); 208 } 209 } 210 211 private static class XmlSaxParser implements Parser { 212 @Override public void parse(String data) throws Exception { 213 SAXParserFactory.newInstance().newSAXParser().parse( 214 new InputSource(new StringReader(data)), new DefaultHandler()); 215 } 216 } 217} 218