changeset 30227:b00ff462e0f2

improve efficiency of mxArray -> octave_value conversion * Array.h (Array<T>::ArrayRep, Array<T>::Array): New constructor that accepts externally allocated data array. * Sparse.h (Sparse<T>::SparseRep, Sparse<T>::Sparse): New constructor that accepts externally allocated data and index arrays. * mex.cc (maybe_disown_ptr): New function. (mxArray_interleaved_full::fp_to_ov): Use new Array constructor to pass allocated data from mxArray object to Array object without copying data. Call maybe_disown_ptr on data pointer to give up ownership. (mxArray_interleaved_sparse::to_ov): Use new Array constructor to pass allocated data from mxArray object to Array object without copying data. Call maybe_disown_ptr on data pointer to give up ownership. (mxArray_separate_full::to_ov): New template. (mxArray_separate_flll::as_octave_value): Use it.
author John W. Eaton <jwe@octave.org>
date Fri, 01 Oct 2021 15:25:51 -0400
parents f3ffb4596bd8
children 39a4ab124fd0
files libinterp/corefcn/mex.cc liboctave/array/Array.h liboctave/array/Sparse.h
diffstat 3 files changed, 103 insertions(+), 80 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/corefcn/mex.cc	Wed Sep 29 16:43:08 2021 -0400
+++ b/libinterp/corefcn/mex.cc	Fri Oct 01 15:25:51 2021 -0400
@@ -352,6 +352,8 @@
 
 static inline void * maybe_mark_foreign (void *ptr);
 
+static inline void maybe_disown_ptr (void *ptr);
+
 #define VOID_MUTATION_METHOD(FCN_NAME, ARG_LIST)        \
   void FCN_NAME ARG_LIST { request_mutation (); }
 
@@ -1995,10 +1997,12 @@
     switch (get_class_id ())
       {
       case mxDOUBLE_CLASS:
-        return fp_to_ov<double> (dv);
+        return (is_complex ()
+                ? fp_to_ov<Complex> (dv) : fp_to_ov<double> (dv));
 
       case mxSINGLE_CLASS:
-        return fp_to_ov<float> (dv);
+        return (is_complex ()
+                ? fp_to_ov<FloatComplex> (dv) : fp_to_ov<float> (dv));
 
       case mxCHAR_CLASS:
         return int_to_ov<mxChar, charNDArray, char> (dv);
@@ -2051,36 +2055,13 @@
   octave_value
   fp_to_ov (const dim_vector& dv) const
   {
-    if (is_complex ())
-      {
-        std::complex<ELT_T> *ppr = static_cast<std::complex<ELT_T> *> (m_pr);
-
-        Array<std::complex<ELT_T>> val (dv);
-
-        std::complex<ELT_T> *ptr = val.fortran_vec ();
-
-        mwSize nel = get_number_of_elements ();
-
-        for (mwIndex i = 0; i < nel; i++)
-          ptr[i] = ppr[i];
-
-        return octave_value (val);
-      }
-    else
-      {
-        ELT_T *ppr = static_cast<ELT_T *> (m_pr);
-
-        Array<ELT_T> val (dv);
-
-        ELT_T *ptr = val.fortran_vec ();
-
-        mwSize nel = get_number_of_elements ();
-
-        for (mwIndex i = 0; i < nel; i++)
-          ptr[i] = ppr[i];
-
-        return octave_value (val);
-      }
+    ELT_T *ppr = static_cast<ELT_T *> (m_pr);
+
+    Array<ELT_T> val (ppr, dv);
+
+    maybe_disown_ptr (m_pr);
+
+    return octave_value (val);
   }
 
   template <typename ELT_T, typename ARRAY_T, typename ARRAY_ELT_T>
@@ -2092,6 +2073,11 @@
 
     ELT_T *ppr = static_cast<ELT_T *> (m_pr);
 
+#if 0
+    ARRAY_T val (ppr, dv);
+
+    maybe_disown_ptr (m_pr);
+#else
     ARRAY_T val (dv);
 
     ARRAY_ELT_T *ptr = val.fortran_vec ();
@@ -2100,6 +2086,7 @@
 
     for (mwIndex i = 0; i < nel; i++)
       ptr[i] = ppr[i];
+#endif
 
     return octave_value (val);
   }
@@ -2307,42 +2294,10 @@
     switch (get_class_id ())
       {
       case mxDOUBLE_CLASS:
-        {
-          mwSize nel = get_number_of_elements ();
-
-          double *ppr = static_cast<double *> (m_pr);
-
-          ComplexNDArray val (dv);
-
-          Complex *ptr = val.fortran_vec ();
-
-          double *ppi = static_cast<double *> (m_pi);
-
-          for (mwIndex i = 0; i < nel; i++)
-            ptr[i] = Complex (ppr[i], ppi[i]);
-
-          retval = val;
-        }
-        break;
+        return to_ov<double> (dv);
 
       case mxSINGLE_CLASS:
-        {
-          mwSize nel = get_number_of_elements ();
-
-          float *ppr = static_cast<float *> (m_pr);
-
-          FloatComplexNDArray val (dv);
-
-          FloatComplex *ptr = val.fortran_vec ();
-
-          float *ppi = static_cast<float *> (m_pi);
-
-          for (mwIndex i = 0; i < nel; i++)
-            ptr[i] = FloatComplex (ppr[i], ppi[i]);
-
-          retval = val;
-        }
-        break;
+        return to_ov<float> (dv);
 
       case mxLOGICAL_CLASS:
       case mxINT8_CLASS:
@@ -2376,6 +2331,26 @@
 
 private:
 
+  template <typename T>
+  octave_value
+  to_ov (const dim_vector& dv) const
+  {
+    mwSize nel = get_number_of_elements ();
+
+    T *ppr = static_cast<T *> (m_pr);
+
+    Array<std::complex<T>> val (dv);
+
+    std::complex<T> *ptr = val.fortran_vec ();
+
+    T *ppi = static_cast<T *> (m_pi);
+
+    for (mwIndex i = 0; i < nel; i++)
+      ptr[i] = std::complex<T> (ppr[i], ppi[i]);
+
+    return octave_value (val);
+  }
+
   // Pointer to the imaginary part of the data.
   void *m_pi;
 };
@@ -2481,16 +2456,18 @@
   {
     octave_value retval;
 
+    dim_vector dv = dims_to_dim_vector ();
+
     switch (get_class_id ())
       {
       case mxDOUBLE_CLASS:
-        return is_complex () ? to_ov<Complex> (): to_ov<double> ();
+        return is_complex () ? to_ov<Complex> (dv): to_ov<double> (dv);
 
       case mxSINGLE_CLASS:
         error ("single precision sparse data type not supported");
 
       case mxLOGICAL_CLASS:
-        return to_ov<bool> ();
+        return to_ov<bool> (dv);
 
       default:
         panic_impossible ();
@@ -2503,21 +2480,16 @@
 
   template <typename ELT_T>
   octave_value
-  to_ov (void) const
+  to_ov (const dim_vector& dv) const
   {
     ELT_T *ppr = static_cast<ELT_T *> (m_pr);
 
-    Sparse<ELT_T> val (get_m (), get_n (),
-                       static_cast<octave_idx_type> (m_nzmax));
-
-    for (mwIndex i = 0; i < m_nzmax; i++)
-      {
-        val.xdata (i) = ppr[i];
-        val.xridx (i) = m_ir[i];
-      }
-
-    for (mwIndex i = 0; i < get_n () + 1; i++)
-      val.xcidx (i) = m_jc[i];
+    Sparse<ELT_T> val (dv, static_cast<octave_idx_type> (m_nzmax),
+                       ppr, m_ir, m_jc);
+
+    maybe_disown_ptr (m_pr);
+    maybe_disown_ptr (m_ir);
+    maybe_disown_ptr (m_jc);
 
     return octave_value (val);
   }
@@ -2641,6 +2613,8 @@
 
     octave_value retval;
 
+    dim_vector dv = dims_to_dim_vector ();
+
     switch (get_class_id ())
       {
       case mxDOUBLE_CLASS:
@@ -3604,6 +3578,17 @@
   return ptr;
 }
 
+static inline void
+maybe_disown_ptr (void *ptr)
+{
+  if (mex_context)
+    {
+      mex_context->unmark (ptr);
+      mex_context->global_unmark (ptr);
+      mex_context->mark_foreign (ptr);
+    }
+}
+
 static inline mxArray *
 maybe_unmark_array (mxArray *ptr)
 {
--- a/liboctave/array/Array.h	Wed Sep 29 16:43:08 2021 -0400
+++ b/liboctave/array/Array.h	Fri Oct 01 15:25:51 2021 -0400
@@ -167,6 +167,10 @@
       std::fill_n (m_data, n, val);
     }
 
+    explicit ArrayRep (T *ptr, const dim_vector& dv)
+      : m_data (ptr), m_len (dv.safe_numel ()), m_count (1)
+    { }
+
     ArrayRep (const ArrayRep& a)
       : m_data (new T [a.m_len]), m_len (a.m_len), m_count (1)
     {
@@ -271,6 +275,20 @@
     m_dimensions.chop_trailing_singletons ();
   }
 
+  // Construct an Array from a pointer to an externally allocated array
+  // of values.  PTR must be allocated with operator new.  The Array
+  // object takes ownership of PTR and will delete it when the Array
+  // object is deleted.  The dimension vector DV must be consistent with
+  // the size of the allocated PTR array.
+
+  explicit Array (T *ptr, const dim_vector& dv)
+    : m_dimensions (dv),
+      m_rep (new typename Array<T>::ArrayRep (ptr, dv)),
+      m_slice_data (m_rep->m_data), m_slice_len (m_rep->m_len)
+  {
+    m_dimensions.chop_trailing_singletons ();
+  }
+
   //! Reshape constructor.
   OCTARRAY_API Array (const Array<T>& a, const dim_vector& dv);
 
--- a/liboctave/array/Sparse.h	Wed Sep 29 16:43:08 2021 -0400
+++ b/liboctave/array/Sparse.h	Fri Oct 01 15:25:51 2021 -0400
@@ -113,6 +113,13 @@
       std::copy_n (c, nc + 1, m_cidx);
     }
 
+    template <typename U>
+    SparseRep (const dim_vector& dv, octave_idx_type nz,
+               U *d, octave_idx_type *r, octave_idx_type *c)
+      : m_data (d), m_ridx (r), m_cidx (c),
+        m_nzmax (nz), m_nrows (dv(0)), m_ncols (dv(1)), m_count (1)
+    { }
+
     SparseRep (const SparseRep& a)
       : m_data (new T [a.m_nzmax]), m_ridx (new octave_idx_type [a.m_nzmax]),
         m_cidx (new octave_idx_type [a.m_ncols + 1]),
@@ -218,6 +225,19 @@
     : m_rep (new typename Sparse<T>::SparseRep (nr, nc, nz)),
       m_dimensions (dim_vector (nr, nc)) { }
 
+  // Construct a Sparse array from pointers to externally allocated
+  // arrays of values and indices.  PTR, RIDX, and CIDX must be
+  // allocated with operator new.  The Sparse object takes ownership of
+  // these arrays and will delete them when the Sparse object is
+  // deleted.  The dimension vector DV must be consistent with the sizes
+  // of the allocated PTR, CIDX, and RIDX arrays.
+
+  Sparse (const dim_vector& dv, octave_idx_type nz,
+          T *ptr, octave_idx_type *ridx, octave_idx_type *cidx)
+    : m_rep (new typename Sparse<T>::SparseRep (dv, nz, ptr, ridx, cidx)),
+      m_dimensions (dv)
+  { }
+
   // Both SparseMatrix and SparseBoolMatrix need this ctor, and this
   // is their only common ancestor.
   explicit OCTAVE_API Sparse (const PermMatrix& a);