197aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes/*
297aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes * Copyright (C) 2011 Google Inc.
397aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes *
497aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes * Licensed under the Apache License, Version 2.0 (the "License");
597aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes * you may not use this file except in compliance with the License.
697aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes * You may obtain a copy of the License at
797aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes *
897aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes * http://www.apache.org/licenses/LICENSE-2.0
997aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes *
1097aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes * Unless required by applicable law or agreed to in writing, software
1197aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes * distributed under the License is distributed on an "AS IS" BASIS,
1297aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1397aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes * See the License for the specific language governing permissions and
1497aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes * limitations under the License.
1597aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes */
1697aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes
1797aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughespackage benchmarks.regression;
1897aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes
19ea13f8291a92b6f47f50011da1d5e8c107984bc3Paul Duffinimport com.google.caliper.BeforeExperiment;
2097aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughesimport com.google.caliper.Param;
2197aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughesimport java.io.IOException;
2297aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughesimport java.io.InputStream;
2397aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughesimport java.io.InputStreamReader;
2497aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughesimport java.io.Reader;
2597aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughesimport java.io.StringReader;
2697aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughesimport java.io.StringWriter;
2797aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughesimport javax.xml.parsers.DocumentBuilderFactory;
2897aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughesimport javax.xml.parsers.SAXParserFactory;
2997aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughesimport org.json.JSONArray;
3097aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughesimport org.json.JSONObject;
3197aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughesimport org.xml.sax.InputSource;
3297aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughesimport org.xml.sax.helpers.DefaultHandler;
3397aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughesimport org.xmlpull.v1.XmlPullParser;
3497aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes
3597aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes/**
3697aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes * Measure throughput of various parsers.
3797aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes *
3897aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes * <p>This benchmark requires that ParseBenchmarkData.zip is on the classpath.
3997aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes * That file contains Twitter feed data, which is representative of what
4097aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes * applications will be parsing.
4197aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes */
42ea13f8291a92b6f47f50011da1d5e8c107984bc3Paul Duffinpublic final class ParseBenchmark {
4397aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes
4497aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes    @Param Document document;
4597aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes    @Param Api api;
4697aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes
4797aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes    private enum Document {
4897aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        TWEETS,
4997aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        READER_SHORT,
5097aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        READER_LONG
5197aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes    }
5297aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes
5397aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes    private enum Api {
5497aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        ANDROID_STREAM("json") {
5597aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            @Override Parser newParser() {
5697aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                return new AndroidStreamParser();
5797aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            }
5897aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        },
5997aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        ORG_JSON("json") {
6097aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            @Override Parser newParser() {
6197aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                return new OrgJsonParser();
6297aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            }
6397aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        },
6497aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        XML_PULL("xml") {
6597aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            @Override Parser newParser() {
6697aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                return new GeneralXmlPullParser();
6797aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            }
6897aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        },
6997aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        XML_DOM("xml") {
7097aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            @Override Parser newParser() {
7197aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                return new XmlDomParser();
7297aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            }
7397aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        },
7497aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        XML_SAX("xml") {
7597aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            @Override Parser newParser() {
7697aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                return new XmlSaxParser();
7797aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            }
7897aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        };
7997aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes
8097aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        final String extension;
8197aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes
8297aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        private Api(String extension) {
8397aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            this.extension = extension;
8497aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        }
8597aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes
8697aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        abstract Parser newParser();
8797aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes    }
8897aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes
8997aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes    private String text;
9097aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes    private Parser parser;
9197aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes
92ea13f8291a92b6f47f50011da1d5e8c107984bc3Paul Duffin    @BeforeExperiment
93ea13f8291a92b6f47f50011da1d5e8c107984bc3Paul Duffin    protected void setUp() throws Exception {
9497aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        text = resourceToString("/" + document.name() + "." + api.extension);
9597aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        parser = api.newParser();
9697aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes    }
9797aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes
9897aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes    public void timeParse(int reps) throws Exception {
9997aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        for (int i = 0; i < reps; i++) {
10097aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            parser.parse(text);
10197aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        }
10297aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes    }
10397aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes
10497aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes    private static String resourceToString(String path) throws Exception {
10597aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        InputStream in = ParseBenchmark.class.getResourceAsStream(path);
10697aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        if (in == null) {
10797aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            throw new IllegalArgumentException("No such file: " + path);
10897aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        }
10997aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes
11097aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        Reader reader = new InputStreamReader(in, "UTF-8");
11197aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        char[] buffer = new char[8192];
11297aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        StringWriter writer = new StringWriter();
11397aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        int count;
11497aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        while ((count = reader.read(buffer)) != -1) {
11597aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            writer.write(buffer, 0, count);
11697aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        }
11797aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        reader.close();
11897aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        return writer.toString();
11997aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes    }
12097aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes
12197aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes    interface Parser {
12297aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        void parse(String data) throws Exception;
12397aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes    }
12497aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes
12597aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes    private static class AndroidStreamParser implements Parser {
12697aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        @Override public void parse(String data) throws Exception {
12797aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            android.util.JsonReader jsonReader
12897aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                    = new android.util.JsonReader(new StringReader(data));
12997aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            readToken(jsonReader);
13097aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            jsonReader.close();
13197aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        }
13297aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes
13397aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        public void readObject(android.util.JsonReader reader) throws IOException {
13497aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            reader.beginObject();
13597aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            while (reader.hasNext()) {
13697aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                reader.nextName();
13797aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                readToken(reader);
13897aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            }
13997aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            reader.endObject();
14097aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        }
14197aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes
14297aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        public void readArray(android.util.JsonReader reader) throws IOException {
14397aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            reader.beginArray();
14497aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            while (reader.hasNext()) {
14597aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                readToken(reader);
14697aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            }
14797aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            reader.endArray();
14897aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        }
14997aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes
15097aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        private void readToken(android.util.JsonReader reader) throws IOException {
15197aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            switch (reader.peek()) {
15297aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            case BEGIN_ARRAY:
15397aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                readArray(reader);
15497aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                break;
15597aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            case BEGIN_OBJECT:
15697aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                readObject(reader);
15797aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                break;
15897aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            case BOOLEAN:
15997aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                reader.nextBoolean();
16097aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                break;
16197aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            case NULL:
16297aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                reader.nextNull();
16397aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                break;
16497aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            case NUMBER:
16597aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                reader.nextLong();
16697aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                break;
16797aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            case STRING:
16897aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                reader.nextString();
16997aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                break;
17097aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            default:
17197aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                throw new IllegalArgumentException("Unexpected token" + reader.peek());
17297aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            }
17397aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        }
17497aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes    }
17597aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes
17697aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes    private static class OrgJsonParser implements Parser {
17797aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        @Override public void parse(String data) throws Exception {
17897aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            if (data.startsWith("[")) {
17997aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                new JSONArray(data);
18097aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            } else if (data.startsWith("{")) {
18197aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                new JSONObject(data);
18297aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            } else {
18397aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                throw new IllegalArgumentException();
18497aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            }
18597aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        }
18697aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes    }
18797aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes
18897aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes    private static class GeneralXmlPullParser implements Parser {
18997aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        @Override public void parse(String data) throws Exception {
19097aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            XmlPullParser xmlParser = android.util.Xml.newPullParser();
19197aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            xmlParser.setInput(new StringReader(data));
19297aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            xmlParser.nextTag();
19397aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            while (xmlParser.next() != XmlPullParser.END_DOCUMENT) {
19497aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                xmlParser.getName();
19597aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                xmlParser.getText();
19697aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            }
19797aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        }
19897aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes    }
19997aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes
20097aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes    private static class XmlDomParser implements Parser {
20197aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        @Override public void parse(String data) throws Exception {
20297aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            DocumentBuilderFactory.newInstance().newDocumentBuilder()
20397aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                    .parse(new InputSource(new StringReader(data)));
20497aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        }
20597aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes    }
20697aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes
20797aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes    private static class XmlSaxParser implements Parser {
20897aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        @Override public void parse(String data) throws Exception {
20997aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes            SAXParserFactory.newInstance().newSAXParser().parse(
21097aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes                    new InputSource(new StringReader(data)), new DefaultHandler());
21197aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes        }
21297aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes    }
21397aba27f961a5a6f37dcaf7f455df371e250ede3Elliott Hughes}
214