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 17 package benchmarks.regression; 18 19 import com.google.caliper.Param; 20 import com.google.caliper.Runner; 21 import com.google.caliper.SimpleBenchmark; 22 import java.io.IOException; 23 import java.io.InputStream; 24 import java.io.InputStreamReader; 25 import java.io.Reader; 26 import java.io.StringReader; 27 import java.io.StringWriter; 28 import javax.xml.parsers.DocumentBuilderFactory; 29 import javax.xml.parsers.SAXParserFactory; 30 import org.json.JSONArray; 31 import org.json.JSONObject; 32 import org.xml.sax.InputSource; 33 import org.xml.sax.helpers.DefaultHandler; 34 import 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 */ 43 public 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") { newParser()56 @Override Parser newParser() { 57 return new AndroidStreamParser(); 58 } 59 }, 60 ORG_JSON("json") { newParser()61 @Override Parser newParser() { 62 return new OrgJsonParser(); 63 } 64 }, 65 XML_PULL("xml") { newParser()66 @Override Parser newParser() { 67 return new GeneralXmlPullParser(); 68 } 69 }, 70 XML_DOM("xml") { newParser()71 @Override Parser newParser() { 72 return new XmlDomParser(); 73 } 74 }, 75 XML_SAX("xml") { newParser()76 @Override Parser newParser() { 77 return new XmlSaxParser(); 78 } 79 }; 80 81 final String extension; 82 Api(String extension)83 private Api(String extension) { 84 this.extension = extension; 85 } 86 newParser()87 abstract Parser newParser(); 88 } 89 90 private String text; 91 private Parser parser; 92 setUp()93 @Override protected void setUp() throws Exception { 94 text = resourceToString("/" + document.name() + "." + api.extension); 95 parser = api.newParser(); 96 } 97 timeParse(int reps)98 public void timeParse(int reps) throws Exception { 99 for (int i = 0; i < reps; i++) { 100 parser.parse(text); 101 } 102 } 103 main(String... args)104 public static void main(String... args) throws Exception { 105 Runner.main(ParseBenchmark.class, args); 106 } 107 resourceToString(String path)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 { parse(String data)126 void parse(String data) throws Exception; 127 } 128 129 private static class AndroidStreamParser implements Parser { parse(String data)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 readObject(android.util.JsonReader reader)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 readArray(android.util.JsonReader reader)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 readToken(android.util.JsonReader reader)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 { parse(String data)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 { parse(String data)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 { parse(String data)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 { parse(String data)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