Mesa (main): util: Replace recursive DFS with iterative implementation

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Tue Aug 17 18:32:17 UTC 2021


Module: Mesa
Branch: main
Commit: dcf1d8d7a4fe5657a24d4a6799532a39aa117977
URL:    http://cgit.freedesktop.org/mesa/mesa/commit/?id=dcf1d8d7a4fe5657a24d4a6799532a39aa117977

Author: Matt Turner <mattst88 at gmail.com>
Date:   Wed Aug  4 18:47:02 2021 -0700

util: Replace recursive DFS with iterative implementation

Doesn't fix, but improves the situation in issue #5163. The
VK.spirv_assembly.instruction.graphics.spirv_ids_abuse.lots_ids_* tests
emit about 160k instructions. ir3_sched blows out the stack after
dag_traverse_bottom_up_node reaches a depth of about 130k frames.

This patch replaces the recursively-implemented post-order traversal
with an iterative implementation using a stack, allowing us to process
DAGs as large as memory can hold.

Definitely makes you appreciate the elegance of recursion...

Reviewed-by: Emma Anholt <emma at anholt.net>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/12232>

---

 src/util/dag.c | 50 ++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 44 insertions(+), 6 deletions(-)

diff --git a/src/util/dag.c b/src/util/dag.c
index 8f7cdd6348e..8e8d2d68506 100644
--- a/src/util/dag.c
+++ b/src/util/dag.c
@@ -111,12 +111,50 @@ dag_traverse_bottom_up_node(struct dag_node *node,
    if (_mesa_set_search(state->seen, node))
       return;
 
-   util_dynarray_foreach(&node->edges, struct dag_edge, edge) {
-      dag_traverse_bottom_up_node(edge->child, cb, state);
-   }
-
-   cb(node, state->data);
-   _mesa_set_add(state->seen, node);
+   struct util_dynarray stack;
+   util_dynarray_init(&stack, NULL);
+
+   do {
+      assert(node);
+
+      while (node->edges.size != 0) {
+         util_dynarray_append(&stack, struct dag_node *, node);
+
+         /* Push unprocessed children onto stack in reverse order. Note that
+          * it's possible for any of the children nodes to already be on the
+          * stack.
+          */
+         util_dynarray_foreach_reverse(&node->edges, struct dag_edge, edge) {
+            if (!_mesa_set_search(state->seen, edge->child)) {
+               util_dynarray_append(&stack, struct dag_node *, edge->child);
+            }
+         }
+
+         /* Get last element pushed: either left-most child or current node.
+          * If it's the current node, that means that we've processed all its
+          * children already.
+          */
+         struct dag_node *top = util_dynarray_pop(&stack, struct dag_node *);
+         if (top == node)
+            break;
+         node = top;
+      }
+
+      /* Process the node */
+      cb(node, state->data);
+      _mesa_set_add(state->seen, node);
+
+      /* Find the next unprocessed node in the stack */
+      do {
+         node = NULL;
+         if (stack.size == 0)
+            break;
+
+         node = util_dynarray_pop(&stack, struct dag_node *);
+      } while (_mesa_set_search(state->seen, node));
+   } while (node);
+
+   util_dynarray_fini(&stack);
 }
 
 /**



More information about the mesa-commit mailing list