TINKERPOP-1676 Performance enhancement to graphson serialization

Focuses on speeding up serialization of graph elements. Prevent use of generic 
maps and stream graphson data directly into "detached" elements. Not using 
intermediate maps cut down on memory usage and a bunch of jackson reflection 
calls (still not sure what they were for and why they were not made to be more 
efficient). It did mean some ugly changes to "detached" stuff. Will need to 
maybe refactor some more, but the basic premise seems to be proven.


Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/02b00736
Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/02b00736
Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/02b00736

Branch: refs/heads/TINKERPOP-1681
Commit: 02b007366b434918fe1181f99d80689c1c03684b
Parents: 251f5b7
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Tue May 23 20:13:46 2017 -0400
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Thu May 25 14:52:30 2017 -0400

----------------------------------------------------------------------
 .../io/graphson/GraphSONSerializersV2d0.java    | 71 ++++++++++----------
 .../structure/util/detached/DetachedEdge.java   | 18 ++++-
 .../util/detached/DetachedElement.java          | 15 ++++-
 .../structure/util/detached/DetachedUtil.java   | 70 +++++++++++++++++++
 .../structure/util/detached/DetachedVertex.java | 19 ++++--
 .../util/detached/DetachedVertexProperty.java   | 14 +++-
 6 files changed, 161 insertions(+), 46 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/02b00736/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV2d0.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV2d0.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV2d0.java
index 0008d3a..bdf3fe5 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV2d0.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONSerializersV2d0.java
@@ -36,6 +36,7 @@ import org.apache.tinkerpop.gremlin.structure.VertexProperty;
 import org.apache.tinkerpop.gremlin.structure.util.Comparators;
 import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedEdge;
 import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedProperty;
+import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedUtil;
 import org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex;
 import 
org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertexProperty;
 import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
@@ -425,25 +426,26 @@ class GraphSONSerializersV2d0 {
         }
 
         public Vertex deserialize(final JsonParser jsonParser, final 
DeserializationContext deserializationContext) throws IOException, 
JsonProcessingException {
-            final JavaType propertiesType = 
deserializationContext.getConfig().getTypeFactory().constructMapType(HashMap.class,
 String.class, Object.class);
-
-            Object id = null;
-            String label = null;
-            Map<String, Object> properties = null;
+            final DetachedVertex v = DetachedUtil.newDetachedVertex();
             while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
                 if (jsonParser.getCurrentName().equals(GraphSONTokens.ID)) {
                     jsonParser.nextToken();
-                    id = deserializationContext.readValue(jsonParser, 
Object.class);
+                    DetachedUtil.setId(v, 
deserializationContext.readValue(jsonParser, Object.class));
                 } else if 
(jsonParser.getCurrentName().equals(GraphSONTokens.LABEL)) {
                     jsonParser.nextToken();
-                    label = jsonParser.getText();
+                    DetachedUtil.setLabel(v, jsonParser.getText());
                 } else if 
(jsonParser.getCurrentName().equals(GraphSONTokens.PROPERTIES)) {
                     jsonParser.nextToken();
-                    properties = deserializationContext.readValue(jsonParser, 
propertiesType);
+                    while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
+                        jsonParser.nextToken();
+                        while (jsonParser.nextToken() != JsonToken.END_ARRAY) {
+                            DetachedUtil.addProperty(v, 
(DetachedVertexProperty) deserializationContext.readValue(jsonParser, 
VertexProperty.class));
+                        }
+                    }
                 }
             }
 
-            return new DetachedVertex(id, label, properties);
+            return v;
         }
 
         @Override
@@ -460,41 +462,41 @@ class GraphSONSerializersV2d0 {
 
         @Override
         public Edge deserialize(final JsonParser jsonParser, final 
DeserializationContext deserializationContext) throws IOException, 
JsonProcessingException {
-            final JavaType propertiesType = 
deserializationContext.getConfig().getTypeFactory().constructMapType(HashMap.class,
 String.class, Object.class);
-
-            Object id = null;
-            String label = null;
-            Object outVId = null;
-            String outVLabel = null;
-            Object inVId = null;
-            String inVLabel = null;
-            Map<String, Object> properties = null;
+            final DetachedEdge e = DetachedUtil.newDetachedEdge();
+            final DetachedVertex inV = DetachedUtil.newDetachedVertex();
+            final DetachedVertex outV = DetachedUtil.newDetachedVertex();
             while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
                 if (jsonParser.getCurrentName().equals(GraphSONTokens.ID)) {
                     jsonParser.nextToken();
-                    id = deserializationContext.readValue(jsonParser, 
Object.class);
+                    DetachedUtil.setId(e, 
deserializationContext.readValue(jsonParser, Object.class));
                 } else if 
(jsonParser.getCurrentName().equals(GraphSONTokens.LABEL)) {
                     jsonParser.nextToken();
-                    label = jsonParser.getText();
+                    DetachedUtil.setLabel(e, jsonParser.getText());
                 } else if 
(jsonParser.getCurrentName().equals(GraphSONTokens.OUT)) {
                     jsonParser.nextToken();
-                    outVId = deserializationContext.readValue(jsonParser, 
Object.class);
+                    DetachedUtil.setId(outV, 
deserializationContext.readValue(jsonParser, Object.class));
                 } else if 
(jsonParser.getCurrentName().equals(GraphSONTokens.OUT_LABEL)) {
                     jsonParser.nextToken();
-                    outVLabel = jsonParser.getText();
+                    DetachedUtil.setLabel(outV, jsonParser.getText());
                 } else if 
(jsonParser.getCurrentName().equals(GraphSONTokens.IN)) {
                     jsonParser.nextToken();
-                    inVId = deserializationContext.readValue(jsonParser, 
Object.class);
+                    DetachedUtil.setId(inV, 
deserializationContext.readValue(jsonParser, Object.class));
                 } else if 
(jsonParser.getCurrentName().equals(GraphSONTokens.IN_LABEL)) {
                     jsonParser.nextToken();
-                    inVLabel = jsonParser.getText();
+                    DetachedUtil.setLabel(inV,jsonParser.getText());
                 } else if 
(jsonParser.getCurrentName().equals(GraphSONTokens.PROPERTIES)) {
                     jsonParser.nextToken();
-                    properties = deserializationContext.readValue(jsonParser, 
propertiesType);
+                    while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
+                        jsonParser.nextToken();
+                        DetachedUtil.addProperty(e, (DetachedProperty) 
deserializationContext.readValue(jsonParser, Property.class));
+                    }
                 }
             }
 
-            return new DetachedEdge(id, label, properties, outVId, outVLabel, 
inVId, inVLabel);
+            DetachedUtil.setInV(e, inV);
+            DetachedUtil.setOutV(e, outV);
+
+            return e;
         }
 
         @Override
@@ -560,29 +562,28 @@ class GraphSONSerializersV2d0 {
 
         @Override
         public VertexProperty deserialize(final JsonParser jsonParser, final 
DeserializationContext deserializationContext) throws IOException, 
JsonProcessingException {
+            final DetachedVertexProperty vp = 
DetachedUtil.newDetachedVertexProperty();
             final JavaType propertiesType = 
deserializationContext.getConfig().getTypeFactory().constructMapType(HashMap.class,
 String.class, Object.class);
 
-            Object id = null;
-            String label = null;
-            Object value = null;
-            Map<String, Object> properties = null;
+            Map<String, Object> properties;
             while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
                 if (jsonParser.getCurrentName().equals(GraphSONTokens.ID)) {
                     jsonParser.nextToken();
-                    id = deserializationContext.readValue(jsonParser, 
Object.class);
+                    DetachedUtil.setId(vp, 
deserializationContext.readValue(jsonParser, Object.class));
                 } else if 
(jsonParser.getCurrentName().equals(GraphSONTokens.LABEL)) {
                     jsonParser.nextToken();
-                    label = jsonParser.getText();
+                    DetachedUtil.setLabel(vp, jsonParser.getText());
                 } else if 
(jsonParser.getCurrentName().equals(GraphSONTokens.VALUE)) {
                     jsonParser.nextToken();
-                    value = deserializationContext.readValue(jsonParser, 
Object.class);
-                }else if 
(jsonParser.getCurrentName().equals(GraphSONTokens.PROPERTIES)) {
+                    DetachedUtil.setValue(vp, 
deserializationContext.readValue(jsonParser, Object.class));
+                } else if 
(jsonParser.getCurrentName().equals(GraphSONTokens.PROPERTIES)) {
                     jsonParser.nextToken();
                     properties = deserializationContext.readValue(jsonParser, 
propertiesType);
+                    properties.entrySet().iterator().forEachRemaining(kv -> 
DetachedUtil.addProperty(vp, new DetachedProperty(kv.getKey(), kv.getValue())));
                 }
             }
 
-            return new DetachedVertexProperty<>(id, label, value, properties);
+            return vp;
         }
 
         @Override

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/02b00736/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedEdge.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedEdge.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedEdge.java
index 1284ca7..40b6e34 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedEdge.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedEdge.java
@@ -51,9 +51,7 @@ public class DetachedEdge extends DetachedElement<Edge> 
implements Edge {
     private DetachedVertex outVertex;
     private DetachedVertex inVertex;
 
-    private DetachedEdge() {
-
-    }
+    DetachedEdge() {}
 
     protected DetachedEdge(final Edge edge, final boolean withProperties) {
         super(edge);
@@ -150,4 +148,18 @@ public class DetachedEdge extends DetachedElement<Edge> 
implements Edge {
     public <V> Iterator<Property<V>> properties(final String... propertyKeys) {
         return (Iterator) super.properties(propertyKeys);
     }
+
+    @Override
+    void internalAddProperty(final Property p) {
+        if (null == properties) properties = new HashMap<>();
+        this.properties.put(p.key(), Collections.singletonList(p));
+    }
+
+    void internalSetOutV(final DetachedVertex v) {
+        outVertex = v;
+    }
+
+    void internalSetInV(final DetachedVertex v) {
+        inVertex = v;
+    }
 }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/02b00736/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedElement.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedElement.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedElement.java
index f9c8c73..4e40303 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedElement.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedElement.java
@@ -25,9 +25,12 @@ import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.structure.util.Attachable;
 import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
 import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
+import org.apache.tinkerpop.gremlin.util.function.HashMapSupplier;
 
 import java.io.Serializable;
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -40,7 +43,7 @@ public abstract class DetachedElement<E> implements Element, 
Serializable, Attac
 
     protected Object id;
     protected String label;
-    protected Map<String, List<? extends Property>> properties = null;
+    protected Map<String, List<Property>> properties = null;
 
     protected DetachedElement() {
 
@@ -106,4 +109,14 @@ public abstract class DetachedElement<E> implements 
Element, Serializable, Attac
     public E get() {
         return (E) this;
     }
+
+    abstract void internalAddProperty(final Property p);
+
+    void internalSetId(final Object id) {
+        this.id = id;
+    }
+
+    void inernalSetLabel(final String label) {
+        this.label = label;
+    }
 }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/02b00736/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedUtil.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedUtil.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedUtil.java
new file mode 100644
index 0000000..f2c658d
--- /dev/null
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedUtil.java
@@ -0,0 +1,70 @@
+/*
+ * 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.tinkerpop.gremlin.structure.util.detached;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public final class DetachedUtil {
+
+    private DetachedUtil() {}
+
+    /**
+     * Provides a way to mutate something that is "detached". This method is 
really for internal usage as there
+     * typically is not need for application developers to mutate a "detached" 
element.
+     */
+    public static void addProperty(final DetachedElement e, final 
DetachedProperty p) {
+        e.internalAddProperty(p);
+    }
+    public static void addProperty(final DetachedVertex v, final 
DetachedVertexProperty vp) {
+        v.internalAddProperty(vp);
+    }
+
+    public static void setId(final DetachedElement e, final Object id) {
+        e.internalSetId(id);
+    }
+
+    public static void setLabel(final DetachedElement e, final String label) {
+        e.inernalSetLabel(label);
+    }
+
+    public static void setValue(final DetachedVertexProperty vp, final Object 
value) {
+        vp.internalSetValue(value);
+    }
+
+    public static DetachedVertex newDetachedVertex() {
+        return new DetachedVertex();
+    }
+
+    public static DetachedVertexProperty newDetachedVertexProperty() {
+        return new DetachedVertexProperty();
+    }
+
+    public static DetachedEdge newDetachedEdge() {
+        return new DetachedEdge();
+    }
+
+    public static void setInV(final DetachedEdge e, final DetachedVertex v) {
+        e.internalSetInV(v);
+    }
+
+    public static void setOutV(final DetachedEdge e, final DetachedVertex v) {
+        e.internalSetOutV(v);
+    }
+}

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/02b00736/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedVertex.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedVertex.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedVertex.java
index d3fbd8e..15e956b 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedVertex.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedVertex.java
@@ -22,8 +22,10 @@ import org.apache.tinkerpop.gremlin.structure.Direction;
 import org.apache.tinkerpop.gremlin.structure.Edge;
 import org.apache.tinkerpop.gremlin.structure.Element;
 import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.Property;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
 import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
 
@@ -53,8 +55,7 @@ public class DetachedVertex extends DetachedElement<Vertex> 
implements Vertex {
     private static final String VALUE = "value";
     private static final String PROPERTIES = "properties";
 
-    private DetachedVertex() {
-    }
+    DetachedVertex() {}
 
     protected DetachedVertex(final Vertex vertex, final boolean 
withProperties) {
         super(vertex);
@@ -67,7 +68,7 @@ public class DetachedVertex extends DetachedElement<Vertex> 
implements Vertex {
             if (propertyIterator.hasNext()) {
                 this.properties = new HashMap<>();
                 propertyIterator.forEachRemaining(property -> {
-                    final List<VertexProperty<?>> list = 
(List<VertexProperty<?>>) this.properties.getOrDefault(property.key(), new 
ArrayList<>());
+                    final List<Property> list = 
this.properties.getOrDefault(property.key(), new ArrayList<>());
                     list.add(DetachedFactory.detach(property, true));
                     this.properties.put(property.key(), list);
                 });
@@ -80,7 +81,7 @@ public class DetachedVertex extends DetachedElement<Vertex> 
implements Vertex {
         if (properties != null && !properties.isEmpty()) {
             this.properties = new HashMap<>();
             properties.entrySet().iterator().forEachRemaining(entry ->
-                this.properties.put(entry.getKey(), 
IteratorUtils.<VertexProperty>list(IteratorUtils.map(((List<Object>) 
entry.getValue()).iterator(),
+                this.properties.put(entry.getKey(), 
IteratorUtils.<Property>list(IteratorUtils.map(((List<Object>) 
entry.getValue()).iterator(),
                         m -> 
VertexProperty.class.isAssignableFrom(m.getClass())
                                 ? (VertexProperty) m
                                 : new DetachedVertexProperty<>(((Map) 
m).get(ID), entry.getKey(), ((Map) m).get(VALUE), (Map<String, Object>) ((Map) 
m).getOrDefault(PROPERTIES, new HashMap<>()), this)))));
@@ -143,4 +144,14 @@ public class DetachedVertex extends 
DetachedElement<Vertex> implements Vertex {
     public void remove() {
         throw Vertex.Exceptions.vertexRemovalNotSupported();
     }
+
+    @Override
+    void internalAddProperty(final Property p) {
+        if (null == properties) properties = new HashMap<>();
+
+        if (!properties.containsKey(p.key()))
+            properties.put(p.key(), new ArrayList<>());
+
+        this.properties.get(p.key()).add(p);
+    }
 }

http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/02b00736/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedVertexProperty.java
----------------------------------------------------------------------
diff --git 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedVertexProperty.java
 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedVertexProperty.java
index 069c14c..f682e04 100644
--- 
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedVertexProperty.java
+++ 
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/detached/DetachedVertexProperty.java
@@ -18,7 +18,6 @@
  */
 package org.apache.tinkerpop.gremlin.structure.util.detached;
 
-import org.apache.tinkerpop.gremlin.structure.Graph;
 import org.apache.tinkerpop.gremlin.structure.Property;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
@@ -40,8 +39,7 @@ public class DetachedVertexProperty<V> extends 
DetachedElement<VertexProperty<V>
     protected V value;
     protected transient DetachedVertex vertex;
 
-    private DetachedVertexProperty() {
-    }
+    DetachedVertexProperty() {}
 
     protected DetachedVertexProperty(final VertexProperty<V> vertexProperty, 
final boolean withProperties) {
         super(vertexProperty);
@@ -127,4 +125,14 @@ public class DetachedVertexProperty<V> extends 
DetachedElement<VertexProperty<V>
     public <U> Iterator<Property<U>> properties(final String... propertyKeys) {
         return (Iterator) super.properties(propertyKeys);
     }
+
+    @Override
+    void internalAddProperty(final Property p) {
+        if (null == properties) properties = new HashMap<>();
+        this.properties.put(p.key(), Collections.singletonList(p));
+    }
+
+    void internalSetValue(final V value) {
+        this.value = value;
+    }
 }

Reply via email to