Browse Source

dtoolutil: Add small_vector implementation and use it

This vector implementation does is optimized for the case of having only a small number of elements, which are stored directly on the vector object rather than being allocated from the heap.  If the capacity exceeds this small number, then a heap allocation is done.

This should improve performance in a couple of cases where vectors typically store 1 element and rarely more than that.
rdb 2 years ago
parent
commit
eefb51f510
50 changed files with 937 additions and 106 deletions
  1. 2 3
      direct/src/interval/cInterval.cxx
  2. 2 2
      direct/src/interval/cInterval.h
  3. 7 7
      direct/src/interval/cMetaInterval.cxx
  4. 1 0
      dtool/src/dtoolutil/CMakeLists.txt
  5. 694 0
      dtool/src/dtoolutil/small_vector.I
  6. 132 0
      dtool/src/dtoolutil/small_vector.h
  7. 2 3
      dtool/src/prc/configDeclaration.h
  8. 1 1
      panda/src/chan/animGroup.cxx
  9. 2 1
      panda/src/chan/movingPartBase.h
  10. 2 2
      panda/src/chan/partBundle.cxx
  11. 1 1
      panda/src/chan/partBundle.h
  12. 1 1
      panda/src/chan/partBundleNode.cxx
  13. 2 2
      panda/src/chan/partBundleNode.h
  14. 3 2
      panda/src/char/characterJoint.h
  15. 2 2
      panda/src/display/displayRegion.h
  16. 3 3
      panda/src/display/graphicsEngine.cxx
  17. 2 1
      panda/src/display/graphicsEngine.h
  18. 1 1
      panda/src/egg2pg/eggLoader.cxx
  19. 2 1
      panda/src/event/asyncFuture.h
  20. 2 1
      panda/src/event/event.h
  21. 1 0
      panda/src/event/eventHandler.h
  22. 1 1
      panda/src/express/ordered_vector.I
  23. 2 1
      panda/src/framework/pandaFramework.h
  24. 2 1
      panda/src/framework/windowFramework.h
  25. 1 1
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  26. 2 2
      panda/src/glstuff/glGraphicsStateGuardian_src.h
  27. 2 1
      panda/src/glstuff/glShaderContext_src.h
  28. 15 20
      panda/src/gobj/geomVertexFormat.cxx
  29. 2 1
      panda/src/gobj/geomVertexFormat.h
  30. 2 1
      panda/src/gobj/vertexTransform.h
  31. 2 2
      panda/src/gsgbase/graphicsStateGuardianBase.cxx
  32. 2 1
      panda/src/gsgbase/graphicsStateGuardianBase.h
  33. 1 1
      panda/src/parametrics/curveFitter.cxx
  34. 1 1
      panda/src/pgraph/camera.cxx
  35. 2 1
      panda/src/pgraph/camera.h
  36. 2 1
      panda/src/pgraph/lensNode.h
  37. 2 4
      panda/src/pgraph/pandaNode.cxx
  38. 2 1
      panda/src/pgraph/pandaNode.h
  39. 2 3
      panda/src/pgraph/texGenAttrib.cxx
  40. 4 4
      panda/src/pgraph/textureAttrib.cxx
  41. 2 1
      panda/src/pgraph/textureAttrib.h
  42. 4 6
      panda/src/pgraphnodes/computeNode.cxx
  43. 2 1
      panda/src/pgraphnodes/computeNode.h
  44. 4 6
      panda/src/pgui/pgEntry.cxx
  45. 3 2
      panda/src/pgui/pgEntry.h
  46. 1 1
      panda/src/pipeline/threadSimpleManager.cxx
  47. 1 1
      panda/src/pnmimagetypes/pnmFileTypePNG.cxx
  48. 0 1
      panda/src/putil/simpleHashMap.h
  49. 5 4
      panda/src/tform/buttonThrower.h
  50. 1 1
      panda/src/tform/mouseWatcher.cxx

+ 2 - 3
direct/src/interval/cInterval.cxx

@@ -654,9 +654,8 @@ void CInterval::
 mark_dirty() {
   if (!_dirty) {
     _dirty = true;
-    Parents::iterator pi;
-    for (pi = _parents.begin(); pi != _parents.end(); ++pi) {
-      (*pi)->mark_dirty();
+    for (CInterval *parent : _parents) {
+      parent->mark_dirty();
     }
   }
 }

+ 2 - 2
direct/src/interval/cInterval.h

@@ -16,7 +16,7 @@
 
 #include "directbase.h"
 #include "typedReferenceCount.h"
-#include "pvector.h"
+#include "small_vector.h"
 #include "config_interval.h"
 #include "pStatCollector.h"
 #include "extension.h"
@@ -178,7 +178,7 @@ private:
   // We keep a record of the "parent" intervals (that is, any CMetaInterval
   // objects that keep a pointer to this one) strictly so we can mark all of
   // our parents dirty when this interval gets dirty.
-  typedef pvector<CInterval *> Parents;
+  typedef small_vector<CInterval *> Parents;
   Parents _parents;
 
   static PStatCollector _root_pcollector;

+ 7 - 7
direct/src/interval/cMetaInterval.cxx

@@ -70,9 +70,9 @@ clear_intervals() {
     IntervalDef &def = (*di);
     if (def._c_interval != nullptr) {
       CInterval::Parents::iterator pi =
-        find(def._c_interval->_parents.begin(),
-             def._c_interval->_parents.end(),
-             this);
+        std::find(def._c_interval->_parents.begin(),
+                  def._c_interval->_parents.end(),
+                  this);
       nassertv(pi != def._c_interval->_parents.end());
       def._c_interval->_parents.erase(pi);
     }
@@ -798,7 +798,7 @@ do_event_forward(CMetaInterval::PlaybackEvent *event,
       // Erase the event from either the new active or the current active
       // lists.
       ActiveEvents::iterator ai;
-      ai = find(new_active.begin(), new_active.end(), event->_begin_event);
+      ai = std::find(new_active.begin(), new_active.end(), event->_begin_event);
       if (ai != new_active.end()) {
         new_active.erase(ai);
         // This interval was new this frame; we must invoke it as an instant
@@ -806,7 +806,7 @@ do_event_forward(CMetaInterval::PlaybackEvent *event,
         enqueue_event(event->_n, ET_instant, is_initial);
 
       } else {
-        ai = find(_active.begin(), _active.end(), event->_begin_event);
+        ai = std::find(_active.begin(), _active.end(), event->_begin_event);
         if (ai != _active.end()) {
           _active.erase(ai);
           enqueue_event(event->_n, ET_finalize, is_initial);
@@ -872,14 +872,14 @@ do_event_reverse(CMetaInterval::PlaybackEvent *event,
       // Erase the event from either the new active or the current active
       // lists.
       ActiveEvents::iterator ai;
-      ai = find(new_active.begin(), new_active.end(), event);
+      ai = std::find(new_active.begin(), new_active.end(), event);
       if (ai != new_active.end()) {
         new_active.erase(ai);
         // This interval was new this frame; we invoke it as an instant event.
         enqueue_event(event->_n, ET_reverse_instant, is_initial);
 
       } else {
-        ai = find(_active.begin(), _active.end(), event);
+        ai = std::find(_active.begin(), _active.end(), event);
         if (ai != _active.end()) {
           _active.erase(ai);
           enqueue_event(event->_n, ET_reverse_finalize, is_initial);

+ 1 - 0
dtool/src/dtoolutil/CMakeLists.txt

@@ -13,6 +13,7 @@ set(P3DTOOLUTIL_HEADERS
   panda_getopt.h panda_getopt_long.h panda_getopt_impl.h
   pfstream.h pfstream.I pfstreamBuf.h
   preprocess_argv.h
+  small_vector.h small_vector.I
   string_utils.h string_utils.I
   stringDecoder.h stringDecoder.I
   textEncoder.h textEncoder.I

+ 694 - 0
dtool/src/dtoolutil/small_vector.I

@@ -0,0 +1,694 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file small_vector.I
+ * @author rdb
+ * @date 2023-01-25
+ */
+
+/**
+ *
+ */
+template<class T, unsigned N>
+INLINE small_vector<T, N>::
+small_vector(TypeHandle type_handle) {
+}
+
+/**
+ * Initializer list constructor.
+ */
+template<class T, unsigned N>
+INLINE small_vector<T, N>::
+small_vector(std::initializer_list<T> init) :
+  _storage(init.size() > N ? (T *)PANDA_MALLOC_ARRAY(sizeof(T) * init.size()) : nullptr),
+  _size(init.size()),
+  _capacity(std::max((size_type)N, (size_type)init.size())) {
+
+  T *to = this->data();
+  for (const T &value : init) {
+    new (to++) T(value);
+  }
+}
+
+/**
+ * Copy constructor.
+ */
+template<class T, unsigned N>
+INLINE small_vector<T, N>::
+small_vector(const small_vector &copy) :
+  _storage(copy._size > N ? (T *)PANDA_MALLOC_ARRAY(sizeof(T) * copy._size) : nullptr),
+  _size(copy._size),
+  _capacity(std::max((size_type)N, copy._size)) {
+
+  const T *from = copy.data();
+  T *to = this->data();
+  for (size_type i = 0; i < _size; ++i) {
+    new (to + i) T(from[i]);
+  }
+}
+
+/**
+ * Move constructor.
+ */
+template<class T, unsigned N>
+INLINE small_vector<T, N>::
+small_vector(small_vector &&other) noexcept :
+  _storage(other._storage._large),
+  _size(other._size),
+  _capacity(other._capacity) {
+
+  if (_size <= N) {
+    T *to = _storage._small;
+    T *from = other.data();
+    for (size_type i = 0; i < _size; ++i) {
+      new (to + i) T(std::move(from[i]));
+      from[i].~T();
+    }
+    _capacity = N;
+  }
+  other._storage._large = nullptr;
+  other._size = 0;
+  other._capacity = N;
+}
+
+/**
+ * Destructor.
+ */
+template<class T, unsigned N>
+INLINE small_vector<T, N>::
+~small_vector() {
+  this->clear();
+  if (!this->is_small()) {
+    PANDA_FREE_ARRAY(_storage._large);
+    _capacity = N;
+  }
+}
+
+/**
+ * Copy assignment operator.
+ */
+template<class T, unsigned N>
+INLINE small_vector<T, N> &small_vector<T, N>::
+operator =(const small_vector &copy) {
+  this->clear();
+  this->reserve(copy._size);
+
+  const T *from = copy.data();
+  T *to = this->data();
+  for (size_type i = 0; i < copy._size; ++i) {
+    new (to + i) T(from[i]);
+  }
+  _size = copy._size;
+  return *this;
+}
+
+/**
+ * Move assignment operator.
+ */
+template<class T, unsigned N>
+INLINE small_vector<T, N> &small_vector<T, N>::
+operator =(small_vector &&from) noexcept {
+  if (UNLIKELY(this == &from)) {
+    return *this;
+  }
+
+  this->clear();
+  if (!this->is_small()) {
+    PANDA_FREE_ARRAY(_storage._large);
+    _capacity = N;
+  }
+
+  _storage._large = from._storage._large;
+  _size = from._size;
+  _capacity = from._capacity;
+
+  if (this->is_small()) {
+#if defined(__has_builtin) && __has_builtin(__builtin_assume)
+    __builtin_assume(_size <= N);
+#endif
+    T *to = this->_storage._small;
+    for (size_type i = 0; i < _size; ++i) {
+      new (to + i) T(std::move(from._storage._small[i]));
+      from._storage._small[i].~T();
+    }
+  }
+  from._storage._large = nullptr;
+  from._size = 0;
+  from._capacity = N;
+  return *this;
+}
+
+/**
+ * Returns true if there are no elements in this vector.
+ */
+template<class T, unsigned N>
+constexpr bool small_vector<T, N>::
+empty() const {
+  return _size == 0;
+}
+
+/**
+ * Returns the number of elements stored in the vector.
+ */
+template<class T, unsigned N>
+constexpr typename small_vector<T, N>::size_type small_vector<T, N>::
+size() const {
+  return _size;
+}
+
+/**
+ * Returns the total capacity of the vector.
+ */
+template<class T, unsigned N>
+constexpr typename small_vector<T, N>::size_type small_vector<T, N>::
+capacity() const {
+  return _capacity;
+}
+
+/**
+ * Returns the maximum size of the vector.
+ */
+template<class T, unsigned N>
+constexpr typename small_vector<T, N>::size_type small_vector<T, N>::
+max_size() const {
+  return SIZE_MAX;
+}
+
+/**
+ * Returns the ith element with bounds checking.
+ */
+template<class T, unsigned N>
+INLINE typename small_vector<T, N>::reference small_vector<T, N>::
+at(size_type i) {
+  assert(i < size());
+  return *(this->begin() + i);
+}
+
+/**
+ * Returns the ith element with bounds checking.
+ */
+template<class T, unsigned N>
+INLINE typename small_vector<T, N>::const_reference small_vector<T, N>::
+at(size_type i) const {
+  assert(i < size());
+  return *(this->begin() + i);
+}
+
+/**
+ * Returns the ith element without bounds checking.
+ */
+template<class T, unsigned N>
+ALWAYS_INLINE typename small_vector<T, N>::reference small_vector<T, N>::
+operator [](size_type i) {
+  return *(this->begin() + i);
+}
+
+/**
+ * Returns the ith element without bounds checking.
+ */
+template<class T, unsigned N>
+ALWAYS_INLINE typename small_vector<T, N>::const_reference small_vector<T, N>::
+operator [](size_type i) const {
+  return *(this->begin() + i);
+}
+
+/**
+ * Returns a reference to the first element.
+ */
+template<class T, unsigned N>
+INLINE typename small_vector<T, N>::reference small_vector<T, N>::
+front() {
+  return *(this->begin());
+}
+
+/**
+ * Returns a const reference to the first element.
+ */
+template<class T, unsigned N>
+INLINE typename small_vector<T, N>::const_reference small_vector<T, N>::
+front() const {
+  return *(this->begin());
+}
+
+/**
+ * Returns a reference to the last element.
+ */
+template<class T, unsigned N>
+INLINE typename small_vector<T, N>::reference small_vector<T, N>::
+back() {
+  return *(this->end() - 1);
+}
+
+/**
+ * Returns a const reference to the last element.
+ */
+template<class T, unsigned N>
+INLINE typename small_vector<T, N>::const_reference small_vector<T, N>::
+back() const {
+  return *(this->end() - 1);
+}
+
+/**
+ * Returns a pointer to the data.
+ */
+template<class T, unsigned N>
+ALWAYS_INLINE T *small_vector<T, N>::
+data() {
+  return this->is_small() ? this->_storage._small : this->_storage._large;
+}
+
+/**
+ * Returns a pointer to the data.
+ */
+template<class T, unsigned N>
+ALWAYS_INLINE const T *small_vector<T, N>::
+data() const {
+  return this->is_small() ? this->_storage._small : this->_storage._large;
+}
+
+/**
+ * Returns an iterator to the first element of the vector.
+ */
+template<class T, unsigned N>
+ALWAYS_INLINE typename small_vector<T, N>::iterator small_vector<T, N>::
+begin() {
+  return this->is_small() ? this->_storage._small : this->_storage._large;
+}
+
+/**
+ * Returns an iterator one element past the last element in the vector.
+ */
+template<class T, unsigned N>
+ALWAYS_INLINE typename small_vector<T, N>::iterator small_vector<T, N>::
+end() {
+  return this->begin() + _size;
+}
+
+/**
+ * Returns a const iterator to the first element of the vector.
+ */
+template<class T, unsigned N>
+ALWAYS_INLINE typename small_vector<T, N>::const_iterator small_vector<T, N>::
+begin() const {
+  return this->is_small() ? this->_storage._small : this->_storage._large;
+}
+
+/**
+ * Returns a const iterator one element past the last element in the vector.
+ */
+template<class T, unsigned N>
+ALWAYS_INLINE typename small_vector<T, N>::const_iterator small_vector<T, N>::
+end() const {
+  return this->begin() + _size;
+}
+
+/**
+ * Returns a const iterator to the first element of the vector.
+ */
+template<class T, unsigned N>
+ALWAYS_INLINE typename small_vector<T, N>::const_iterator small_vector<T, N>::
+cbegin() const {
+  return this->is_small() ? this->_storage._small : this->_storage._large;
+}
+
+/**
+ * Returns a const iterator one element past the last element in the vector.
+ */
+template<class T, unsigned N>
+ALWAYS_INLINE typename small_vector<T, N>::const_iterator small_vector<T, N>::
+cend() const {
+  return this->begin() + _size;
+}
+
+/**
+ * Returns a reverse iterator to the end of the vector.
+ */
+template<class T, unsigned N>
+ALWAYS_INLINE typename small_vector<T, N>::reverse_iterator small_vector<T, N>::
+rbegin() {
+  return reverse_iterator(this->end());
+}
+
+/**
+ * Returns a reverse iterator to the beginning of the vector.
+ */
+template<class T, unsigned N>
+ALWAYS_INLINE typename small_vector<T, N>::reverse_iterator small_vector<T, N>::
+rend() {
+  return reverse_iterator(this->begin());
+}
+
+/**
+ * Returns a reverse iterator to the end of the vector.
+ */
+template<class T, unsigned N>
+ALWAYS_INLINE typename small_vector<T, N>::const_reverse_iterator small_vector<T, N>::
+rbegin() const {
+  return const_reverse_iterator(this->end());
+}
+
+/**
+ * Returns a reverse iterator to the beginning of the vector.
+ */
+template<class T, unsigned N>
+ALWAYS_INLINE typename small_vector<T, N>::const_reverse_iterator small_vector<T, N>::
+rend() const {
+  return const_reverse_iterator(this->begin());
+}
+
+/**
+ * Returns a reverse iterator to the end of the vector.
+ */
+template<class T, unsigned N>
+ALWAYS_INLINE typename small_vector<T, N>::const_reverse_iterator small_vector<T, N>::
+crbegin() const {
+  return const_reverse_iterator(this->end());
+}
+
+/**
+ * Returns a reverse iterator to the beginning of the vector.
+ */
+template<class T, unsigned N>
+ALWAYS_INLINE typename small_vector<T, N>::const_reverse_iterator small_vector<T, N>::
+crend() const {
+  return const_reverse_iterator(this->begin());
+}
+
+/**
+ * Removes all elements from the vector.  Does not deallocate storage, consider
+ * calling shrink_to_fit afterwards.
+ */
+template<class T, unsigned N>
+INLINE void small_vector<T, N>::
+clear() {
+  for (T &element : *this) {
+    element.~T();
+  }
+  _size = 0;
+}
+
+/**
+ * Reallocates the size down to the current content size.
+ */
+template<class T, unsigned N>
+INLINE void small_vector<T, N>::
+shrink_to_fit() {
+  if (_capacity > N && _capacity > _size) {
+    T *from = _storage._large;
+    T *to;
+    if (_size > N) {
+      _capacity = _size;
+      to = (T *)PANDA_MALLOC_ARRAY(sizeof(T) * _size);
+    } else {
+      _capacity = N;
+      to = _storage._small;
+    }
+    for (size_type i = 0; i < _size; ++i) {
+      new (to + i) T(std::move(from[i]));
+      from[i].~T();
+    }
+    PANDA_FREE_ARRAY(from);
+    _capacity = std::max(_size, (size_type)N);
+  }
+}
+
+/**
+ * Allocates enough capacity for the given number of elements.
+ */
+template<class T, unsigned N>
+INLINE void small_vector<T, N>::
+reserve(size_type n) {
+  if (n > _capacity) {
+    T *from = this->data();
+    T *to = (T *)PANDA_MALLOC_ARRAY(sizeof(T) * n);
+    for (size_type i = 0; i < _size; ++i) {
+      new (to + i) T(std::move(from[i]));
+      from[i].~T();
+    }
+
+    if (!this->is_small()) {
+      PANDA_FREE_ARRAY(from);
+    }
+
+    //NB. It's not safe to overwrite this until we're done reading from the
+    // internal storage (which overlaps with this pointer).
+    _storage._large = to;
+    _capacity = n;
+  }
+}
+
+/**
+ * Resizes the vector to the given number of elements.
+ */
+template<class T, unsigned N>
+INLINE void small_vector<T, N>::
+resize(size_type n, T value) {
+  if (n > _size) {
+    this->reserve(n);
+
+    T *to = this->data();
+    for (size_type i = _size; i < n; ++i) {
+      new (to + i) T(std::move(value));
+    }
+  }
+  else {
+    T *to = this->data();
+    for (size_type i = n; i < _size; ++i) {
+      to[i].~T();
+    }
+  }
+
+  _size = n;
+}
+
+/**
+ * Constructs a new element at the end of the vector.
+ */
+template<class T, unsigned N>
+template<class... Args>
+INLINE typename small_vector<T, N>::reference small_vector<T, N>::
+emplace_back(Args&&... args) {
+  iterator it = this->append();
+  new (it) T(std::forward<Args>(args)...);
+  return *it;
+}
+
+/**
+ * Appends an element to the end of the vector.
+ */
+template<class T, unsigned N>
+INLINE void small_vector<T, N>::
+push_back(const T &value) {
+  new (this->append()) T(value);
+}
+
+/**
+ * Appends an element to the end of the vector.
+ */
+template<class T, unsigned N>
+INLINE void small_vector<T, N>::
+push_back(T &&value) {
+  new (this->append()) T(std::move(value));
+}
+
+/**
+ * Removes the last element from the vector.  Does not deallocate storage,
+ * consider calling shrink_to_fit afterwards.
+ */
+template<class T, unsigned N>
+INLINE void small_vector<T, N>::
+pop_back() {
+  assert(!this->empty());
+
+  (this->data() + --_size)->~T();
+}
+
+/**
+ * Inserts the given element before the indicated position.
+ */
+template<class T, unsigned N>
+INLINE typename small_vector<T, N>::iterator small_vector<T, N>::
+insert(const_iterator pos, const T &value) {
+  iterator new_pos = this->insert_gap(pos, 1);
+  new (new_pos) T(value);
+  return new_pos;
+}
+
+/**
+ * Inserts the given element before the indicated position.
+ */
+template<class T, unsigned N>
+INLINE typename small_vector<T, N>::iterator small_vector<T, N>::
+insert(const_iterator pos, T &&value) {
+  iterator new_pos = this->insert_gap(pos, 1);
+  new (new_pos) T(std::move(value));
+  return new_pos;
+}
+
+/**
+ * Removes the element at the given position from the vector.  Usually does not
+ * reallocate storage (except when removing from the beginning if the new size
+ * is smaller than the preallocated storage), consider calling shrink_to_fit()
+ * afterwards.
+ */
+template<class T, unsigned N>
+INLINE typename small_vector<T, N>::iterator small_vector<T, N>::
+erase(const_iterator pos) {
+  T *data = this->data();
+  size_type i = pos - data;
+  assert(i < _size);
+  --_size;
+
+  T *to = (T *)pos;
+  T *to_end = data + _size;
+  T *from = to + 1;
+
+  T *free_ptr = nullptr;
+  if (i == 0 && _size <= N && !this->is_small()) {
+    // We're moving everything anyway, just move it to internal storage
+    to = this->_storage._small;
+    to_end = to + _size;
+    _capacity = N;
+    free_ptr = data;
+  }
+
+  pos->~T();
+
+  while (to < to_end) {
+    new (to++) T(std::move(*from));
+    from->~T();
+    ++from;
+  }
+
+  if (free_ptr != nullptr) {
+    PANDA_FREE_ARRAY(free_ptr);
+  }
+
+  return to;
+}
+
+/**
+ * Removes the given range of elements from the vector.  Usually does not
+ * reallocate storage (except when removing from the beginning if the new size
+ * is smaller than the preallocated storage), consider calling shrink_to_fit()
+ * afterwards.
+ */
+template<class T, unsigned N>
+INLINE typename small_vector<T, N>::iterator small_vector<T, N>::
+erase(const_iterator begin, const_iterator end) {
+  if (begin == end) {
+    return (iterator)end;
+  }
+
+  T *data = this->data();
+  size_type i = begin - data;
+  assert(i < _size);
+  _size -= (end - begin);
+  assert(_size <= _capacity);
+
+  T *to = (T *)begin;
+  T *to_end = data + _size;
+  T *from = (T *)end;
+
+  T *free_ptr = nullptr;
+  if (i == 0 && _size <= N && !this->is_small()) {
+    // We're moving everything anyway, just move it to internal storage
+    to = this->_storage._small;
+    to_end = to + _size;
+    _capacity = N;
+    free_ptr = data;
+  }
+
+  while (begin < end) {
+    begin->~T();
+    ++begin;
+  }
+
+  while (to < to_end) {
+    new (to++) T(std::move(*from));
+    from->~T();
+    ++from;
+  }
+
+  if (free_ptr != nullptr) {
+    PANDA_FREE_ARRAY(free_ptr);
+  }
+
+  return to;
+}
+
+/**
+ * Shifts elements right starting from the given position.  The gap remains
+ * uninitialized.
+ */
+template<class T, unsigned N>
+INLINE typename small_vector<T, N>::iterator small_vector<T, N>::
+insert_gap(const_iterator pos, size_type count) {
+  T *data = this->data();
+  size_type offset = pos - data;
+  assert(offset <= _size);
+  _size += count;
+
+  if (_size <= _capacity) {
+    // Just move everything back.
+    for (size_type i = _size - 1; i >= offset + count; --i) {
+      new (data + i) T(std::move(data[i - count]));
+      data[i - count].~T();
+    }
+  }
+  else {
+    // Need to resize, so move everything to the new array.
+    size_type new_cap = std::max(_size, _capacity << 1u);
+
+    T *old_data = data;
+    data = (T *)PANDA_MALLOC_ARRAY(sizeof(T) * new_cap);
+
+    T *from = old_data;
+    T *to = data;
+    for (size_type i = 0; i < offset; ++i) {
+      new (to++) T(std::move(*from));
+      from->~T();
+      ++from;
+    }
+
+    to += count;
+
+    T *to_end = data + _size;
+    while (to < to_end) {
+      new (to++) T(std::move(*from));
+      from->~T();
+      ++from;
+    }
+
+    if (old_data != _storage._small) {
+      PANDA_FREE_ARRAY(old_data);
+    }
+
+    //NB. It's not safe to overwrite this until we're done reading from the
+    // internal storage (which overlaps with this pointer).
+    _storage._large = data;
+    _capacity = new_cap;
+  }
+
+  return data + offset;
+}
+
+/**
+ * Grows the vector in size by one, leaving the new element uninitialized.
+ * Returns an iterator to the back.
+ */
+template<class T, unsigned N>
+INLINE typename small_vector<T, N>::iterator small_vector<T, N>::
+append() {
+  size_type size = _size;
+  if (size == _capacity) {
+    this->reserve(_capacity << 1u);
+  }
+  ++_size;
+  return this->begin() + size;
+}

+ 132 - 0
dtool/src/dtoolutil/small_vector.h

@@ -0,0 +1,132 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file small_vector.h
+ * @author rdb
+ * @date 2023-01-25
+ */
+
+#ifndef SMALL_VECTOR_H
+#define SMALL_VECTOR_H
+
+#include "dtoolbase.h"
+#include "typeHandle.h"
+
+#include <stdint.h>
+#include <utility>
+#include <iterator>
+
+/**
+ * A vector type that is particularly optimized for the case where there is a
+ * small number of elements (by default as many as fit inside the space of a
+ * single pointer, but at least one).  No heap allocations are required as long
+ * as the vector stays below this size.  It also has the same footprint as
+ * std::vector if the element is only a single pointer in size.
+ *
+ * The number of preallocated elements may also be 0, in which case the vector
+ * behaves as a regular vector, only benefiting from the reduced footprint.
+ *
+ * Furthermore, it can be safely used at static init time due to the existence
+ * of a constexpr constructor.
+ *
+ * However, some other operations are probably less efficient.  For example,
+ * swapping a small_vector is more expensive, so this operation is not provided.
+ *
+ * If memory is allocated, it is not automatically deallocated (even if the
+ * vector is resized down to zero) but see shrink_to_fit() to accomplish this.
+ */
+template<class T, unsigned N = (sizeof(T) >= sizeof(void *) ? 1 : sizeof(void *) / sizeof(T))>
+class small_vector {
+public:
+  typedef size_t size_type;
+  typedef ptrdiff_t difference_type;
+  typedef T &reference;
+  typedef const T &const_reference;
+  typedef T *iterator;
+  typedef const T *const_iterator;
+  typedef std::reverse_iterator<iterator> reverse_iterator;
+  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+  ALWAYS_INLINE constexpr small_vector() = default;
+  INLINE small_vector(TypeHandle type_handle);
+  INLINE small_vector(std::initializer_list<T> init);
+  INLINE small_vector(const small_vector &copy);
+  INLINE small_vector(small_vector &&from) noexcept;
+  INLINE ~small_vector();
+
+  INLINE small_vector &operator =(const small_vector &copy);
+  INLINE small_vector &operator =(small_vector &&from) noexcept;
+
+  constexpr bool empty() const;
+  constexpr size_type size() const;
+  constexpr size_type capacity() const;
+  constexpr size_type max_size() const;
+
+  INLINE reference at(size_type i);
+  INLINE const_reference at(size_type i) const;
+  ALWAYS_INLINE reference operator [](size_type i);
+  ALWAYS_INLINE const_reference operator [](size_type i) const;
+
+  ALWAYS_INLINE reference front();
+  ALWAYS_INLINE const_reference front() const;
+  ALWAYS_INLINE reference back();
+  ALWAYS_INLINE const_reference back() const;
+  ALWAYS_INLINE T *data();
+  ALWAYS_INLINE const T *data() const;
+
+  ALWAYS_INLINE iterator begin();
+  ALWAYS_INLINE iterator end();
+  ALWAYS_INLINE const_iterator begin() const;
+  ALWAYS_INLINE const_iterator end() const;
+  ALWAYS_INLINE const_iterator cbegin() const;
+  ALWAYS_INLINE const_iterator cend() const;
+
+  ALWAYS_INLINE reverse_iterator rbegin();
+  ALWAYS_INLINE reverse_iterator rend();
+  ALWAYS_INLINE const_reverse_iterator rbegin() const;
+  ALWAYS_INLINE const_reverse_iterator rend() const;
+  ALWAYS_INLINE const_reverse_iterator crbegin() const;
+  ALWAYS_INLINE const_reverse_iterator crend() const;
+
+  INLINE void clear();
+  INLINE void shrink_to_fit();
+  INLINE void reserve(size_type n);
+  INLINE void resize(size_type n, T value = T());
+
+  template<class... Args>
+  INLINE reference emplace_back(Args&&... args);
+  INLINE void push_back(const T &value);
+  INLINE void push_back(T &&value);
+  INLINE void pop_back();
+  INLINE iterator insert(const_iterator pos, const T &value);
+  INLINE iterator insert(const_iterator pos, T &&value);
+  INLINE iterator erase(const_iterator pos);
+  INLINE iterator erase(const_iterator begin, const_iterator end);
+
+private:
+  INLINE iterator insert_gap(const_iterator pos, size_type count);
+  INLINE iterator append();
+
+  constexpr bool is_small() const { return LIKELY(_capacity <= N); }
+
+  union Storage {
+    constexpr Storage() : _large(nullptr) {}
+    constexpr Storage(T *large) : _large(large) {}
+    ~Storage() {}
+
+    T *_large;
+    T _small[N];
+  } _storage;
+
+  size_type _size = 0;
+  size_type _capacity = N;
+};
+
+#include "small_vector.I"
+
+#endif

+ 2 - 3
dtool/src/prc/configDeclaration.h

@@ -20,8 +20,7 @@
 #include "vector_string.h"
 #include "numeric_types.h"
 #include "filename.h"
-
-#include <vector>
+#include "small_vector.h"
 
 class ConfigVariableCore;
 
@@ -114,7 +113,7 @@ private:
     short _flags;
   };
 
-  typedef std::vector<Word> Words;
+  typedef small_vector<Word> Words;
   Words _words;
   bool _got_words;
 

+ 1 - 1
panda/src/chan/animGroup.cxx

@@ -158,7 +158,7 @@ public:
  */
 void AnimGroup::
 sort_descendants() {
-  sort(_children.begin(), _children.end(), AnimGroupAlphabeticalOrder());
+  std::sort(_children.begin(), _children.end(), AnimGroupAlphabeticalOrder());
 
   Children::iterator ci;
   for (ci = _children.begin(); ci != _children.end(); ++ci) {

+ 2 - 1
panda/src/chan/movingPartBase.h

@@ -19,6 +19,7 @@
 #include "partGroup.h"
 #include "partBundle.h"
 #include "animChannelBase.h"
+#include "small_vector.h"
 
 /**
  * This is the base class for a single animatable piece that may be bound to
@@ -75,7 +76,7 @@ protected:
   virtual void determine_effective_channels(const CycleData *root_cdata);
 
   // This is the vector of all channels bound to this part.
-  typedef pvector< PT(AnimChannelBase) > Channels;
+  typedef small_vector< PT(AnimChannelBase) > Channels;
   Channels _channels;
 
   // This is the single channel that has an effect on this part, as determined

+ 2 - 2
panda/src/chan/partBundle.cxx

@@ -632,7 +632,7 @@ do_bind_anim(AnimControl *control, AnimBundle *anim,
  */
 void PartBundle::
 add_node(PartBundleNode *node) {
-  nassertv(find(_nodes.begin(), _nodes.end(), node) == _nodes.end());
+  nassertv(std::find(_nodes.begin(), _nodes.end(), node) == _nodes.end());
   _nodes.push_back(node);
 }
 
@@ -643,7 +643,7 @@ add_node(PartBundleNode *node) {
  */
 void PartBundle::
 remove_node(PartBundleNode *node) {
-  Nodes::iterator ni = find(_nodes.begin(), _nodes.end(), node);
+  Nodes::iterator ni = std::find(_nodes.begin(), _nodes.end(), node);
   nassertv(ni != _nodes.end());
   _nodes.erase(ni);
 }

+ 1 - 1
panda/src/chan/partBundle.h

@@ -168,7 +168,7 @@ private:
 
   COWPT(AnimPreloadTable) _anim_preload;
 
-  typedef pvector<PartBundleNode *> Nodes;
+  typedef small_vector<PartBundleNode *> Nodes;
   Nodes _nodes;
 
   typedef pmap<WCPT(TransformState), WPT(PartBundle), std::owner_less<WCPT(TransformState)> > AppliedTransforms;

+ 1 - 1
panda/src/chan/partBundleNode.cxx

@@ -97,7 +97,7 @@ add_bundle(PartBundle *bundle) {
  */
 void PartBundleNode::
 do_add_bundle_handle(PartBundleHandle *handle) {
-  Bundles::iterator bi = find(_bundles.begin(), _bundles.end(), handle);
+  Bundles::iterator bi = std::find(_bundles.begin(), _bundles.end(), handle);
   if (bi != _bundles.end()) {
     // This handle is already within the node.
     return;

+ 2 - 2
panda/src/chan/partBundleNode.h

@@ -21,7 +21,7 @@
 #include "lightMutex.h"
 #include "pandaNode.h"
 #include "dcast.h"
-#include "pvector.h"
+#include "small_vector.h"
 
 /**
  * This is a node that contains a pointer to an PartBundle.  Like
@@ -66,7 +66,7 @@ protected:
 
 protected:
   LightMutex _lock;
-  typedef pvector< PT(PartBundleHandle) > Bundles;
+  typedef small_vector< PT(PartBundleHandle) > Bundles;
   Bundles _bundles;
 
 public:

+ 3 - 2
panda/src/char/characterJoint.h

@@ -21,6 +21,7 @@
 #include "pandaNode.h"
 #include "nodePathCollection.h"
 #include "ordered_vector.h"
+#include "small_vector.h"
 
 class JointVertexTransform;
 class Character;
@@ -77,11 +78,11 @@ private:
   // Not a reference-counted pointer.
   Character *_character;
 
-  typedef ov_set< PT(PandaNode) > NodeList;
+  typedef ov_set<PT(PandaNode), std::less<PT(PandaNode)>, small_vector<PT(PandaNode)> > NodeList;
   NodeList _net_transform_nodes;
   NodeList _local_transform_nodes;
 
-  typedef ov_set<JointVertexTransform *> VertexTransforms;
+  typedef ov_set<JointVertexTransform *, std::less<JointVertexTransform *>, small_vector<JointVertexTransform *> > VertexTransforms;
   VertexTransforms _vertex_transforms;
 
 public:

+ 2 - 2
panda/src/display/displayRegion.h

@@ -37,7 +37,7 @@
 #include "cullTraverser.h"
 #include "callbackObject.h"
 #include "luse.h"
-#include "epvector.h"
+#include "small_vector.h"
 
 class GraphicsOutput;
 class GraphicsPipe;
@@ -197,7 +197,7 @@ public:
     LVecBase4i _pixels;
     LVecBase4i _pixels_i;
   };
-  typedef epvector<Region> Regions;
+  typedef small_vector<Region> Regions;
 
 private:
   class CData;

+ 3 - 3
panda/src/display/graphicsEngine.cxx

@@ -777,7 +777,7 @@ render_frame() {
         }
       }
     }
-    _windows.swap(new_windows);
+    _windows = std::move(new_windows);
 
     // Go ahead and release any textures' ram images for textures that were
     // drawn in the previous frame.
@@ -969,7 +969,7 @@ open_windows() {
 
   ReMutexHolder holder(_lock, current_thread);
 
-  pvector<PT(GraphicsOutput)> new_windows;
+  NewWindows new_windows;
   {
     MutexHolder new_windows_holder(_new_windows_lock, current_thread);
     if (_new_windows.empty()) {
@@ -1016,7 +1016,7 @@ open_windows() {
     }
 
     // Steal the list, since remove_window() may remove from _new_windows.
-    new_windows.swap(_new_windows);
+    new_windows = std::move(_new_windows);
   }
 
   do_resort_windows();

+ 2 - 1
panda/src/display/graphicsEngine.h

@@ -322,7 +322,8 @@ private:
   // This lock protects the next two fields.
   Mutex _new_windows_lock;
   unsigned int _window_sort_index;
-  pvector<PT(GraphicsOutput)> _new_windows;
+  typedef small_vector<PT(GraphicsOutput)> NewWindows;
+  NewWindows _new_windows;
 
   WindowRenderer _app;
   typedef pmap<std::string, PT(RenderThread) > Threads;

+ 1 - 1
panda/src/egg2pg/eggLoader.cxx

@@ -1866,7 +1866,7 @@ make_lod(EggBin *egg_bin, PandaNode *parent) {
 
   // Now that we've created all of our children, put them in the proper order
   // and tell the LOD node about them.
-  sort(instances.begin(), instances.end());
+  std::sort(instances.begin(), instances.end());
 
   if (!instances.empty()) {
     // Set up the LOD node's center.  All of the children should have the same

+ 2 - 1
panda/src/event/asyncFuture.h

@@ -19,6 +19,7 @@
 #include "typedWritableReferenceCount.h"
 #include "eventParameter.h"
 #include "patomic.h"
+#include "small_vector.h"
 
 class AsyncTaskManager;
 class AsyncTask;
@@ -131,7 +132,7 @@ protected:
   std::string _done_event;
 
   // Tasks and gathering futures waiting for this one to complete.
-  Futures _waiting;
+  small_vector<PT(AsyncFuture)> _waiting;
 
   friend class AsyncGatheringFuture;
   friend class AsyncTaskChain;

+ 2 - 1
panda/src/event/event.h

@@ -18,6 +18,7 @@
 #include "pandabase.h"
 #include "eventParameter.h"
 #include "typedReferenceCount.h"
+#include "small_vector.h"
 
 class EventReceiver;
 
@@ -60,7 +61,7 @@ PUBLISHED:
   MAKE_PROPERTY2(receiver, has_receiver, get_receiver, set_receiver, clear_receiver);
 
 protected:
-  typedef pvector<EventParameter> ParameterList;
+  typedef small_vector<EventParameter> ParameterList;
   ParameterList _parameters;
   EventReceiver *_receiver;
 

+ 1 - 0
panda/src/event/eventHandler.h

@@ -22,6 +22,7 @@
 
 #include "pset.h"
 #include "pmap.h"
+#include "pvector.h"
 
 #ifndef CPPPARSER
 #include <functional>

+ 1 - 1
panda/src/express/ordered_vector.I

@@ -587,7 +587,7 @@ INLINE void ordered_vector<Key, Compare, Vector>::
 sort_unique() {
   TAU_PROFILE("ordered_vector::sort_unique()", " ", TAU_USER);
   sort(begin(), end(), _compare);
-  iterator new_end = unique(begin(), end(), EquivalentTest(_compare));
+  iterator new_end = std::unique(begin(), end(), EquivalentTest(_compare));
   erase(new_end, end());
 }
 

+ 2 - 1
panda/src/framework/pandaFramework.h

@@ -30,6 +30,7 @@
 #include "genericAsyncTask.h"
 
 #include "pvector.h"
+#include "small_vector.h"
 
 /**
  * This class serves to provide a high-level framework for basic applications
@@ -176,7 +177,7 @@ private:
   EventHandler &_event_handler;
   AsyncTaskManager &_task_mgr;
 
-  typedef pvector< PT(WindowFramework) > Windows;
+  typedef small_vector< PT(WindowFramework) > Windows;
   Windows _windows;
 
   typedef pmap< const GraphicsOutput *, NodePath > Mouses;

+ 2 - 1
panda/src/framework/windowFramework.h

@@ -33,6 +33,7 @@
 #include "textNode.h"
 #include "eventHandler.h"
 #include "genericAsyncTask.h"
+#include "small_vector.h"
 
 class PandaFramework;
 class AmbientLight;
@@ -166,7 +167,7 @@ private:
   PT(DisplayRegion) _display_region_3d;
 
   NodePath _camera_group;
-  typedef pvector< PT(Camera) > Cameras;
+  typedef small_vector< PT(Camera) > Cameras;
   Cameras _cameras;
 
   NodePath _render;

+ 1 - 1
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -98,7 +98,7 @@ PStatCollector CLP(GraphicsStateGuardian)::_copy_texture_finish_pcollector("Draw
 
 #if defined(HAVE_CG) && !defined(OPENGLES)
 AtomicAdjust::Integer CLP(GraphicsStateGuardian)::_num_gsgs_with_cg_contexts = 0;
-pvector<CGcontext> CLP(GraphicsStateGuardian)::_destroyed_cg_contexts;
+small_vector<CGcontext> CLP(GraphicsStateGuardian)::_destroyed_cg_contexts;
 #endif
 
 // The following noop functions are assigned to the corresponding glext

+ 2 - 2
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -723,7 +723,7 @@ protected:
 #if defined(HAVE_CG) && !defined(OPENGLES)
   CGcontext _cg_context;
   static AtomicAdjust::Integer _num_gsgs_with_cg_contexts;
-  static pvector<CGcontext> _destroyed_cg_contexts;
+  static small_vector<CGcontext> _destroyed_cg_contexts;
 #endif
 
 #ifdef SUPPORT_IMMEDIATE_MODE
@@ -1201,7 +1201,7 @@ public:
     GLint64 _gpu_sync_time;
     double _cpu_sync_time;
     pvector<std::pair<GLuint, int> > _queries;
-    pvector<GLint64> _latency_refs;
+    small_vector<GLint64> _latency_refs;
   };
   pdeque<FrameTiming> _frame_timings;
   FrameTiming *_current_frame_timing = nullptr;

+ 2 - 1
panda/src/glstuff/glShaderContext_src.h

@@ -20,6 +20,7 @@
 #include "shaderContext.h"
 #include "deletedChain.h"
 #include "paramTexture.h"
+#include "small_vector.h"
 
 class CLP(GraphicsStateGuardian);
 
@@ -68,7 +69,7 @@ public:
 private:
   bool _validated;
   GLuint _glsl_program;
-  typedef pvector<GLuint> GLSLShaders;
+  typedef small_vector<GLuint, 2> GLSLShaders;
   GLSLShaders _glsl_shaders;
 
   WCPT(RenderState) _state_rs;

+ 15 - 20
panda/src/gobj/geomVertexFormat.cxx

@@ -364,15 +364,13 @@ void GeomVertexFormat::
 remove_empty_arrays() {
   nassertv(!is_registered());
 
-  Arrays orig_arrays;
-  orig_arrays.swap(_arrays);
-  Arrays::const_iterator ai;
-  for (ai = orig_arrays.begin(); ai != orig_arrays.end(); ++ai) {
-    GeomVertexArrayFormat *array_format = (*ai);
+  Arrays new_arrays;
+  for (PT(GeomVertexArrayFormat) &array_format : _arrays) {
     if (array_format->get_num_columns() != 0) {
-      _arrays.push_back(array_format);
+      new_arrays.push_back(std::move(array_format));
     }
   }
+  _arrays = std::move(new_arrays);
 }
 
 /**
@@ -382,9 +380,8 @@ remove_empty_arrays() {
 size_t GeomVertexFormat::
 get_num_columns() const {
   size_t num_columns = 0;
-  Arrays::const_iterator ai;
-  for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
-    num_columns += (*ai)->get_num_columns();
+  for (GeomVertexArrayFormat *array_format : _arrays) {
+    num_columns += array_format->get_num_columns();
   }
   return num_columns;
 }
@@ -394,12 +391,11 @@ get_num_columns() const {
  */
 const GeomVertexColumn *GeomVertexFormat::
 get_column(size_t i) const {
-  Arrays::const_iterator ai;
-  for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
-    if (i < (size_t)(*ai)->get_num_columns()) {
-      return (*ai)->get_column(i);
+  for (GeomVertexArrayFormat *array_format : _arrays) {
+    if (i < (size_t)array_format->get_num_columns()) {
+      return array_format->get_column(i);
     }
-    i -= (*ai)->get_num_columns();
+    i -= array_format->get_num_columns();
   }
 
   return nullptr;
@@ -410,12 +406,11 @@ get_column(size_t i) const {
  */
 const InternalName *GeomVertexFormat::
 get_column_name(size_t i) const {
-  Arrays::const_iterator ai;
-  for (ai = _arrays.begin(); ai != _arrays.end(); ++ai) {
-    if (i < (size_t)(*ai)->get_num_columns()) {
-      return (*ai)->get_column(i)->get_name();
+  for (GeomVertexArrayFormat *array_format : _arrays) {
+    if (i < (size_t)array_format->get_num_columns()) {
+      return array_format->get_column(i)->get_name();
     }
-    i -= (*ai)->get_num_columns();
+    i -= array_format->get_num_columns();
   }
 
   return nullptr;
@@ -717,7 +712,7 @@ do_register() {
   nassertv(_columns_by_name.empty());
 
   Arrays orig_arrays;
-  orig_arrays.swap(_arrays);
+  std::swap(orig_arrays, _arrays);
   Arrays::const_iterator ai;
   for (ai = orig_arrays.begin(); ai != orig_arrays.end(); ++ai) {
     CPT(GeomVertexArrayFormat) array_format = (*ai);

+ 2 - 1
panda/src/gobj/geomVertexFormat.h

@@ -25,6 +25,7 @@
 #include "pmap.h"
 #include "pset.h"
 #include "pvector.h"
+#include "small_vector.h"
 #include "indirectCompareTo.h"
 #include "lightReMutex.h"
 
@@ -197,7 +198,7 @@ private:
 
   GeomVertexAnimationSpec _animation;
 
-  typedef pvector< PT(GeomVertexArrayFormat) > Arrays;
+  typedef small_vector<PT(GeomVertexArrayFormat), 2> Arrays;
   Arrays _arrays;
 
   class DataTypeRecord {

+ 2 - 1
panda/src/gobj/vertexTransform.h

@@ -19,6 +19,7 @@
 #include "updateSeq.h"
 #include "luse.h"
 #include "ordered_vector.h"
+#include "small_vector.h"
 #include "cycleData.h"
 #include "cycleDataReader.h"
 #include "cycleDataWriter.h"
@@ -54,7 +55,7 @@ protected:
   void mark_modified(Thread *current_thread);
 
 private:
-  typedef ov_set<TransformTable *> Palettes;
+  typedef ov_set<TransformTable *, std::less<TransformTable *>, small_vector<TransformTable *>> Palettes;
   Palettes _tables;
 
   // This is the data that must be cycled between pipeline stages.

+ 2 - 2
panda/src/gsgbase/graphicsStateGuardianBase.cxx

@@ -109,7 +109,7 @@ add_gsg(GraphicsStateGuardianBase *gsg) {
 
   LightMutexHolder holder(gsg_list->_lock);
 
-  if (find(gsg_list->_gsgs.begin(), gsg_list->_gsgs.end(), gsg) != gsg_list->_gsgs.end()) {
+  if (std::find(gsg_list->_gsgs.begin(), gsg_list->_gsgs.end(), gsg) != gsg_list->_gsgs.end()) {
     // Already on the list.
     return;
   }
@@ -135,7 +135,7 @@ remove_gsg(GraphicsStateGuardianBase *gsg) {
   LightMutexHolder holder(gsg_list->_lock);
 
   GSGList::GSGs::iterator gi;
-  gi = find(gsg_list->_gsgs.begin(), gsg_list->_gsgs.end(), gsg);
+  gi = std::find(gsg_list->_gsgs.begin(), gsg_list->_gsgs.end(), gsg);
   if (gi == gsg_list->_gsgs.end()) {
     // Already removed, or never added.
     return;

+ 2 - 1
panda/src/gsgbase/graphicsStateGuardianBase.h

@@ -21,6 +21,7 @@
 #include "luse.h"
 #include "lightMutex.h"
 #include "patomic.h"
+#include "small_vector.h"
 
 // A handful of forward references.
 
@@ -263,7 +264,7 @@ private:
   struct GSGList {
     LightMutex _lock;
 
-    typedef pvector<GraphicsStateGuardianBase *> GSGs;
+    typedef small_vector<GraphicsStateGuardianBase *> GSGs;
     GSGs _gsgs;
     GraphicsStateGuardianBase *_default_gsg = nullptr;
   };

+ 1 - 1
panda/src/parametrics/curveFitter.cxx

@@ -213,7 +213,7 @@ wrap_hpr() {
  */
 void CurveFitter::
 sort_points() {
-  sort(_data.begin(), _data.end());
+  std::sort(_data.begin(), _data.end());
 }
 
 /**

+ 1 - 1
panda/src/pgraph/camera.cxx

@@ -249,7 +249,7 @@ add_display_region(DisplayRegion *display_region) {
 void Camera::
 remove_display_region(DisplayRegion *display_region) {
   DisplayRegions::iterator dri =
-    find(_display_regions.begin(), _display_regions.end(), display_region);
+    std::find(_display_regions.begin(), _display_regions.end(), display_region);
   if (dri != _display_regions.end()) {
     _display_regions.erase(dri);
   }

+ 2 - 1
panda/src/pgraph/camera.h

@@ -24,6 +24,7 @@
 #include "renderState.h"
 #include "pointerTo.h"
 #include "pmap.h"
+#include "small_vector.h"
 #include "auxSceneData.h"
 
 class DisplayRegion;
@@ -115,7 +116,7 @@ private:
   DrawMask _camera_mask;
   PN_stdfloat _lod_scale;
 
-  typedef pvector<DisplayRegion *> DisplayRegions;
+  typedef small_vector<DisplayRegion *> DisplayRegions;
   DisplayRegions _display_regions;
 
   CPT(RenderState) _initial_state;

+ 2 - 1
panda/src/pgraph/lensNode.h

@@ -20,6 +20,7 @@
 #include "lens.h"
 #include "perspectiveLens.h"
 #include "pointerTo.h"
+#include "small_vector.h"
 
 /**
  * A node that contains a Lens.  The most important example of this kind of
@@ -67,7 +68,7 @@ protected:
     bool _is_active;
   };
 
-  typedef pvector<LensSlot> Lenses;
+  typedef small_vector<LensSlot> Lenses;
   Lenses _lenses;
 
 public:

+ 2 - 4
panda/src/pgraph/pandaNode.cxx

@@ -4030,8 +4030,7 @@ complete_up_list(PandaNode::Up &up_list, const string &tag,
   new_up_list.sort();
 
   // Make it permanent.
-  up_list.swap(new_up_list);
-  new_up_list.clear();
+  up_list = std::move(new_up_list);
 
   return pi;
 }
@@ -4060,8 +4059,7 @@ complete_down_list(PandaNode::Down &down_list, const string &tag,
   // should be preserved from one session to the next.
 
   // Make it permanent.
-  down_list.swap(new_down_list);
-  new_down_list.clear();
+  down_list = std::move(new_down_list);
 
   return pi;
 }

+ 2 - 1
panda/src/pgraph/pandaNode.h

@@ -46,6 +46,7 @@
 #include "extension.h"
 #include "simpleHashMap.h"
 #include "geometricBoundingVolume.h"
+#include "small_vector.h"
 
 class NodePathComponent;
 class CullTraverser;
@@ -520,7 +521,7 @@ private:
     // children do not circularly reference each other.
     PandaNode *_parent;
   };
-  typedef ov_set<UpConnection> UpList;
+  typedef ov_set<UpConnection, std::less<UpConnection>, small_vector<UpConnection> > UpList;
   typedef CopyOnWriteObj1< UpList, TypeHandle > Up;
 
   // We also maintain a set of NodePathComponents in the node.  This

+ 2 - 3
panda/src/pgraph/texGenAttrib.cxx

@@ -548,11 +548,10 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   // For now, read in a linear list of the modes we will assign to each
   // associated TextureStage pointer.  Later, in complete_pointers, we'll fill
   // up the map the with appropriate TextureStageMode pairing.
-  _read_modes.clear();
-  _read_modes.reserve(num_stages);
+  _read_modes.resize(num_stages);
   for (size_t i = 0; i < num_stages; i++) {
     manager->read_pointer(scan);
     Mode mode = (Mode)scan.get_uint8();
-    _read_modes.push_back(mode);
+    _read_modes[i] = mode;
   }
 }

+ 4 - 4
panda/src/pgraph/textureAttrib.cxx

@@ -319,8 +319,8 @@ filter_to_max(int max_texture_stages) const {
   RenderStages priority_stages = _render_stages;
 
   // This sort function uses the STL function object defined above.
-  sort(priority_stages.begin(), priority_stages.end(),
-       CompareTextureStagePriorities());
+  std::sort(priority_stages.begin(), priority_stages.end(),
+            CompareTextureStagePriorities());
 
   // Now lop off all of the stages after the first max_texture_stages.
   priority_stages.erase(priority_stages.begin() + max_texture_stages,
@@ -979,8 +979,8 @@ sort_on_stages() {
     _render_stages.push_back(&sn);
   }
 
-  sort(_render_stages.begin(), _render_stages.end(), CompareTextureStageSort());
-  sort(_render_ff_stages.begin(), _render_ff_stages.end(), CompareTextureStageSort());
+  std::sort(_render_stages.begin(), _render_stages.end(), CompareTextureStageSort());
+  std::sort(_render_ff_stages.begin(), _render_ff_stages.end(), CompareTextureStageSort());
 
   // We'd like to clear the _filtered map, in case the TextureStage priority
   // values have changed as well, but we can't do that here: it's too

+ 2 - 1
panda/src/pgraph/textureAttrib.h

@@ -23,6 +23,7 @@
 #include "ordered_vector.h"
 #include "vector_int.h"
 #include "epvector.h"
+#include "small_vector.h"
 
 /**
  * Indicates the set of TextureStages and their associated Textures that
@@ -147,7 +148,7 @@ private:
   typedef ov_set<StageNode, CompareTextureStagePointer, epvector<StageNode> > Stages;
   Stages _on_stages;  // set of all "on" stages, indexed by pointer.
 
-  typedef pvector<StageNode *> RenderStages;
+  typedef small_vector<StageNode *> RenderStages;
   RenderStages _render_stages;      // all "on" stages, sorted in render order.
   RenderStages _render_ff_stages;   // fixed-function stages only, in render order.
   unsigned int _next_implicit_sort;

+ 4 - 6
panda/src/pgraphnodes/computeNode.cxx

@@ -125,9 +125,8 @@ do_callback(CallbackData *cbdata) {
 
   CDReader cdata(_cycler);
 
-  Dispatches::const_iterator it;
-  for (it = cdata->_dispatches.begin(); it != cdata->_dispatches.end(); ++it) {
-    gsg->dispatch_compute(it->get_x(), it->get_y(), it->get_z());
+  for (const LVecBase3i &dispatch : cdata->_dispatches) {
+    gsg->dispatch_compute(dispatch[0], dispatch[1], dispatch[2]);
   }
 
   // No need to upcall; we don't have any geometry, after all.
@@ -194,9 +193,8 @@ void ComputeNode::Dispatcher::CData::
 write_datagram(BamWriter *manager, Datagram &dg) const {
   dg.add_uint16(_dispatches.size());
 
-  Dispatches::const_iterator it;
-  for (it = _dispatches.begin(); it != _dispatches.end(); ++it) {
-    generic_write_datagram(dg, *it);
+  for (const LVecBase3i &dispatch : _dispatches) {
+    generic_write_datagram(dg, dispatch);
   }
 }
 

+ 2 - 1
panda/src/pgraphnodes/computeNode.h

@@ -19,6 +19,7 @@
 #include "callbackObject.h"
 #include "callbackNode.h"
 #include "pointerTo.h"
+#include "small_vector.h"
 
 /**
  * A special node, the sole purpose of which is to invoke a dispatch operation
@@ -61,7 +62,7 @@ public:
 
     virtual void do_callback(CallbackData *cbdata);
 
-    typedef pvector<LVecBase3i> Dispatches;
+    typedef small_vector<LVecBase3i> Dispatches;
 
     class EXPCL_PANDA_PGRAPHNODES CData : public CycleData {
     public:

+ 4 - 6
panda/src/pgui/pgEntry.cxx

@@ -698,10 +698,8 @@ get_text_def(int state) const {
     // If we don't have a definition, use the global one.
     return get_text_node();
   }
-  if (_text_defs[state] == nullptr) {
-    return get_text_node();
-  }
-  return _text_defs[state];
+  TextNode *text_def = _text_defs[state];
+  return text_def ? text_def : get_text_node();
 }
 
 /**
@@ -750,8 +748,8 @@ is_wtext() const {
  */
 void PGEntry::
 slot_text_def(int state) {
-  while (state >= (int)_text_defs.size()) {
-    _text_defs.push_back(nullptr);
+  if (state >= (int)_text_defs.size()) {
+    _text_defs.resize(state + 1, nullptr);
   }
 }
 

+ 3 - 2
panda/src/pgui/pgEntry.h

@@ -20,7 +20,7 @@
 
 #include "textNode.h"
 #include "pointerTo.h"
-#include "pvector.h"
+#include "small_vector.h"
 #include "clockObject.h"
 #include "textAssembler.h"
 #include "pipeline.h"
@@ -168,7 +168,8 @@ private:
   std::string _candidate_active;
   std::string _candidate_inactive;
 
-  typedef pvector< PT(TextNode) > TextDefs;
+  // Most entries have 3 states.
+  typedef small_vector<PT(TextNode), 3> TextDefs;
   TextDefs _text_defs;
 
   // This is the subgraph that renders both the text and the cursor.

+ 1 - 1
panda/src/pipeline/threadSimpleManager.cxx

@@ -403,7 +403,7 @@ write_status(std::ostream &out) const {
   out << "Sleeping:";
   // Copy and sort for convenience.
   Sleeping s2 = _sleeping;
-  sort(s2.begin(), s2.end(), CompareStartTime());
+  std::sort(s2.begin(), s2.end(), CompareStartTime());
   for (si = s2.begin(); si != s2.end(); ++si) {
     out << " " << *(*si)->_parent_obj << "(" << (*si)->_wake_time - now
         << "s)";

+ 1 - 1
panda/src/pnmimagetypes/pnmFileTypePNG.cxx

@@ -604,7 +604,7 @@ write_data(xel *array, xelval *alpha_data) {
 
           // Re-sort the palette to put the semitransparent pixels at the
           // beginning.
-          sort(palette.begin(), palette.end(), LowAlphaCompare());
+          std::sort(palette.begin(), palette.end(), LowAlphaCompare());
 
           double palette_scale = 255.0 / _maxval;
 

+ 0 - 1
panda/src/putil/simpleHashMap.h

@@ -15,7 +15,6 @@
 #define SIMPLEHASHMAP_H
 
 #include "pandabase.h"
-#include "pvector.h"
 #include "config_putil.h"
 
 /**

+ 5 - 4
panda/src/tform/buttonThrower.h

@@ -16,12 +16,13 @@
 
 #include "pandabase.h"
 
+#include "buttonEventList.h"
 #include "dataNode.h"
+#include "eventParameter.h"
 #include "modifierButtons.h"
-#include "buttonEventList.h"
-#include "pvector.h"
 #include "pmap.h"
-#include "eventParameter.h"
+#include "pvector.h"
+#include "small_vector.h"
 
 /**
  * Throws Panda Events for button down/up events generated within the data
@@ -115,7 +116,7 @@ private:
   bool _time_flag;
   ModifierButtons _mods;
 
-  typedef pvector<EventParameter> ParameterList;
+  typedef small_vector<EventParameter> ParameterList;
   ParameterList _parameters;
 
   typedef pvector<ModifierButtons> ThrowButtonDef;

+ 1 - 1
panda/src/tform/mouseWatcher.cxx

@@ -575,7 +575,7 @@ get_over_regions(MouseWatcher::Regions &regions, const LPoint2 &pos) const {
   // Now sort the regions by pointer.  By convention, the Regions vectors are
   // always kept in order by pointer, so we can do easy linear comparison and
   // intersection operations.
-  sort(regions.begin(), regions.end());
+  std::sort(regions.begin(), regions.end());
 }
 
 /**