# HG changeset patch
# User Petter Tomner <tomner@kth.se>
# Date 1640095214 -3600
#      Tue Dec 21 15:00:14 2021 +0100
# Node ID 3d47721884d3321df8ef7f0cff0f31204bded684
# Parent  f40431034aef59dfeef79cc0d2bf5defca19e56c
interpreter: tiny_vector container for non-heap allocation

Introducing a tiny_vector class that puts a "small amount" of elements
in itself, and "big amount" on the heap. For better memory locality and
less mallocs.

Changing list usage on octave_lvalue for tiny_vector on multiple places.

liboctave/util/
	* tiny_vector.h: New
	* module.mk: Export tiny_vector.h header

libinterp/
	* corefcn/mex.cc: list -> tiny_vec
	* octave-value/ov-classdef.cc: ...
	* parse-tree/oct-lvalue.h: Need copy operator
	* parse-tree/pt-assign.cc: list -> tiny_vec
	* parse-tree/pt-cell.cc: ...
	* parse-tree/pt-eval.cc:
	* parse-tree/pt-eval.h:
	* parse-tree/pt-idx.cc:

diff -r f40431034aef -r 3d47721884d3 libinterp/corefcn/mex.cc
--- a/libinterp/corefcn/mex.cc	Mon Dec 20 18:45:19 2021 +0100
+++ b/libinterp/corefcn/mex.cc	Tue Dec 21 15:00:14 2021 +0100
@@ -4827,7 +4827,7 @@
       octave::tree_evaluator& tw = interp.get_evaluator ();
 
       octave::unwind_action act
-        ([&tw] (const std::list<octave::octave_lvalue> *lvl)
+        ([&tw] (const octave::tiny_vector<octave::octave_lvalue> *lvl)
          {
            tw.set_lvalue_list (lvl);
          }, tw.lvalue_list ());
diff -r f40431034aef -r 3d47721884d3 libinterp/octave-value/ov-classdef.cc
--- a/libinterp/octave-value/ov-classdef.cc	Mon Dec 20 18:45:19 2021 +0100
+++ b/libinterp/octave-value/ov-classdef.cc	Tue Dec 21 15:00:14 2021 +0100
@@ -48,6 +48,7 @@
 #include "pt-eval.h"
 #include "pt-misc.h"
 #include "oct-lvalue.h"
+#include "tiny_vector.h"
 
 static bool
 in_class_method (const octave::cdef_class& cls)
@@ -258,10 +259,11 @@
           octave::interpreter& interp = octave::__get_interpreter__ ("octave_classdef::xnumel");
           octave::tree_evaluator& tw = interp.get_evaluator();
 
-          octave::unwind_action act ([&tw] (const std::list<octave::octave_lvalue> *lvl)
-                            {
-                              tw.set_lvalue_list (lvl);
-                            }, tw.lvalue_list ());
+          octave::unwind_action act (
+            [&tw] (const octave::tiny_vector<octave::octave_lvalue> *lvl)
+            {
+              tw.set_lvalue_list (lvl);
+            }, tw.lvalue_list ());
           tw.set_lvalue_list (nullptr);
 
           octave_value_list lv = meth.execute (args, 1, true, "numel");
diff -r f40431034aef -r 3d47721884d3 libinterp/parse-tree/oct-lvalue.h
--- a/libinterp/parse-tree/oct-lvalue.h	Mon Dec 20 18:45:19 2021 +0100
+++ b/libinterp/parse-tree/oct-lvalue.h	Tue Dec 21 15:00:14 2021 +0100
@@ -295,7 +295,12 @@
       olv.m_rep = nullptr;
     }
 
-    octave_lvalue& operator = (const octave_lvalue&) = delete;
+    octave_lvalue& operator = (const octave_lvalue &rh)
+    {
+      delete m_rep;
+      m_rep = rh.m_rep->clone ();
+      return *this;
+    }
 
     ~octave_lvalue (void)
     {
diff -r f40431034aef -r 3d47721884d3 libinterp/parse-tree/pt-assign.cc
--- a/libinterp/parse-tree/pt-assign.cc	Mon Dec 20 18:45:19 2021 +0100
+++ b/libinterp/parse-tree/pt-assign.cc	Tue Dec 21 15:00:14 2021 +0100
@@ -36,6 +36,8 @@
 #include "pt-arg-list.h"
 #include "pt-assign.h"
 
+#include "tiny_vector.h"
+
 namespace octave
 {
   // Simple assignment expressions.
@@ -86,14 +88,14 @@
           {
             octave_lvalue ult = m_lhs->lvalue (tw);
 
-            std::list<octave_lvalue> lvalue_list;
-            lvalue_list.push_back (ult);
+            tiny_vector<octave_lvalue> lvalue_vec;
+            lvalue_vec.push_back (ult);
 
-            unwind_action act ([&tw] (const std::list<octave_lvalue> *lvl)
+            unwind_action act ([&tw] (const tiny_vector<octave_lvalue> *lvl)
                                {
                                  tw.set_lvalue_list (lvl);
                                }, tw.lvalue_list ());
-            tw.set_lvalue_list (&lvalue_list);
+            tw.set_lvalue_list (&lvalue_vec);
 
             if (ult.numel () != 1)
               err_invalid_structure_assignment ();
@@ -184,9 +186,9 @@
 
     if (m_rhs)
       {
-        std::list<octave_lvalue> lvalue_list = tw.make_lvalue_list (m_lhs);
+        tiny_vector<octave_lvalue> lvalue_list = tw.make_lvalue_list (m_lhs);
 
-        unwind_action act ([&tw] (const std::list<octave_lvalue> *lvl)
+        unwind_action act ([&tw] (const tiny_vector<octave_lvalue> *lvl)
                            {
                              tw.set_lvalue_list (lvl);
                            }, tw.lvalue_list ());
diff -r f40431034aef -r 3d47721884d3 libinterp/parse-tree/pt-cell.cc
--- a/libinterp/parse-tree/pt-cell.cc	Mon Dec 20 18:45:19 2021 +0100
+++ b/libinterp/parse-tree/pt-cell.cc	Tue Dec 21 15:00:14 2021 +0100
@@ -51,7 +51,7 @@
   octave_value
   tree_cell::evaluate (tree_evaluator& tw, int)
   {
-    unwind_action act ([&tw] (const std::list<octave_lvalue> *lvl)
+    unwind_action act ([&tw] (const tiny_vector<octave_lvalue> *lvl)
                        {
                          tw.set_lvalue_list (lvl);
                        }, tw.lvalue_list ());
diff -r f40431034aef -r 3d47721884d3 libinterp/parse-tree/pt-eval.cc
--- a/libinterp/parse-tree/pt-eval.cc	Mon Dec 20 18:45:19 2021 +0100
+++ b/libinterp/parse-tree/pt-eval.cc	Tue Dec 21 15:00:14 2021 +0100
@@ -1436,7 +1436,7 @@
   {
     Matrix retval;
 
-    const std::list<octave_lvalue> *lvalues = m_lvalue_list;
+    const tiny_vector<octave_lvalue> *lvalues = m_lvalue_vec;
 
     if (! lvalues)
       return retval;
@@ -3735,8 +3735,8 @@
           {
             if (cmd)
               {
-                unwind_protect_var<const std::list<octave_lvalue> *>
-                  upv (m_lvalue_list, nullptr);
+                unwind_protect_var<const tiny_vector<octave_lvalue> *>
+                  upv (m_lvalue_vec, nullptr);
 
                 cmd->accept (*this);
               }
@@ -4555,8 +4555,8 @@
 
     if (args)
       {
-        unwind_protect_var<const std::list<octave_lvalue> *>
-          upv (m_lvalue_list, nullptr);
+        unwind_protect_var<const tiny_vector<octave_lvalue> *>
+          upv (m_lvalue_vec, nullptr);
 
         int len = args->length ();
 
@@ -4602,10 +4602,10 @@
     return retval;
   }
 
-  std::list<octave_lvalue>
+  tiny_vector<octave_lvalue>
   tree_evaluator::make_lvalue_list (tree_argument_list *lhs)
   {
-    std::list<octave_lvalue> retval;
+    tiny_vector<octave_lvalue> retval;
 
     for (tree_expression *elt : *lhs)
       retval.push_back (elt->lvalue (*this));
diff -r f40431034aef -r 3d47721884d3 libinterp/parse-tree/pt-eval.h
--- a/libinterp/parse-tree/pt-eval.h	Mon Dec 20 18:45:19 2021 +0100
+++ b/libinterp/parse-tree/pt-eval.h	Tue Dec 21 15:00:14 2021 +0100
@@ -43,6 +43,7 @@
 #include "profiler.h"
 #include "pt-walk.h"
 #include "stack-frame.h"
+#include "tiny_vector.h"
 
 class octave_user_code;
 
@@ -129,7 +130,7 @@
 
     tree_evaluator (interpreter& interp)
       : m_interpreter (interp), m_parser (), m_statement_context (SC_OTHER),
-        m_lvalue_list (nullptr), m_autoload_map (), m_bp_table (*this),
+        m_lvalue_vec (nullptr), m_autoload_map (), m_bp_table (*this),
         m_call_stack (*this), m_profiler (), m_debug_frame (0),
         m_debug_mode (false), m_quiet_breakpoint_flag (false),
         m_debugger_stack (), m_exit_status (0), m_max_recursion_depth (256),
@@ -746,14 +747,14 @@
 
     octave_value_list evaluate_end_expression (const octave_value_list& args);
 
-    const std::list<octave_lvalue> * lvalue_list (void) const
+    const tiny_vector<octave_lvalue> * lvalue_list (void) const
     {
-      return m_lvalue_list;
+      return m_lvalue_vec;
     }
 
-    void set_lvalue_list (const std::list<octave_lvalue> *lst)
+    void set_lvalue_list (const tiny_vector<octave_lvalue> *v)
     {
-      m_lvalue_list = lst;
+      m_lvalue_vec = v;
     }
 
     int breaking (void) const { return m_breaking; }
@@ -833,7 +834,7 @@
     octave_value_list
     make_value_list (tree_argument_list *args, const string_vector& arg_nm);
 
-    std::list<octave_lvalue> make_lvalue_list (tree_argument_list *);
+    tiny_vector<octave_lvalue> make_lvalue_list (tree_argument_list *);
 
     void push_echo_state (int type, const std::string& file_name, int pos = 1);
 
@@ -882,7 +883,7 @@
     // The context for the current evaluation.
     stmt_list_type m_statement_context;
 
-    const std::list<octave_lvalue> *m_lvalue_list;
+    const tiny_vector<octave_lvalue> *m_lvalue_vec;
 
     // List of autoloads (function -> file mapping).
     std::map<std::string, std::string> m_autoload_map;
diff -r f40431034aef -r 3d47721884d3 libinterp/parse-tree/pt-idx.cc
--- a/libinterp/parse-tree/pt-idx.cc	Mon Dec 20 18:45:19 2021 +0100
+++ b/libinterp/parse-tree/pt-idx.cc	Tue Dec 21 15:00:14 2021 +0100
@@ -376,7 +376,7 @@
 
             if (al && al->length () > 0)
               {
-                unwind_action act ([&tw] (const std::list<octave_lvalue> *lvl)
+                unwind_action act ([&tw] (const tiny_vector<octave_lvalue> *lvl)
                                    {
                                      tw.set_lvalue_list (lvl);
                                    }, tw.lvalue_list ());
diff -r f40431034aef -r 3d47721884d3 liboctave/util/module.mk
--- a/liboctave/util/module.mk	Mon Dec 20 18:45:19 2021 +0100
+++ b/liboctave/util/module.mk	Tue Dec 21 15:00:14 2021 +0100
@@ -38,6 +38,7 @@
   %reldir%/singleton-cleanup.h \
   %reldir%/sparse-util.h \
   %reldir%/str-vec.h \
+  %reldir%/tiny_vector.h \
   %reldir%/unwind-prot.h \
   %reldir%/url-transfer.h
 
diff -r f40431034aef -r 3d47721884d3 liboctave/util/tiny_vector.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/liboctave/util/tiny_vector.h	Tue Dec 21 15:00:14 2021 +0100
@@ -0,0 +1,437 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 1996-2021 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if ! defined (tiny_vector_h)
+#define tiny_vector_h 1
+
+#include <vector>
+#include <cstddef>
+#include <initializer_list>
+
+namespace octave
+{
+  // tiny_vector is a vector container where the initial N elements
+  // are stored in the tiny_vector object itself. After N + 1 elements
+  // has been added, all the elements are stored on the heap.
+  //
+  // In this way, usage of malloc/free can be limited to cases where there
+  // is a need for a big vector.
+  //
+  // Constructors etc. resembles std::vector
+  //
+  template <class T, size_t N = sizeof (std::vector<T>) / sizeof (T)>
+  class tiny_vector
+  {
+  public:
+    static constexpr size_t n_tiny = N;
+
+    tiny_vector () {};
+
+    tiny_vector (size_t count)
+    {
+      if (count > n_tiny)
+        {
+          new (&m_data.m_vec) std::vector<T> (count);
+          m_tag = -1;
+        }
+      else
+        {
+          for (unsigned i = 0; i < count; i++)
+            new (&m_data.m_arr[i]) T;
+          m_tag = count;
+        }
+    }
+
+    tiny_vector (size_t count, const T& val)
+    {
+      if (count > n_tiny)
+        {
+          new (&m_data.m_vec) std::vector<T> (count, val);
+
+          m_tag = -1;
+        }
+      else
+        {
+          for (unsigned i = 0; i < count; i++)
+            new (&m_data.m_arr[i]) T (val);
+          m_tag = count;
+        }
+    }
+
+    tiny_vector (typename std::initializer_list<T>::const_iterator beg,
+                 typename std::initializer_list<T>::const_iterator end)
+    {
+      size_t count = end - beg;
+      if (count > n_tiny)
+        {
+          new (&m_data.m_vec) std::vector<T> (beg, end);
+          // Set the tag to indicate vector usage
+          m_tag = -1;
+        }
+      else
+        {
+          for (unsigned i = 0; i < count; i++)
+            new (&m_data.m_arr[i]) T (*beg++);
+          m_tag = count;
+        }
+    }
+
+    tiny_vector (const tiny_vector &tovv)
+    {
+      if (tovv.m_tag >= 0)
+        {
+          for (int i = 0; i < tovv.m_tag; i++)
+            new (&m_data.m_arr[i]) T(tovv.m_data.m_arr[i]);
+          m_tag = tovv.m_tag;
+        }
+      else
+        {
+          new (&m_data.m_vec) std::vector<T> (tovv.m_data.m_vec);
+          // Set the tag to indicate vector usage
+          m_tag = -1;
+        }
+    }
+
+    template<class iter> //TODO: Fix to T::const_iterator
+    tiny_vector (const iter begin,
+                 const iter end)
+    {
+      // The iterators does not have to support taking the difference
+      // so we just use vector for this
+      new (&m_data.m_vec) std::vector<T> (begin, end);
+      // Set the tag to indicate vector usage
+      m_tag = -1;
+    }
+
+    ~tiny_vector ()
+    {
+      if (m_tag < 0)
+        {
+          m_data.m_vec.~vector ();
+        }
+      else
+        {
+          for (int i = 0; i < m_tag; i++)
+            m_data.m_arr [i].~T ();
+        }
+    }
+
+    bool empty () const
+    {
+      if (m_tag >= 0)
+        return m_tag == 0;
+      else
+        return m_data.m_vec.empty ();
+    }
+
+    size_t size () const
+    {
+      if (m_tag >= 0)
+        return m_tag;
+      else
+        return m_data.m_vec.size ();
+    }
+
+    void clear ()
+    {
+      if (m_tag >= 0)
+        {
+          // Destroy old elements
+          for (int i = 0; i < m_tag; i++)
+            m_data.m_arr[i].~T ();
+
+          m_tag = 0;
+        }
+      else
+        return m_data.m_vec.clear ();
+    }
+
+    const T* begin () const
+    {
+      if (m_tag >= 0)
+        return &m_data.m_arr[0];
+      else
+        return m_data.m_vec.data ();
+    }
+
+    T* begin ()
+    {
+      if (m_tag >= 0)
+        return &m_data.m_arr[0];
+      else
+        return m_data.m_vec.data ();
+    }
+
+    const T* end () const
+    {
+      if (m_tag >= 0)
+        return &m_data.m_arr[m_tag];
+      else
+        return m_data.m_vec.data () + m_data.m_vec.size ();
+    }
+
+    T* end ()
+    {
+      if (m_tag >= 0)
+        return &m_data.m_arr[m_tag];
+      else
+        return m_data.m_vec.data () + m_data.m_vec.size ();
+    }
+
+    tiny_vector& operator=(tiny_vector &&tovv)
+    {
+      if (m_tag >= 0 && tovv.m_tag >= 0)
+        {
+          // Destroy old elements
+          for (int i = 0; i < m_tag; i++)
+            m_data.m_arr[i].~T ();
+
+          // Construct elements
+          for (int i = 0; i < tovv.m_tag; i++)
+            new (&m_data.m_arr[i]) T(tovv.m_data.m_arr[i]);
+
+          m_tag = tovv.m_tag;
+
+          return *this;
+        }
+      else if (m_tag < 0 && tovv.m_tag < 0)
+        {
+          m_data.m_vec = tovv.m_data.m_vec;
+          m_tag = -1;
+          return *this;
+        }
+      else if (m_tag >= 0 && tovv.m_tag < 0)
+        {
+          // Destroy elements
+          for (int i = 0; i < m_tag; i++)
+            m_data.m_arr[i].~T ();
+
+          // Construct the vector in the union
+          new (&m_data.m_vec) std::vector<T> (tovv.m_data.m_vec);
+
+          //Mark for vector usage
+          m_tag = -1;
+
+          return *this;
+        }
+      else // (m_tag < 0 && tovv.m_tag >= 0)
+        {
+          m_data.m_vec.resize (tovv.m_tag);
+          for (int i = 0; i < tovv.m_tag; i++)
+            m_data.m_vec[i] = tovv.m_data.m_arr[i];
+
+          return *this;
+        }
+    }
+
+    tiny_vector& operator=(const tiny_vector &tovv)
+    {
+      if (m_tag >= 0 && tovv.m_tag >= 0)
+        {
+          // Destroy old elements
+          for (int i = 0; i < m_tag; i++)
+            m_data.m_arr[i].~T ();
+
+          // Construct elements
+          for (int i = 0; i < tovv.m_tag; i++)
+            new (&m_data.m_arr[i]) T(tovv.m_data.m_arr[i]);
+
+          m_tag = tovv.m_tag;
+
+          return *this;
+        }
+      else if (m_tag < 0 && tovv.m_tag < 0)
+        {
+          m_data.m_vec = tovv.m_data.m_vec;
+          return *this;
+        }
+      else if (m_tag >= 0 && tovv.m_tag < 0)
+        {
+          // Destroy elements
+          for (int i = 0; i < m_tag; i++)
+            m_data.m_arr[i].~T ();
+
+          //Mark for vector usage
+          m_tag = -1;
+
+          // Construct the vector in the union
+          new (&m_data.m_vec) std::vector<T> (tovv.m_data.m_vec);
+
+          return *this;
+        }
+      else // (m_tag < 0 && tovv.m_tag >= 0)
+        {
+          m_data.m_vec.resize (tovv.m_tag);
+          for (int i = 0; i < tovv.m_tag; i++)
+            m_data.m_vec[i] = tovv.m_data.m_arr[i];
+
+          return *this;
+        }
+    }
+
+    T& operator[](std::size_t idx)
+    {
+      if (m_tag >= 0)
+        {
+      // Poor compiler can't figure out that m_tag is supposed to be
+      // -1 when idx is bigger than the array.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Warray-bounds"
+        return m_data.m_arr[idx];
+#pragma GCC diagnostic pop
+        }
+      else
+        return m_data.m_vec[idx];
+    }
+
+    const T& operator[](std::size_t idx) const
+    {
+      if (m_tag >= 0)
+        {
+      // Poor compiler can't figure out that m_tag is supposed to be
+      // -1 when idx is bigger than the array.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Warray-bounds"
+          return m_data.m_arr[idx];
+#pragma GCC diagnostic pop
+        }
+      else
+        {
+          return m_data.m_vec[idx];
+        }
+    }
+
+    T& front ()
+    {
+      if (m_tag >= 0)
+        return m_data.m_arr[0];
+      else
+        return m_data.m_vec.front ();
+    }
+
+    const T& front () const
+    {
+      if (m_tag >= 0)
+        return m_data.m_arr[0];
+      else
+        return m_data.m_vec.front ();
+    }
+
+    void push_back (const T &v)
+    {
+      if (m_tag >= 0 && m_tag < static_cast<int> (n_tiny))
+        {
+          new (&m_data.m_arr[m_tag++]) T (v);
+          return;
+        }
+      if (m_tag == n_tiny)
+        switch_to_vec ();
+      m_data.m_vec.push_back (v);
+    }
+
+    void push_back (T &&v)
+    {
+      if (m_tag >= 0 && m_tag < static_cast<int> (n_tiny))
+        {
+          new (&m_data.m_arr[m_tag++]) T (v);
+          return;
+        }
+      if (m_tag == n_tiny)
+        switch_to_vec ();
+      m_data.m_vec.push_back (v);
+    }
+
+    void resize (size_t count, const T& ref = T ())
+    {
+      if (m_tag >= 0)
+        {
+          size_t n = static_cast <size_t> (m_tag);
+          // Check that we don't need to switch to vector
+          if (count <= n_tiny)
+            {
+              if (count < n)
+                {
+                  // Destroy elements
+                  for (unsigned i = count; i < n; i++)
+                    m_data.m_arr[i].~octave_value ();
+                }
+              else if (count > n)
+                {
+                  // Construct elements
+                  for (unsigned i = n; i < count; i++)
+                    new (&m_data.m_arr[i]) T (ref);
+                }
+              // Nothing to do on same size
+
+              m_tag = count;
+              return;
+            }
+          // Switch to vector so that the new size fits
+          switch_to_vec();
+        }
+
+      m_data.m_vec.resize (count, ref);
+      return;
+    }
+
+  private:
+    union vec_or_arr
+    {
+      T m_arr[n_tiny];
+      std::vector<T> m_vec;
+
+      vec_or_arr () {};
+      ~vec_or_arr () {};
+    };
+
+    // The tag tracks how many elements there are in the array for non-negative
+    // numbers. For negative numbers, the std::vector is in use
+    int m_tag = 0;
+
+    // The data object
+    vec_or_arr m_data;
+
+    void switch_to_vec ()
+    {
+      std::vector<T> tmp;
+      for (int i = 0; i < m_tag; i++)
+        {
+          tmp.push_back (m_data.m_arr[i]);
+          // Destroy ov:s in the array
+          m_data.m_arr[i].~T ();
+        }
+      // Construct the vector in the union
+      new (&m_data.m_vec) std::vector<T>;
+      // Copy the data
+      m_data.m_vec = tmp;
+      // Set the tag to indicate vector usage
+      m_tag = -1;
+    }
+  };
+} // namespace octave
+
+
+#endif
+
