Browse Source

Further NodePath::find() optimizations

David Rose 22 years ago
parent
commit
90bf34ddcf

+ 0 - 3
panda/src/pgraph/Sources.pp

@@ -41,7 +41,6 @@
     depthWriteAttrib.I depthWriteAttrib.h \
     directionalLight.I directionalLight.h \
     drawCullHandler.I drawCullHandler.h \
-    findApproxLevel.I findApproxLevel.h \
     findApproxLevelEntry.I findApproxLevelEntry.h \
     findApproxPath.I findApproxPath.h \
     fog.I fog.h \
@@ -129,7 +128,6 @@
     depthWriteAttrib.cxx \
     directionalLight.cxx \
     drawCullHandler.cxx \
-    findApproxLevel.cxx \
     findApproxLevelEntry.cxx \
     findApproxPath.cxx \
     fog.cxx \
@@ -267,7 +265,6 @@
     workingNodePath.I workingNodePath.h
 
 // No need to install these.
-//    findApproxLevel.I findApproxLevel.h \
 //    findApproxLevelEntry.I findApproxLevelEntry.h \
 //    findApproxPath.I findApproxPath.h \
 

+ 0 - 29
panda/src/pgraph/findApproxLevel.I

@@ -1,29 +0,0 @@
-// Filename: findApproxLevel.I
-// Created by:  drose (13Mar02)
-//
-////////////////////////////////////////////////////////////////////
-//
-// PANDA 3D SOFTWARE
-// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
-//
-// All use of this software is subject to the terms of the Panda 3d
-// Software license.  You should have received a copy of this license
-// along with this source code; you will also find a current copy of
-// the license at http://etc.cmu.edu/panda3d/docs/license/ .
-//
-// To contact the maintainers of this program write to
-// [email protected] .
-//
-////////////////////////////////////////////////////////////////////
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: FindApproxLevel::add_entry
-//       Access: Public
-//  Description: Adds a new entry to the level.
-////////////////////////////////////////////////////////////////////
-INLINE void FindApproxLevel::
-add_entry(const FindApproxLevelEntry &entry) {
-  _v.push_back(entry);
-}
-

+ 0 - 34
panda/src/pgraph/findApproxLevel.cxx

@@ -1,34 +0,0 @@
-// Filename: findApproxLevel.cxx
-// Created by:  drose (13Mar02)
-//
-////////////////////////////////////////////////////////////////////
-//
-// PANDA 3D SOFTWARE
-// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
-//
-// All use of this software is subject to the terms of the Panda 3d
-// Software license.  You should have received a copy of this license
-// along with this source code; you will also find a current copy of
-// the license at http://etc.cmu.edu/panda3d/docs/license/ .
-//
-// To contact the maintainers of this program write to
-// [email protected] .
-//
-////////////////////////////////////////////////////////////////////
-
-#include "findApproxLevel.h"
-
-////////////////////////////////////////////////////////////////////
-//     Function: FindApproxLevel::write
-//       Access: Public
-//  Description: Shows the entire contents of the level, one entry per
-//               line.  For debugging only.
-////////////////////////////////////////////////////////////////////
-void FindApproxLevel::
-write(ostream &out) const {
-  Vec::const_iterator vi;
-  for (vi = _v.begin(); vi != _v.end(); ++vi) {
-    (*vi).output(out);
-    out << "\n";
-  }
-}

+ 0 - 46
panda/src/pgraph/findApproxLevel.h

@@ -1,46 +0,0 @@
-// Filename: findApproxLevel.h
-// Created by:  drose (13Mar02)
-//
-////////////////////////////////////////////////////////////////////
-//
-// PANDA 3D SOFTWARE
-// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
-//
-// All use of this software is subject to the terms of the Panda 3d
-// Software license.  You should have received a copy of this license
-// along with this source code; you will also find a current copy of
-// the license at http://etc.cmu.edu/panda3d/docs/license/ .
-//
-// To contact the maintainers of this program write to
-// [email protected] .
-//
-////////////////////////////////////////////////////////////////////
-
-#ifndef FINDAPPROXLEVEL_H
-#define FINDAPPROXLEVEL_H
-
-#include "pandabase.h"
-
-#include "findApproxLevelEntry.h"
-#include "pvector.h"
-
-////////////////////////////////////////////////////////////////////
-//       Class : FindApproxLevel
-// Description : This class is local to this package only; it doesn't
-//               get exported.  It maintains the list of nodes
-//               find_approx() considers for each level of the scene
-//               graph it visits, in its breadth-first search.
-////////////////////////////////////////////////////////////////////
-class FindApproxLevel {
-public:
-  INLINE void add_entry(const FindApproxLevelEntry &entry);
-
-  void write(ostream &out) const;
-
-  typedef pvector<FindApproxLevelEntry> Vec;
-  Vec _v;
-};
-
-#include "findApproxLevel.I"
-
-#endif

+ 69 - 2
panda/src/pgraph/findApproxLevelEntry.I

@@ -28,6 +28,25 @@ FindApproxLevelEntry(const WorkingNodePath &node_path, FindApproxPath &approx_pa
   _approx_path(approx_path)
 {
   _i = 0;
+  _next = NULL;
+  nassertv(_node_path.is_valid());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FindApproxLevelEntry::Constructor
+//       Access: Public
+//  Description: This constructor is used to construct the next entry
+//               based on a child node of the previous entry's node.
+////////////////////////////////////////////////////////////////////
+INLINE FindApproxLevelEntry::
+FindApproxLevelEntry(const FindApproxLevelEntry &parent,
+                     PandaNode *child_node, int i,
+                     FindApproxLevelEntry *next) :
+  _node_path(parent._node_path, child_node),
+  _i(i),
+  _approx_path(parent._approx_path),
+  _next(next)
+{
   nassertv(_node_path.is_valid());
 }
 
@@ -37,11 +56,12 @@ FindApproxLevelEntry(const WorkingNodePath &node_path, FindApproxPath &approx_pa
 //  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE FindApproxLevelEntry::
-FindApproxLevelEntry(const FindApproxLevelEntry &copy, int increment) :
+FindApproxLevelEntry(const FindApproxLevelEntry &copy) :
   _node_path(copy._node_path),
-  _i(copy._i + increment),
+  _i(copy._i),
   _approx_path(copy._approx_path)
 {
+  _next = NULL;
   nassertv(_node_path.is_valid());
 }
 
@@ -81,3 +101,50 @@ INLINE bool FindApproxLevelEntry::
 is_solution(int increment) const {
   return (_i + increment >= _approx_path.get_num_components());
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: FindApproxLevelEntry::operator new
+//       Access: Public
+//  Description: Allocates the memory for a new FindApproxLevelEntry.
+//               This is specialized here to provide for fast
+//               allocation of these things.
+////////////////////////////////////////////////////////////////////
+INLINE void *FindApproxLevelEntry::
+operator new(size_t size) {
+  if (_deleted_chain != (FindApproxLevelEntry *)NULL) {
+    FindApproxLevelEntry *obj = _deleted_chain;
+    _deleted_chain = _deleted_chain->_next;
+    return obj;
+  }
+#ifndef NDEBUG
+  _num_ever_allocated++;
+#endif  // NDEBUG
+  return ::operator new(size);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FindApproxLevelEntry::operator delete
+//       Access: Public
+//  Description: Frees the memory for a deleted FindApproxLevelEntry.
+//               This is specialized here to provide for fast
+//               allocation of these things.
+////////////////////////////////////////////////////////////////////
+INLINE void FindApproxLevelEntry::
+operator delete(void *ptr) {
+  FindApproxLevelEntry *obj = (FindApproxLevelEntry *)ptr;
+  obj->_next = _deleted_chain;
+  _deleted_chain = obj;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FindApproxLevelEntry::get_num_ever_allocated
+//       Access: Published, Static
+//  Description: Returns the number of FindApproxLevelEntry pointers
+//               ever simultaneously allocated; these are now either
+//               in active use or have been recycled into the deleted
+//               FindApproxLevelEntry pool to be used again.
+////////////////////////////////////////////////////////////////////
+INLINE int FindApproxLevelEntry::
+get_num_ever_allocated() {
+  return _num_ever_allocated;
+}

+ 58 - 34
panda/src/pgraph/findApproxLevelEntry.cxx

@@ -19,6 +19,10 @@
 #include "findApproxLevelEntry.h"
 #include "nodePathCollection.h"
 #include "pandaNode.h"
+#include "indent.h"
+
+FindApproxLevelEntry *FindApproxLevelEntry::_deleted_chain = (FindApproxLevelEntry *)NULL;
+int FindApproxLevelEntry::_num_ever_allocated = 0;
 
 
 ////////////////////////////////////////////////////////////////////
@@ -40,14 +44,49 @@ output(ostream &out) const {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: FindApproxLevelEntry::consider_node
+//     Function: FindApproxLevelEntry::write_level
 //       Access: Public
-//  Description:
+//  Description: Writes the entire level (a linked list of entries
+//               beginning at this entry).  For debugging only.
 ////////////////////////////////////////////////////////////////////
 void FindApproxLevelEntry::
-consider_node(NodePathCollection &result, FindApproxLevel &next_level,
+write_level(ostream &out, int indent_level) const {
+  for (const FindApproxLevelEntry *entry = this;
+       entry != (const FindApproxLevelEntry *)NULL;
+       entry = entry->_next) {
+    indent(out, indent_level);
+    out << *entry << "\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FindApproxLevelEntry::consider_node
+//       Access: Public
+//  Description: Considers the node represented by the entry for
+//               matching the find path.  If a solution is found, it
+//               is added to result; if the children of this node
+//               should be considered, the appropriate entries are
+//               added to next_level.
+//
+//               The return value is true if result now contains
+//               max_matches solutions, or false if we should keep
+//               looking.
+////////////////////////////////////////////////////////////////////
+bool FindApproxLevelEntry::
+consider_node(NodePathCollection &result, FindApproxLevelEntry *&next_level,
               int max_matches, int increment) const {
-  nassertv(_i + increment < _approx_path.get_num_components());
+  if (is_solution(increment)) {
+    // If the entry represents a solution, save it and we're done with
+    // the entry.
+    result.add_path(_node_path.get_node_path());
+    if (max_matches > 0 && result.get_num_paths() >= max_matches) { 
+      return true;
+    }
+
+    return false;
+  }
+
+  // If the entry is not itself a solution, consider its children.
 
   if (_approx_path.is_component_match_many(_i + increment)) {
     // Match any number, zero or more, levels of nodes.  This is the
@@ -64,32 +103,24 @@ consider_node(NodePathCollection &result, FindApproxLevel &next_level,
     // FindApproxLevelEntry objects.  Instead, we pass around the
     // increment parameter, which increments _i on the fly.
 
-    if (is_solution(increment + 1)) {
-      // Does this now represent a solution?
-      result.add_path(_node_path.get_node_path());
-      if (max_matches > 0 && result.get_num_paths() >= max_matches) { 
-        return;
-      }
-    } else {
-      consider_node(result, next_level, max_matches, increment + 1);
+    if (consider_node(result, next_level, max_matches, increment + 1)) {
+      return true;
     }
   }
 
   PandaNode *this_node = _node_path.node();
-  nassertv(this_node != (PandaNode *)NULL);
+  nassertr(this_node != (PandaNode *)NULL, false);
 
   bool stashed_only = next_is_stashed(increment);
 
   if (!stashed_only) {
     // Check the normal list of children.
-    int num_children = this_node->get_num_children();
+    PandaNode::Children children = this_node->get_children();
+    int num_children = children.get_num_children();
     for (int i = 0; i < num_children; i++) {
-      PandaNode *child_node = this_node->get_child(i);
+      PandaNode *child_node = children.get_child(i);
       
-      consider_next_step(result, child_node, next_level, max_matches, increment);
-      if (max_matches > 0 && result.get_num_paths() >= max_matches) {
-        return;
-      }
+      consider_next_step(child_node, next_level, increment);
     }
   }
 
@@ -99,12 +130,11 @@ consider_node(NodePathCollection &result, FindApproxLevel &next_level,
     for (int i = 0; i < num_stashed; i++) {
       PandaNode *stashed_node = this_node->get_stashed(i);
       
-      consider_next_step(result, stashed_node, next_level, max_matches, increment);
-      if (max_matches > 0 && result.get_num_paths() >= max_matches) {
-        return;
-      }
+      consider_next_step(stashed_node, next_level, increment);
     }
   }
+
+  return false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -115,12 +145,9 @@ consider_node(NodePathCollection &result, FindApproxLevel &next_level,
 //               of the path.  If it matches, generates whatever
 //               additional entries are appropriate and stores them in
 //               next_level.
-//
-//               If a complete solution is found, stores it in result.
 ////////////////////////////////////////////////////////////////////
 void FindApproxLevelEntry::
-consider_next_step(NodePathCollection &result, PandaNode *child_node,
-                   FindApproxLevel &next_level, int max_matches,
+consider_next_step(PandaNode *child_node, FindApproxLevelEntry *&next_level, 
                    int increment) const {
   if (!_approx_path.return_hidden() &&
       child_node->get_draw_mask().is_zero()) {
@@ -139,17 +166,14 @@ consider_next_step(NodePathCollection &result, PandaNode *child_node,
     // And now we just add the next entry without incrementing its
     // path entry.
 
-    FindApproxLevelEntry next(*this, increment);
-    next._node_path = WorkingNodePath(_node_path, child_node);
-    next_level.add_entry(next);
+    next_level = new FindApproxLevelEntry
+      (*this, child_node, _i + increment, next_level);
 
   } else {
     if (_approx_path.matches_component(_i + increment, child_node)) {
       // That matched, and it consumes one path entry.
-      FindApproxLevelEntry next(*this, increment);
-      next._i++;
-      next._node_path = WorkingNodePath(_node_path, child_node);
-      next_level.add_entry(next);
+      next_level = new FindApproxLevelEntry
+        (*this, child_node, _i + increment + 1, next_level);
     }
   }
 }

+ 24 - 6
panda/src/pgraph/findApproxLevelEntry.h

@@ -24,7 +24,6 @@
 #include "findApproxPath.h"
 #include "workingNodePath.h"
 
-class FindApproxLevel;
 class NodePathCollection;
 
 ////////////////////////////////////////////////////////////////////
@@ -38,19 +37,33 @@ class FindApproxLevelEntry {
 public:
   INLINE FindApproxLevelEntry(const WorkingNodePath &node_path,
                               FindApproxPath &approx_path);
-  INLINE FindApproxLevelEntry(const FindApproxLevelEntry &copy, int increment = 0);
+  INLINE FindApproxLevelEntry(const FindApproxLevelEntry &parent,
+                              PandaNode *child_node, int i,
+                              FindApproxLevelEntry *next);
+  INLINE FindApproxLevelEntry(const FindApproxLevelEntry &copy);
   INLINE void operator = (const FindApproxLevelEntry &copy);
 
   INLINE bool next_is_stashed(int increment) const;
 
-  void consider_node(NodePathCollection &result, FindApproxLevel &next_level,
+  bool consider_node(NodePathCollection &result, 
+                     FindApproxLevelEntry *&next_level,
                      int max_matches, int increment) const;
-  void consider_next_step(NodePathCollection &result,
-                          PandaNode *child_node, FindApproxLevel &next_level,
-                          int max_matches, int increment) const;
+  void consider_next_step(PandaNode *child_node, 
+                          FindApproxLevelEntry *&next_level,
+                          int increment) const;
   INLINE bool is_solution(int increment) const;
 
+  // We will allocate and destroy thousands of these during a typical
+  // NodePath::find() or find_all_matches() operation.  As an
+  // optimization, then, we implement operator new and delete here to
+  // minimize this overhead.
+  INLINE void *operator new(size_t size);
+  INLINE void operator delete(void *ptr);
+
+  INLINE static int get_num_ever_allocated();
+
   void output(ostream &out) const;
+  void write_level(ostream &out, int indent_level) const;
 
   // _node_path represents the most recent node that we have
   // previously accepted as being a partial solution.
@@ -62,6 +75,11 @@ public:
   // solution.
   int _i;
   FindApproxPath &_approx_path;
+  FindApproxLevelEntry *_next;
+
+private:
+  static FindApproxLevelEntry *_deleted_chain;
+  static int _num_ever_allocated;
 };
 
 INLINE ostream &

+ 77 - 45
panda/src/pgraph/nodePath.cxx

@@ -20,7 +20,6 @@
 #include "nodePathCollection.h"
 #include "findApproxPath.h"
 #include "findApproxLevelEntry.h"
-#include "findApproxLevel.h"
 #include "config_pgraph.h"
 #include "colorAttrib.h"
 #include "colorScaleAttrib.h"
@@ -3701,64 +3700,97 @@ find_matches(NodePathCollection &result, FindApproxPath &approx_path,
       << "Attempt to extend an empty NodePath by: " << approx_path << ".\n";
     return;
   }
-  FindApproxLevelEntry start(WorkingNodePath(*this), approx_path);
-  nassertv(start._node_path.is_valid());
-  FindApproxLevel level;
-  level.add_entry(start);
-  r_find_matches(result, level, max_matches, _max_search_depth);
+
+  // We start with just one entry on the level.
+  FindApproxLevelEntry *level = 
+    new FindApproxLevelEntry(WorkingNodePath(*this), approx_path);
+  nassertv(level->_node_path.is_valid());
+
+  find_matches(result, level, max_matches);
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: NodePath::r_find_matches
+//     Function: NodePath::find_matches
 //       Access: Private
-//  Description: The recursive implementation of find_matches.
+//  Description: The fundamental implementation of find_matches(),
+//               given a starting level (a linked list of
+//               FindApproxLevelEntry objects).
 ////////////////////////////////////////////////////////////////////
 void NodePath::
-r_find_matches(NodePathCollection &result,
-               const FindApproxLevel &level,
-               int max_matches, int num_levels_remaining) const {
-  if (pgraph_cat.is_debug()) {
-    pgraph_cat.debug()
-      << "r_find_matches(" << result << ", level, "
-      << max_matches << ", " << num_levels_remaining << ")\n";
-    level.write(pgraph_cat.debug(false));
-  }
+find_matches(NodePathCollection &result, FindApproxLevelEntry *level,
+             int max_matches) const {
+  
+  int num_levels_remaining = _max_search_depth;
 
-  // Go on to the next level.  If we exceeded the requested maximum
-  // depth (or if there are no more levels to visit), stop.
-  if (num_levels_remaining <= 0 || level._v.empty()) {
-    return;
-  }
-  num_levels_remaining--;
+  FindApproxLevelEntry *deleted_entries = NULL;
 
-  FindApproxLevel next_level;
-  bool okflag = true;
+  while (num_levels_remaining > 0 && level != NULL) {
+    if (pgraph_cat.is_debug()) {
+      pgraph_cat.debug()
+        << "find_matches pass: " << result << ", "
+        << max_matches << ", " << num_levels_remaining << "\n";
+      level->write_level(pgraph_cat.debug(false), 4);
+    }
 
-  // For each node in the current level, build up the set of possible
-  // matches in the next level.
-  FindApproxLevel::Vec::const_iterator li;
-  for (li = level._v.begin(); li != level._v.end() && okflag; ++li) {
-    const FindApproxLevelEntry &entry = (*li);
+    num_levels_remaining--;
 
-    if (entry.is_solution(0)) {
-      // Does this entry already represent a solution?
-      result.add_path(entry._node_path.get_node_path());
-    } else {
-      entry.consider_node(result, next_level, max_matches, 0);
-    }
+    FindApproxLevelEntry *next_level = NULL;
 
-    if (max_matches > 0 && result.get_num_paths() >= max_matches) {
-      // Really, we just want to return here.  But returning from
-      // within the conditional within the for loop seems to sometimes
-      // cause a compiler fault in GCC.  We'll use a semaphore
-      // variable instead.
-      okflag = false;
+    // For each node in the current level, build up the set of possible
+    // matches in the next level.
+    FindApproxLevelEntry *entry = level;
+    while (entry != (FindApproxLevelEntry *)NULL) {
+      if (entry->consider_node(result, next_level, max_matches, 0)) {
+        // If we found the requisite number of matches, we can stop.
+        // Delete all remaining entries and return immediately.
+
+        while (entry != (FindApproxLevelEntry *)NULL) {
+          FindApproxLevelEntry *next = entry->_next;
+          delete entry;
+          entry = next;
+        }
+        while (next_level != (FindApproxLevelEntry *)NULL) {
+          FindApproxLevelEntry *next = next_level->_next;
+          delete next_level;
+          next_level = next;
+        }
+        while (deleted_entries != (FindApproxLevelEntry *)NULL) {
+          FindApproxLevelEntry *next = deleted_entries->_next;
+          delete deleted_entries;
+          deleted_entries = next;
+        }
+        return;
+      }
+
+      // Move the entry to the delete chain so we can delete it before
+      // we return from this method.  (We can't delete it immediately,
+      // because there might be WorkingNodePaths in the next_level
+      // that reference the WorkingNodePath object within the entry.)
+      FindApproxLevelEntry *next = entry->_next;
+      entry->_next = deleted_entries;
+      deleted_entries = entry;
+
+      entry = next;
     }
+    
+    // Make sure the remaining entries from this level are added to
+    // the delete chain.
+    while (entry != (FindApproxLevelEntry *)NULL) {
+      FindApproxLevelEntry *next = entry->_next;
+      entry->_next = deleted_entries;
+      deleted_entries = entry;
+
+      entry = next;
+    }
+
+    level = next_level;
   }
 
-  // Now recurse on the next level.
-  if (okflag) {
-    r_find_matches(result, next_level, max_matches, num_levels_remaining);
+  // Now it's safe to delete all entries on the delete chain.
+  while (deleted_entries != (FindApproxLevelEntry *)NULL) {
+    FindApproxLevelEntry *next = deleted_entries->_next;
+    delete deleted_entries;
+    deleted_entries = next;
   }
 }
 

+ 4 - 4
panda/src/pgraph/nodePath.h

@@ -33,8 +33,8 @@
 
 class NodePathCollection;
 class TextureCollection;
-class FindApproxLevel;
 class FindApproxPath;
+class FindApproxLevelEntry;
 class Texture;
 class Material;
 class Fog;
@@ -621,9 +621,9 @@ private:
   void find_matches(NodePathCollection &result,
                     FindApproxPath &approx_path,
                     int max_matches) const;
-  void r_find_matches(NodePathCollection &result,
-                      const FindApproxLevel &level,
-                      int max_matches, int num_levels_remaining) const;
+  void find_matches(NodePathCollection &result, 
+                    FindApproxLevelEntry *level,
+                    int max_matches) const;
 
   void r_adjust_all_priorities(PandaNode *node, int adjustment);
 

+ 7 - 6
panda/src/pgraph/nodePathCollection.cxx

@@ -18,7 +18,7 @@
 
 #include "nodePathCollection.h"
 #include "findApproxPath.h"
-#include "findApproxLevel.h"
+#include "findApproxLevelEntry.h"
 
 #include "indent.h"
 
@@ -278,13 +278,14 @@ find_all_matches(const string &path) const {
   FindApproxPath approx_path;
   if (approx_path.add_string(path)) {
     if (!is_empty()) {
-      FindApproxLevel level;
+      FindApproxLevelEntry *level = NULL;
       for (int i = 0; i < get_num_paths(); i++) {
-        FindApproxLevelEntry start(get_path(i), approx_path);
-        level.add_entry(start);
+        FindApproxLevelEntry *start = 
+          new FindApproxLevelEntry(get_path(i), approx_path);
+        start->_next = level;
+        level = start;
       }
-      get_path(0).r_find_matches(result, level, -1,
-                                 NodePath::get_max_search_depth());
+      get_path(0).find_matches(result, level, -1);
     }
   }
 

+ 0 - 1
panda/src/pgraph/pgraph_composite1.cxx

@@ -32,7 +32,6 @@
 #include "directionalLight.cxx"
 #include "drawCullHandler.cxx"
 #include "findApproxPath.cxx"
-#include "findApproxLevel.cxx"
 #include "findApproxLevelEntry.cxx"
 #include "fog.cxx"
 #include "fogAttrib.cxx"

+ 49 - 9
panda/src/pgraph/test_pgraph.cxx

@@ -18,21 +18,61 @@
 
 #include "pandaNode.h"
 #include "nodePath.h"
+#include "nodePathCollection.h"
+#include "findApproxLevelEntry.h"
+#include "clockObject.h"
+
+NodePath
+build_tree(const string &name, int depth) {
+  NodePath node(name);
+  if (depth > 1) {
+    for (int i = 0; i < 3; i++) {
+      char letter = 'a' + i;
+      string child_name = name + string(1, letter);
+      NodePath child = build_tree(child_name, depth - 1);
+      child.reparent_to(node);
+    }
+  }
+
+  return node;
+}
 
 int 
 main(int argc, char *argv[]) {
-  NodePath h("h");
-  NodePath n1("n1");
-  NodePath n2("n2");
 
-  PT(PandaNode) node = new PandaNode("node");
+  // Build up a tree of height 6.  Each level has three children, so
+  // that there are 3^5 + 3^4 + 3^3 + 3^2 + 3^1 + 3^0 = 364 total nodes.
+
+  NodePath root = build_tree("a", 6);
+
+  NodePath abba = root.find("**/abba");
+  cerr << abba << "\n";
+  cerr << "entries allocated: " << FindApproxLevelEntry::get_num_ever_allocated() << "\n";
+
+  NodePath abaab = root.find("ab/aba/abaa/abaab");
+  cerr << abaab << "\n";
+  cerr << "entries allocated: " << FindApproxLevelEntry::get_num_ever_allocated() << "\n";
+
+  NodePathCollection col = root.find_all_matches("**");
+  cerr << col << "\n";
+  cerr << "entries allocated: " << FindApproxLevelEntry::get_num_ever_allocated() << "\n";
+
+  // Now time a bunch of find operations.
+  ClockObject *clock = ClockObject::get_global_clock();
+  static const int num_ops = 10000;
 
-  NodePath t1 = h.attach_new_node(node);
-  t1.reparent_to(n1);
+  volatile double start, end;
+  {
+    start = clock->get_real_time();
+    for (int i = 0; i < num_ops; i++) {
+      root.find_all_matches("**/aabca");
+    }
+    end = clock->get_real_time();
+  }
 
-  t1 = NodePath();
+  double avg_time = (end - start) / (double)num_ops;
+  cerr << "Find operation took " << avg_time * 1000 << " ms\n";
+  cerr << "entries allocated: " << FindApproxLevelEntry::get_num_ever_allocated() << "\n";
 
-  NodePath t2 = h.attach_new_node(node);
-  t2.reparent_to(n2);
   return 0;
 }