package com.opensymphony.module.sitemesh.parser; import com.opensymphony.module.sitemesh.DefaultSitemeshBuffer; import com.opensymphony.module.sitemesh.Page; import com.opensymphony.module.sitemesh.PageParser; import java.io.*; import java.net.URL; /** * This performance test compares the three HTML parsers performance. It downloads a large real world HTML file (at * time of writing this file was 676KB and growing, this is a file that is generated using sitemesh), and then parses * that with each parser 1000 times over. Between each parse it does a System.gc(), to ensure garbage left over from * the last parser doesn't have to be collected during the next parsers run. And it runs each parser 3 times. This * should ensure that by the third time, all major JIT has been done, and so the system will be as close to a running * production server as possible. */ public class ParserPerformanceComparison { private static final String HTML_FILE = "sitemesh-performance-test.html"; private static final String HTML_URL = "https://jira.atlassian.com/browse/JRA-1330"; private static final int PARSE_COUNT = 1000; public static void main(String... args) throws Exception { // Download the file if it doesn't exist File file = new File(System.getProperty("java.io.tmpdir"), HTML_FILE); if (!file.exists()) { System.out.println("Downloading " + HTML_URL + " to use for performance test"); URL url = new URL(HTML_URL); InputStream is = null; OutputStream os = null; try { is = url.openStream(); os = new FileOutputStream(file); copy(is, os); } finally { closeQuietly(is); closeQuietly(os); } } else { System.out.println("Using cached file " + file); } // Read the cached file into a buffer CharArrayWriter writer = new CharArrayWriter(); BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(file)); copy(reader, writer); } finally { closeQuietly(reader); } char[] page = writer.toCharArray(); // Create the parsers PageParser normal = new HTMLPageParser(); PageParser fast = new FastPageParser(); PageParser superfast = new PartialPageParser(); System.out.println("Amount of data: " + page.length); System.gc(); runPerformanceTest("Normal #1", page, normal, PARSE_COUNT); System.gc(); runPerformanceTest("Fast #1", page, fast, PARSE_COUNT); System.gc(); runPerformanceTest("Super Fast #1", page, superfast, PARSE_COUNT); System.gc(); runPerformanceTest("Normal #2", page, normal, PARSE_COUNT); System.gc(); runPerformanceTest("Fast #2", page, fast, PARSE_COUNT); System.gc(); runPerformanceTest("Super Fast #2", page, superfast, PARSE_COUNT); System.gc(); double normalTime = runPerformanceTest("Normal #3", page, normal, PARSE_COUNT); System.gc(); double fastTime = runPerformanceTest("Fast #3", page, fast, PARSE_COUNT); System.gc(); double superfastTime = runPerformanceTest("Super Fast #3", page, superfast, PARSE_COUNT); System.out.println("\nPerformance comparison %\n========================"); System.out.println(String.format("%-10s%12s%12s%12s", "", "Normal", "Fast", "Super Fast")); System.out.println(String.format("%-10s%12s% 11.1f%%% 11.1f%%", "Normal", "X", normalTime / fastTime * 100, normalTime / superfastTime * 100)); System.out.println(String.format("%-10s% 11.1f%%%12s% 11.1f%%", "Fast", fastTime / normalTime * 100, "X", fastTime / superfastTime * 100)); System.out.println(String.format("%-10s% 11.1f%%% 11.1f%%%12s", "Super Fast", superfastTime / normalTime * 100, superfastTime / fastTime * 100, "X")); } public static long runPerformanceTest(String name, char[] data, PageParser parser, int times) throws Exception { Writer writer = new NullWriter(); long start = System.currentTimeMillis(); for (int i = 0; i < times; i++) { Page page = parser.parse(new DefaultSitemeshBuffer(data)); page.writeBody(writer); } long finish = System.currentTimeMillis(); long time = finish - start; System.out.println(name + " total: " + time + "ms"); System.out.println(name + " average: " + (double) time / (double) times + "ms"); return time; } private static class NullWriter extends Writer { @Override public void write(char[] cbuf, int off, int len) throws IOException { // do nothnig } @Override public void flush() throws IOException { } @Override public void close() throws IOException { } } private static void copy(InputStream is, OutputStream os) throws IOException { byte[] buf = new byte[4096]; int length; while ((length = is.read(buf)) > 0) { os.write(buf, 0, length); } } private static void copy(Reader reader, Writer writer) throws IOException { char[] buf = new char[4096]; int length; while ((length = reader.read(buf)) > 0) { writer.write(buf, 0, length); } } private static void closeQuietly(Closeable closeable) { try { if (closeable != null) { closeable.close(); } } catch (IOException ioe) { // Ignore } } }