# HG changeset patch
# User Petter Tomner <tomner@kth.se>
# Date 1639185274 -3600
#      Sat Dec 11 02:14:34 2021 +0100
# Node ID 8950c8b817eb44967ce0856d0becae613b33a4ca
# Parent  bf619727bf6c2f00e70f95f471d447ef67ab2088
Object pool for scalar types

Make octave_scalar_base an its descendents use a object pool to
decrease malloc and free usage.

libinterp/octave-value/
	* ov-base-scalar.cc: Adding new, delete, ~object_pool_cleaner()
	* ov-base-scalar.h: As .cc, static member definitions
	* ov-base.cc: "Use" pool cleaners

diff -r bf619727bf6c -r 8950c8b817eb libinterp/octave-value/ov-base-scalar.cc
--- a/libinterp/octave-value/ov-base-scalar.cc	Sun Nov 21 15:25:04 2021 +0100
+++ b/libinterp/octave-value/ov-base-scalar.cc	Sat Dec 11 02:14:34 2021 +0100
@@ -39,6 +39,46 @@
 #include "ov-base-scalar.h"
 #include "pr-output.h"
 
+// We don't want the new or delete operator in the header, since dynamically
+// loaded object files can't have access to the thread_local objects, since
+// those have the attribute tls_model ("initial-exec"). If they were in the
+// header, they would e.g. be inlined in foreign user libraries and maybe
+// mess up stacks or what not.
+template <typename ST>
+void *
+octave_base_scalar<ST>::operator new (std::size_t count)
+{
+  if (n_in_obj_pool)
+    return obj_pool[--n_in_obj_pool];
+
+  return ::operator new (count);
+}
+
+template <typename ST>
+void octave_base_scalar<ST>::operator delete(void* ptr)
+{
+  if (n_in_obj_pool < obj_pool_size)
+    {
+      obj_pool[n_in_obj_pool++] = ptr;
+      return;
+    }
+  ::operator delete (ptr);
+}
+
+// Can't have this in the header either, since that would expose the
+// thread_local objects
+template <typename ST>
+octave_base_scalar<ST>::object_pool_cleaner::~object_pool_cleaner()
+{
+  int n = n_in_obj_pool;
+  // Set the counter to max so that any lingering objects
+  // do not put them self in the pool
+  n_in_obj_pool = obj_pool_size;
+
+  for (int i = 0; i < n; i++)
+    octave_base_scalar<ST>::operator delete (obj_pool[i]);
+}
+
 template <typename ST>
 octave_value
 octave_base_scalar<ST>::subsref (const std::string& type,
diff -r bf619727bf6c -r 8950c8b817eb libinterp/octave-value/ov-base-scalar.h
--- a/libinterp/octave-value/ov-base-scalar.h	Sun Nov 21 15:25:04 2021 +0100
+++ b/libinterp/octave-value/ov-base-scalar.h	Sat Dec 11 02:14:34 2021 +0100
@@ -63,6 +63,11 @@
 
   ~octave_base_scalar (void) = default;
 
+  // Overloaded new and delete operators that uses a object pool.
+  static void* operator new(std::size_t count) OCTINTERP_API;
+
+  static void operator delete(void* ptr) OCTINTERP_API;
+
   octave_value squeeze (void) const { return scalar; }
 
   octave_value full_value (void) const { return scalar; }
@@ -170,10 +175,52 @@
   OCTINTERP_API bool
   fast_elem_insert_self (void *where, builtin_type_t btyp) const;
 
+  // Class that cleans the object pool when the thread terminates
+  class OCTINTERP_API object_pool_cleaner
+  {
+  public:
+    OCTINTERP_API ~object_pool_cleaner ();
+  };
+
+  // For work around reasons pool_cleaner can't be private. Its addres
+  // has to be taken somewhere to fool the compiler to trigger its
+  // destructor.
+  thread_local static object_pool_cleaner pool_cleaner
+  __attribute__ ((tls_model ("initial-exec")));
+
 protected:
 
   // The value of this scalar.
   ST scalar;
+
+private:
+
+  static constexpr int obj_pool_size = 8;
+
+  // Counter for amount of objects in the pool
+  thread_local static int  n_in_obj_pool
+    __attribute__ ((tls_model ("initial-exec")));
+  // The pool
+  thread_local static void * obj_pool[obj_pool_size]
+    __attribute__ ((tls_model ("initial-exec")));
 };
 
+// Definitions for the object pool
+
+template <typename ST>
+thread_local
+int octave_base_scalar<ST>::n_in_obj_pool
+__attribute__ ((tls_model ("initial-exec")));
+
+template <typename ST>
+thread_local void *
+octave_base_scalar<ST>::obj_pool[octave_base_scalar<ST>::obj_pool_size]
+__attribute__ ((tls_model ("initial-exec")));
+
+template <typename ST>
+thread_local
+typename octave_base_scalar<ST>::object_pool_cleaner
+octave_base_scalar<ST>::pool_cleaner
+__attribute__ ((tls_model ("initial-exec")));
+
 #endif
diff -r bf619727bf6c -r 8950c8b817eb libinterp/octave-value/ov-base.cc
--- a/libinterp/octave-value/ov-base.cc	Sun Nov 21 15:25:04 2021 +0100
+++ b/libinterp/octave-value/ov-base.cc	Sat Dec 11 02:14:34 2021 +0100
@@ -61,6 +61,7 @@
 #include "pr-output.h"
 #include "utils.h"
 #include "variables.h"
+#include "ov-magic-int.h"
 
 builtin_type_t btyp_mixed_numeric (builtin_type_t x, builtin_type_t y)
 {
@@ -1524,6 +1525,63 @@
   return (fcn && fcn->name () == "builtin");
 }
 
+// thread_local destructors seems abit bugged/non-standard compliant in
+// gcc and clang and seems to only be executed if the object is "used" somehow
+// even though the destructor has side effects.
+//
+// So we just make objects here that are initialized with the addres of the
+// cleaner object.
+//
+// See e.g. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61991
+//
+// The pool_cleaner:s are used for the object pools for scalar types.
+
+octave_base_scalar<double>::object_pool_cleaner *
+pcleaner_scalar = &octave_base_scalar<double>::pool_cleaner;
+
+octave_base_scalar<float>::object_pool_cleaner *
+pcleaner_float_scalar = &octave_base_scalar<float>::pool_cleaner;
+
+octave_base_scalar<Complex>::object_pool_cleaner *
+pcleaner_complex_scalar =
+  &octave_base_scalar<Complex>::pool_cleaner;
+octave_base_scalar<FloatComplex>::object_pool_cleaner *
+pcleaner_float_complex =
+  &octave_base_scalar<FloatComplex>::pool_cleaner;
+
+octave_base_scalar<bool>::object_pool_cleaner *
+pcleaner_bool = &octave_base_scalar<bool>::pool_cleaner;
+
+octave_base_scalar<octave_int8>::object_pool_cleaner *
+pcleaner_int8 = &octave_base_scalar<octave_int8>::pool_cleaner;
+octave_base_scalar<octave_int16>::object_pool_cleaner *
+pcleaner_int16 = &octave_base_scalar<octave_int16>::pool_cleaner;
+octave_base_scalar<octave_int32>::object_pool_cleaner *
+pcleaner_int32 = &octave_base_scalar<octave_int32>::pool_cleaner;
+octave_base_scalar<octave_int64>::object_pool_cleaner *
+pcleaner_int64 = &octave_base_scalar<octave_int64>::pool_cleaner;
+
+octave_base_scalar<octave_uint8>::object_pool_cleaner *
+pcleaner_uint8 = &octave_base_scalar<octave_uint8>::pool_cleaner;
+octave_base_scalar<octave_uint16>::object_pool_cleaner *
+pcleaner_uint16 = &octave_base_scalar<octave_uint16>::pool_cleaner;
+octave_base_scalar<octave_uint32>::object_pool_cleaner *
+pcleaner_uint32 = &octave_base_scalar<octave_uint32>::pool_cleaner;
+octave_base_scalar<octave_uint64>::object_pool_cleaner *
+pcleaner_uint64 = &octave_base_scalar<octave_uint64>::pool_cleaner;
+
+// The size of magic ints and the octave_base_scalar of 64bit integer sizes
+// need to match, since they are using the same object pool.
+//
+// If these asserts fail becouse of some change, just overload the new and
+// delete operator for magic ints to "= default" so that they don't use the
+// pool
+static_assert (sizeof (octave_magic_int) ==
+               sizeof (octave_base_scalar<octave_int64>));
+static_assert (sizeof (octave_magic_uint) ==
+               sizeof (octave_base_scalar<octave_uint64>));
+
+
 OCTAVE_NAMESPACE_BEGIN
 
 void
