Optimize DataTreeCandidate.applyToModification() 73/19473/5
authorRobert Varga <rovarga@cisco.com>
Sat, 2 May 2015 04:09:52 +0000 (06:09 +0200)
committerRobert Varga <rovarga@cisco.com>
Sun, 3 May 2015 17:13:51 +0000 (19:13 +0200)
Both implementations use recursive calls to themselves. Instead of recursion,
we can use a simplistic single-linked stack of operations and iterate over
them.

While this results in object allocation, these objects are short-lived
and never leak from current thread, which means they are easily
collected. Since the call is not recursive, JIT should be able to more
easily inline and optimize the operations.

Change-Id: I7d82a58c6be3e853ea5bcf9069876370804cb199
Signed-off-by: Robert Varga <rovarga@cisco.com>
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeCandidateNodes.java
yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/DataTreeCandidates.java

index 44f24c2a70f8548327f49e3c6c797bea6efe6ce9..e1636ae77e8707baccc837e5f721ad78cbc30a55 100644 (file)
@@ -8,6 +8,9 @@
 package org.opendaylight.yangtools.yang.data.api.schema.tree;
 
 import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import java.util.Collection;
+import java.util.Iterator;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
 @Beta
@@ -19,4 +22,66 @@ public final class DataTreeCandidateNodes {
     public static DataTreeCandidateNode fromNormalizedNode(final NormalizedNode<?, ?> node) {
         return new NormalizedNodeDataTreeCandidateNode(node);
     }
+
+    public static void applyToCursor(final DataTreeModificationCursor cursor, final DataTreeCandidateNode node) {
+        switch (node.getModificationType()) {
+        case DELETE:
+            cursor.delete(node.getIdentifier());
+            break;
+        case SUBTREE_MODIFIED:
+            cursor.enter(node.getIdentifier());
+            NodeIterator iterator = new NodeIterator(null, node.getChildNodes().iterator());
+            do {
+                iterator = iterator.next(cursor);
+            } while (iterator != null);
+            break;
+        case UNMODIFIED:
+            // No-op
+            break;
+        case WRITE:
+            cursor.write(node.getIdentifier(), node.getDataAfter().get());
+            break;
+        default:
+            throw new IllegalArgumentException("Unsupported modification " + node.getModificationType());
+        }
+    }
+
+    private static final class NodeIterator {
+        private final Iterator<DataTreeCandidateNode> iterator;
+        private final NodeIterator parent;
+
+        NodeIterator(final NodeIterator parent, final Iterator<DataTreeCandidateNode> iterator) {
+            this.parent = Preconditions.checkNotNull(parent);
+            this.iterator = Preconditions.checkNotNull(iterator);
+        }
+
+        NodeIterator next(final DataTreeModificationCursor cursor) {
+            while (iterator.hasNext()) {
+                final DataTreeCandidateNode node = iterator.next();
+                switch (node.getModificationType()) {
+                case DELETE:
+                    cursor.delete(node.getIdentifier());
+                    break;
+                case SUBTREE_MODIFIED:
+                    final Collection<DataTreeCandidateNode> children = node.getChildNodes();
+                    if (!children.isEmpty()) {
+                        cursor.enter(node.getIdentifier());
+                        return new NodeIterator(this, children.iterator());
+                    }
+                    break;
+                case UNMODIFIED:
+                    // No-op
+                    break;
+                case WRITE:
+                    cursor.write(node.getIdentifier(), node.getDataAfter().get());
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unsupported modification " + node.getModificationType());
+                }
+            }
+
+            cursor.exit();
+            return parent;
+        }
+    }
 }
index 99256397b0e9197f9cb156060c8c03fb7b76278f..1b242918ddd15799dec0b977040bf717bb860d5a 100644 (file)
@@ -8,6 +8,8 @@
 package org.opendaylight.yangtools.yang.data.api.schema.tree;
 
 import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import java.util.Iterator;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.slf4j.Logger;
@@ -31,17 +33,20 @@ public final class DataTreeCandidates {
         return new DefaultDataTreeCandidate(rootPath, new NormalizedNodeDataTreeCandidateNode(node));
     }
 
+    public static void applyToCursor(final DataTreeModificationCursor cursor, final DataTreeCandidate candidate) {
+        DataTreeCandidateNodes.applyToCursor(cursor, candidate.getRootNode());
+    }
+
     public static void applyToModification(final DataTreeModification modification, final DataTreeCandidate candidate) {
         if (modification instanceof CursorAwareDataTreeModification) {
             try (DataTreeModificationCursor cursor = ((CursorAwareDataTreeModification) modification).createCursor(candidate.getRootPath())) {
-                applyNode(cursor, candidate.getRootNode());
+                applyToCursor(cursor, candidate);
             }
-        } else {
-            applyNode(modification, candidate.getRootPath(), candidate.getRootNode());
+            return;
         }
-    }
 
-    private static void applyNode(final DataTreeModification modification, final YangInstanceIdentifier path, final DataTreeCandidateNode node) {
+        final DataTreeCandidateNode node = candidate.getRootNode();
+        final YangInstanceIdentifier path = candidate.getRootPath();
         switch (node.getModificationType()) {
         case DELETE:
             modification.delete(path);
@@ -49,9 +54,11 @@ public final class DataTreeCandidates {
             break;
         case SUBTREE_MODIFIED:
             LOG.debug("Modification {} modified path {}", modification, path);
-            for (DataTreeCandidateNode child : node.getChildNodes()) {
-                applyNode(modification, path.node(child.getIdentifier()), child);
-            }
+
+            NodeIterator iterator = new NodeIterator(null, path, node.getChildNodes().iterator());
+            do {
+                iterator = iterator.next(modification);
+            } while (iterator != null);
             break;
         case UNMODIFIED:
             LOG.debug("Modification {} unmodified path {}", modification, path);
@@ -66,26 +73,44 @@ public final class DataTreeCandidates {
         }
     }
 
-    private static void applyNode(final DataTreeModificationCursor cursor, final DataTreeCandidateNode node) {
-        switch (node.getModificationType()) {
-        case DELETE:
-            cursor.delete(node.getIdentifier());
-            break;
-        case SUBTREE_MODIFIED:
-            cursor.enter(node.getIdentifier());
-            for (DataTreeCandidateNode child : node.getChildNodes()) {
-                applyNode(cursor, child);
+    private static final class NodeIterator {
+        private final Iterator<DataTreeCandidateNode> iterator;
+        private final YangInstanceIdentifier path;
+        private final NodeIterator parent;
+
+        public NodeIterator(final NodeIterator parent, final YangInstanceIdentifier path, final Iterator<DataTreeCandidateNode> iterator) {
+            this.iterator = Preconditions.checkNotNull(iterator);
+            this.parent = Preconditions.checkNotNull(parent);
+            this.path = Preconditions.checkNotNull(path);
+        }
+
+        NodeIterator next(final DataTreeModification modification) {
+            while (iterator.hasNext()) {
+                final DataTreeCandidateNode node = iterator.next();
+                final YangInstanceIdentifier child = path.node(node.getIdentifier());
+
+                switch (node.getModificationType()) {
+                case DELETE:
+                    modification.delete(child);
+                    LOG.debug("Modification {} deleted path {}", modification, child);
+                    break;
+                case SUBTREE_MODIFIED:
+                    LOG.debug("Modification {} modified path {}", modification, child);
+                    return new NodeIterator(this, child, node.getChildNodes().iterator());
+                case UNMODIFIED:
+                    LOG.debug("Modification {} unmodified path {}", modification, child);
+                    // No-op
+                    break;
+                case WRITE:
+                    modification.write(child, node.getDataAfter().get());
+                    LOG.debug("Modification {} written path {}", modification, child);
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unsupported modification " + node.getModificationType());
+                }
             }
-            cursor.exit();
-            break;
-        case UNMODIFIED:
-            // No-op
-            break;
-        case WRITE:
-            cursor.write(node.getIdentifier(), node.getDataAfter().get());
-            break;
-        default:
-            throw new IllegalArgumentException("Unsupported modification " + node.getModificationType());
+
+            return parent;
         }
     }
 }