You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by cd...@apache.org on 2019/06/04 19:33:53 UTC

[plc4x] 02/02: - Optimized the generated code to be way more efficient while parsing

This is an automated email from the ASF dual-hosted git repository.

cdutz pushed a commit to branch feature/code-gen
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 87a9a46b0cd7b07f0cb56e83d96d1b06d9b5c34e
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Tue Jun 4 21:33:41 2019 +0200

    - Optimized the generated code to be way more efficient while parsing
---
 .../apache/plc4x/language/fields/ArrayField.java   |  5 ---
 .../apache/plc4x/language/fields/ContextField.java | 36 -----------------
 .../plc4x/language/fields/OptionalField.java       |  5 ---
 .../plc4x/language/fields/PropertyField.java       |  7 ++++
 .../apache/plc4x/language/fields/SimpleField.java  |  6 ---
 .../code-generation/language-template-java/pom.xml |  5 +++
 .../language/java/JavaLanguageTemplateHelper.java  | 22 ++++++++++
 .../main/resources/templates/java/io-template.ftlh | 47 +++++++++++++++-------
 .../codegenerator/parser/imaginary/Imaginary.g4    | 34 ++++++++--------
 .../parser/MessageFormatListener.java              | 15 +++----
 .../src/main/resources/protocols/s7/protocol.spec  |  2 +-
 .../apache/plc4x/java/utils/EvaluationHelper.java} | 40 +++++++++---------
 .../java/{Test.java => BenchmarkGeneratedS7.java}  | 13 ++++--
 13 files changed, 122 insertions(+), 115 deletions(-)

diff --git a/sandbox/code-generation/language-base/src/main/java/org/apache/plc4x/language/fields/ArrayField.java b/sandbox/code-generation/language-base/src/main/java/org/apache/plc4x/language/fields/ArrayField.java
index e79cb9b..a0af838 100644
--- a/sandbox/code-generation/language-base/src/main/java/org/apache/plc4x/language/fields/ArrayField.java
+++ b/sandbox/code-generation/language-base/src/main/java/org/apache/plc4x/language/fields/ArrayField.java
@@ -19,7 +19,6 @@
 
 package org.apache.plc4x.language.fields;
 
-import org.apache.plc4x.language.references.TypeReference;
 
 public interface ArrayField extends PropertyField {
 
@@ -27,10 +26,6 @@ public interface ArrayField extends PropertyField {
         return "array";
     }
 
-    TypeReference getType();
-
-    String getName();
-
     LengthType getLengthType();
 
     String getLengthExpression();
diff --git a/sandbox/code-generation/language-base/src/main/java/org/apache/plc4x/language/fields/ContextField.java b/sandbox/code-generation/language-base/src/main/java/org/apache/plc4x/language/fields/ContextField.java
deleted file mode 100644
index 5cb451b..0000000
--- a/sandbox/code-generation/language-base/src/main/java/org/apache/plc4x/language/fields/ContextField.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements.  See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership.  The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License.  You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing,
- software distributed under the License is distributed on an
- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- KIND, either express or implied.  See the License for the
- specific language governing permissions and limitations
- under the License.
- */
-
-package org.apache.plc4x.language.fields;
-
-import org.apache.plc4x.language.references.TypeReference;
-
-public interface ContextField extends Field {
-
-    default String getTypeName() {
-        return "context";
-    }
-
-    TypeReference getType();
-
-    String getName();
-
-    String getValueExpression();
-
-}
diff --git a/sandbox/code-generation/language-base/src/main/java/org/apache/plc4x/language/fields/OptionalField.java b/sandbox/code-generation/language-base/src/main/java/org/apache/plc4x/language/fields/OptionalField.java
index fad6cfd..7d0c60c 100644
--- a/sandbox/code-generation/language-base/src/main/java/org/apache/plc4x/language/fields/OptionalField.java
+++ b/sandbox/code-generation/language-base/src/main/java/org/apache/plc4x/language/fields/OptionalField.java
@@ -19,7 +19,6 @@
 
 package org.apache.plc4x.language.fields;
 
-import org.apache.plc4x.language.references.TypeReference;
 
 public interface OptionalField extends PropertyField {
 
@@ -27,10 +26,6 @@ public interface OptionalField extends PropertyField {
         return "optional";
     }
 
-    TypeReference getType();
-
-    String getName();
-
     String getConditionExpression();
 
 }
diff --git a/sandbox/code-generation/language-base/src/main/java/org/apache/plc4x/language/fields/PropertyField.java b/sandbox/code-generation/language-base/src/main/java/org/apache/plc4x/language/fields/PropertyField.java
index b6b83de..167e43d 100644
--- a/sandbox/code-generation/language-base/src/main/java/org/apache/plc4x/language/fields/PropertyField.java
+++ b/sandbox/code-generation/language-base/src/main/java/org/apache/plc4x/language/fields/PropertyField.java
@@ -19,5 +19,12 @@
 
 package org.apache.plc4x.language.fields;
 
+import org.apache.plc4x.language.references.TypeReference;
+
 public interface PropertyField extends Field {
+
+    TypeReference getType();
+
+    String getName();
+
 }
diff --git a/sandbox/code-generation/language-base/src/main/java/org/apache/plc4x/language/fields/SimpleField.java b/sandbox/code-generation/language-base/src/main/java/org/apache/plc4x/language/fields/SimpleField.java
index 2ff614d..73a34f0 100644
--- a/sandbox/code-generation/language-base/src/main/java/org/apache/plc4x/language/fields/SimpleField.java
+++ b/sandbox/code-generation/language-base/src/main/java/org/apache/plc4x/language/fields/SimpleField.java
@@ -19,16 +19,10 @@
 
 package org.apache.plc4x.language.fields;
 
-import org.apache.plc4x.language.references.TypeReference;
-
 public interface SimpleField extends PropertyField {
 
     default String getTypeName() {
         return "simple";
     }
 
-    TypeReference getType();
-
-    String getName();
-
 }
diff --git a/sandbox/code-generation/language-template-java/pom.xml b/sandbox/code-generation/language-template-java/pom.xml
index 60b1ca0..fdc791e 100644
--- a/sandbox/code-generation/language-template-java/pom.xml
+++ b/sandbox/code-generation/language-template-java/pom.xml
@@ -44,6 +44,11 @@
       <artifactId>freemarker</artifactId>
       <version>2.3.28</version>
     </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-text</artifactId>
+      <version>1.6</version>
+    </dependency>
 
     <dependency>
       <groupId>org.slf4j</groupId>
diff --git a/sandbox/code-generation/language-template-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java b/sandbox/code-generation/language-template-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java
index d1c0ed9..744bc4c 100644
--- a/sandbox/code-generation/language-template-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java
+++ b/sandbox/code-generation/language-template-java/src/main/java/org/apache/plc4x/language/java/JavaLanguageTemplateHelper.java
@@ -19,6 +19,7 @@
 
 package org.apache.plc4x.language.java;
 
+import org.apache.commons.text.WordUtils;
 import org.apache.plc4x.language.definitions.DiscriminatedComplexTypeDefinition;
 import org.apache.plc4x.language.definitions.TypeDefinition;
 import org.apache.plc4x.language.fields.ArrayField;
@@ -26,6 +27,9 @@ import org.apache.plc4x.language.references.ComplexTypeReference;
 import org.apache.plc4x.language.references.SimpleTypeReference;
 import org.apache.plc4x.language.references.TypeReference;
 
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
 public class JavaLanguageTemplateHelper {
 
     public String getLanguageTypeNameForSpecType(TypeReference typeReference) {
@@ -253,4 +257,22 @@ public class JavaLanguageTemplateHelper {
         return arrayField.getLengthType() == ArrayField.LengthType.COUNT;
     }
 
+    public String toReadExpression(String expression) {
+        StringBuilder sb = new StringBuilder();
+        Pattern pattern = Pattern.compile("([^\\.]*)\\.([a-zA-Z\\d]+)(.*)");
+        Matcher matcher;
+        while ((matcher = pattern.matcher(expression)).matches()) {
+            String prefix = matcher.group(1);
+            String middle = matcher.group(2);
+            sb.append(prefix).append(".get").append(WordUtils.capitalize(middle)).append("()");
+            expression = matcher.group(3);
+        }
+        sb.append(expression);
+        return sb.toString();
+    }
+
+    public String toWriteExpression(String expression) {
+        return null;
+    }
+
 }
diff --git a/sandbox/code-generation/language-template-java/src/main/resources/templates/java/io-template.ftlh b/sandbox/code-generation/language-template-java/src/main/resources/templates/java/io-template.ftlh
index fb45225..f167a8a 100644
--- a/sandbox/code-generation/language-template-java/src/main/resources/templates/java/io-template.ftlh
+++ b/sandbox/code-generation/language-template-java/src/main/resources/templates/java/io-template.ftlh
@@ -40,6 +40,7 @@ package ${packageName}.io;
 
 import ${packageName}.*;
 import org.apache.commons.jexl3.*;
+import org.apache.plc4x.java.utils.EvaluationHelper;
 import org.apache.plc4x.java.utils.ReadBuffer;
 import org.apache.plc4x.java.utils.ParseException;
 import org.apache.plc4x.java.utils.WriteBuffer;
@@ -54,9 +55,37 @@ public class ${typeName}IO  {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(${typeName}IO.class);
 
+<#assign needJexl=false>
+<#list type.fields as field>
+<#switch field.typeName>
+    <#case "array">
+    private static JexlExpression ex${field.name?cap_first};
+    <#assign needJexl=true>
+    <#break>
+    <#case "optional">
+    private static JexlExpression ex${field.name?cap_first};
+    <#assign needJexl=true>
+    <#break>
+    </#switch>
+</#list>
+<#if needJexl>
+    static {
+        JexlEngine jexl = new JexlBuilder().create();
+<#list type.fields as field>
+<#switch field.typeName>
+    <#case "array">
+        ex${field.name?cap_first} = jexl.createExpression("${field.lengthExpression}");
+        <#break>
+    <#case "optional">
+        ex${field.name?cap_first} = jexl.createExpression("${field.conditionExpression}");
+        <#break>
+</#switch>
+</#list>
+    }
+</#if>
+
     public static <#if helper.isDiscriminatedType(type)>${typeName}Builder<#else>${typeName}</#if> parse(ReadBuffer io<#if type.parserArguments?has_content>, <#list type.parserArguments as parserArgument>${helper.getLanguageTypeNameForSpecType(parserArgument.type)} ${parserArgument.name}<#sep>, </#sep></#list></#if>) throws ParseException {
         int startPos = io.getPos();
-        JexlEngine jexl = new JexlBuilder().create();
         JexlContext jc = new MapContext();
         <#if type.parserArguments?has_content>
         <#list type.parserArguments as parserArgument>
@@ -67,11 +96,10 @@ public class ${typeName}IO  {
     <#case "array">
 
         // Array field
-        JexlExpression ex${field.name} = jexl.createExpression("${field.lengthExpression}");
         <#if field.lengthExpression?contains("curPos")>
         jc.set("curPos", io.getPos() - startPos);
         </#if>
-        int size = Integer.valueOf(ex${field.name}.evaluate(jc).toString());
+        int size = Integer.valueOf(ex${field.name?cap_first}.evaluate(jc).toString());
         <#if helper.isCountArray(field)>
         ${helper.getLanguageTypeNameForSpecType(field.type)}[] ${field.name} = new ${helper.getLanguageTypeNameForSpecType(field.type)}[size];
         for(int i = 0; i < size; i++) {
@@ -96,13 +124,6 @@ public class ${typeName}IO  {
         }
         jc.set("${field.name}", ${field.name});
         <#break>
-    <#case "context">
-
-        // Context Field (Artificial value not retrieved by the input, but the context)
-        JexlExpression ex${field.name?cap_first} = jexl.createExpression("${(field.valueExpression?replace("\"", "\\\"")?no_esc)}");
-        ${helper.getLanguageTypeNameForSpecType(field.type)} ${field.name} = (${helper.getLanguageTypeNameForSpecType(field.type)}) ex${field.name?cap_first}.evaluate(jc);
-        jc.set("${field.name}", ${field.name});
-        <#break>
     <#case "discriminator">
 
         // Discriminator Field (Used as input to a switch field)
@@ -119,7 +140,6 @@ public class ${typeName}IO  {
 
         // Optional Field (Can be skipped, if a given expression evaluates to false)
         ${helper.getLanguageTypeNameForSpecType(field.type)} ${field.name} = ${helper.getNullValueForType(field.type)};
-        JexlExpression ex${field.name?cap_first} = jexl.createExpression("${field.conditionExpression}");
         if(ex${field.name?cap_first}.evaluate(jc) == Boolean.TRUE) {
             ${field.name} = <#if helper.isSimpleType(field.type)>io.${helper.getReadBufferReadMethodCall(field.type)}<#else>${field.type.name}IO.parse(io);</#if>;
         }
@@ -146,10 +166,9 @@ public class ${typeName}IO  {
         // Switch field (Depending on the discriminator values, passes the instantiation to a sub-type)
         ${typeName}Builder builder = null;
         <#list field.cases as case>
-        JexlExpression ex${case.name} = jexl.createExpression("<#list case.discriminatorValues as discriminatorValue>(${field.discriminatorNames[discriminatorValue?index]} == ${discriminatorValue})<#sep> && </#sep></#list>");
-        if(ex${case.name}.evaluate(jc) == Boolean.TRUE) {
+        if(<#list case.discriminatorValues as discriminatorValue>EvaluationHelper.equals(${helper.toReadExpression(field.discriminatorNames[discriminatorValue?index])}, ${discriminatorValue})<#sep> && </#sep></#list>) {
             builder = ${case.name}IO.parse(io<#if type.parserArguments?has_content>, <#list type.parserArguments as parserArgument>${parserArgument.name}<#sep>, </#sep></#list></#if>);
-        }
+        }<#sep> else </#sep>
         </#list>
         if (builder == null) {
             throw new ParseException("Unsupported case for discriminated type");
diff --git a/sandbox/code-generation/plc4x-maven-plugin/src/main/antlr4/org/apache/plc4x/codegenerator/parser/imaginary/Imaginary.g4 b/sandbox/code-generation/plc4x-maven-plugin/src/main/antlr4/org/apache/plc4x/codegenerator/parser/imaginary/Imaginary.g4
index 4671814..6a866a4 100644
--- a/sandbox/code-generation/plc4x-maven-plugin/src/main/antlr4/org/apache/plc4x/codegenerator/parser/imaginary/Imaginary.g4
+++ b/sandbox/code-generation/plc4x-maven-plugin/src/main/antlr4/org/apache/plc4x/codegenerator/parser/imaginary/Imaginary.g4
@@ -1,4 +1,22 @@
 grammar Imaginary;
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
 
 file
  : complexTypeDefinition* EOF
@@ -22,7 +40,6 @@ fieldDefinition
 field
  : arrayField
  | constField
- | contextField
  | discriminatorField
  | simpleField
  | implicitField
@@ -39,10 +56,6 @@ constField
  : K_CONST type=dataType name=idExpression expected=expression
  ;
 
-contextField
- : K_CONTEXT type=dataType name=idExpression valueExpression=expression
- ;
-
 discriminatorField
  : K_DISCRIMINATOR type=dataType name=idExpression
  ;
@@ -112,10 +125,6 @@ innerExpression
  | '"' innerExpression '"'
  ;
 
-context
- : IDENTIFIER ':' expression (',' IDENTIFIER ':' expression)*
- ;
-
 COMMENT
  : K_COMMENT [a-zA-Z0-9,.'":()/ \t\r\n\u000C-]*
  | '//' [a-zA-Z0-9,.'":()/ \t-]*
@@ -136,13 +145,6 @@ fragment HexDigit
  : [0-9a-fA-F]
 ;
 
-modifier
- : K_CONST
- | K_RESERVED
- | K_IMPLICIT
- | K_EMBEDDED
- ;
-
 arrayType
  : K_COUNT
  | K_LENGTH
diff --git a/sandbox/code-generation/plc4x-maven-plugin/src/main/java/org/apache/plc4x/plugins/codegenerator/parser/MessageFormatListener.java b/sandbox/code-generation/plc4x-maven-plugin/src/main/java/org/apache/plc4x/plugins/codegenerator/parser/MessageFormatListener.java
index be438b5..001888c 100644
--- a/sandbox/code-generation/plc4x-maven-plugin/src/main/java/org/apache/plc4x/plugins/codegenerator/parser/MessageFormatListener.java
+++ b/sandbox/code-generation/plc4x-maven-plugin/src/main/java/org/apache/plc4x/plugins/codegenerator/parser/MessageFormatListener.java
@@ -111,15 +111,6 @@ public class MessageFormatListener extends ImaginaryBaseListener {
     }
 
     @Override
-    public void enterContextField(ImaginaryParser.ContextFieldContext ctx) {
-        SimpleTypeReference type = getSimpleTypeReference(ctx.type);
-        String name = ctx.name.id.getText();
-        String valueExpression = ctx.valueExpression.expr.getText();
-        Field field = new DefaultContextField(type, name, valueExpression);
-        parserContexts.peek().add(field);
-    }
-
-    @Override
     public void enterDiscriminatorField(ImaginaryParser.DiscriminatorFieldContext ctx) {
         SimpleTypeReference type = getSimpleTypeReference(ctx.type);
         String name = ctx.name.id.getText();
@@ -209,6 +200,12 @@ public class MessageFormatListener extends ImaginaryBaseListener {
         complexTypes.put(typeName, type);
     }
 
+    /*@Override
+    public void exitExpression(ImaginaryParser.ExpressionContext ctx) {
+        ImaginaryParser.InnerExpressionContext expressionContext = ctx.innerExpression();
+        super.exitExpression(ctx);
+    }*/
+
     private TypeReference getTypeReference(ImaginaryParser.TypeReferenceContext ctx) {
         if(ctx.simpleTypeReference != null) {
             SimpleTypeReference.SimpleBaseType simpleBaseType = SimpleTypeReference.SimpleBaseType.valueOf(
diff --git a/sandbox/code-generation/protocol-s7/src/main/resources/protocols/s7/protocol.spec b/sandbox/code-generation/protocol-s7/src/main/resources/protocols/s7/protocol.spec
index a84cdcb..66e9fe9 100644
--- a/sandbox/code-generation/protocol-s7/src/main/resources/protocols/s7/protocol.spec
+++ b/sandbox/code-generation/protocol-s7/src/main/resources/protocols/s7/protocol.spec
@@ -14,7 +14,7 @@
 ////////////////////////////////////////////////////////////////
 
 [discriminatedType 'COTPPacket'
-    [implicit      uint 8 'headerLength' 'this.size - (payload.size + 1)']
+    [implicit      uint 8 'headerLength' 'size - (payload.size + 1)']
     [discriminator uint 8 'tpduCode']
     [typeSwitch 'tpduCode'
         ['0xF0' COTPPacketData
diff --git a/sandbox/code-generation/plc4x-maven-plugin/src/main/java/org/apache/plc4x/plugins/codegenerator/model/fields/DefaultContextField.java b/sandbox/code-generation/test-java-s7-driver/src/main/java/org/apache/plc4x/java/utils/EvaluationHelper.java
similarity index 50%
rename from sandbox/code-generation/plc4x-maven-plugin/src/main/java/org/apache/plc4x/plugins/codegenerator/model/fields/DefaultContextField.java
rename to sandbox/code-generation/test-java-s7-driver/src/main/java/org/apache/plc4x/java/utils/EvaluationHelper.java
index f9ad5c8..8e62d79 100644
--- a/sandbox/code-generation/plc4x-maven-plugin/src/main/java/org/apache/plc4x/plugins/codegenerator/model/fields/DefaultContextField.java
+++ b/sandbox/code-generation/test-java-s7-driver/src/main/java/org/apache/plc4x/java/utils/EvaluationHelper.java
@@ -17,37 +17,37 @@
  under the License.
  */
 
-package org.apache.plc4x.plugins.codegenerator.model.fields;
+package org.apache.plc4x.java.utils;
 
-import org.apache.plc4x.language.fields.ContextField;
-import org.apache.plc4x.language.references.TypeReference;
+public class EvaluationHelper {
 
-public class DefaultContextField implements ContextField {
-
-    private final TypeReference type;
-    private final String name;
-    private final String valueExpression;
+    public static boolean equals(Object val1, Object val2) {
+        if(val1 instanceof Number && val2 instanceof Number) {
+            Number number1 = (Number) val1;
+            Number number2 = (Number) val2;
+            return number1.doubleValue() == number2.doubleValue();
+        }
+        return false;
+    }
 
-    public DefaultContextField(TypeReference type, String name, String valueExpression) {
-        this.type = type;
-        this.name = name;
-        this.valueExpression = valueExpression;
+    public static boolean notEquals(Object val1, Object val2) {
+        return true;
     }
 
-    public TypeReference getType() {
-        return type;
+    public static boolean greater(Object val1, Object val2) {
+        return true;
     }
 
-    public String getName() {
-        return name;
+    public static boolean greaterEquals(Object val1, Object val2) {
+        return true;
     }
 
-    public String getValueExpression() {
-        return valueExpression;
+    public static boolean smaller(Object val1, Object val2) {
+        return true;
     }
 
-    public String[] getParams() {
-        return new String[0];
+    public static boolean smallerEquals(Object val1, Object val2) {
+        return true;
     }
 
 }
diff --git a/sandbox/code-generation/test-java-s7-driver/src/test/java/Test.java b/sandbox/code-generation/test-java-s7-driver/src/test/java/BenchmarkGeneratedS7.java
similarity index 71%
rename from sandbox/code-generation/test-java-s7-driver/src/test/java/Test.java
rename to sandbox/code-generation/test-java-s7-driver/src/test/java/BenchmarkGeneratedS7.java
index 28598d5..9ba58b3 100644
--- a/sandbox/code-generation/test-java-s7-driver/src/test/java/Test.java
+++ b/sandbox/code-generation/test-java-s7-driver/src/test/java/BenchmarkGeneratedS7.java
@@ -22,12 +22,19 @@ import org.apache.plc4x.java.s7.TPKTPacket;
 import org.apache.plc4x.java.s7.io.TPKTPacketIO;
 import org.apache.plc4x.java.utils.ReadBuffer;
 
-public class Test {
+public class BenchmarkGeneratedS7 {
 
     public static void main(String[] args) throws Exception {
         byte[] data = Hex.decodeHex("0300006702f080320100000001005600000407120a10060001032b84000160120a10020001032b840001a0120a10010001032b840001a9120a10050001032b84000150120a10020001032b84000198120a10040001032b84000140120a10020001032b84000190");
-        ReadBuffer buf = new ReadBuffer(data);
-        TPKTPacket packet = TPKTPacketIO.parse(buf);
+        long start = System.currentTimeMillis();
+        int numRuns = 20000;
+        for(int i = 0; i < numRuns; i++) {
+            ReadBuffer buf = new ReadBuffer(data);
+            TPKTPacket packet = TPKTPacketIO.parse(buf);
+        }
+        long end = System.currentTimeMillis();
+        System.out.println("Parsed " + numRuns + " packets in " + (end - start) + "ms");
+        System.out.println("That's " + ((float) (end - start) / numRuns) + "ms per packet");
     }
 
 }