changeset 15960:bde7731b2b83

added FFTW multithreaded library support build-aux/common.mk: added FFTW3_THREADS_LIBS and FFTW3F_THREADS_LIB configure.ac: added checks for threaded fftw libs and --disable-fftw-threads switch libinterp/dldfcn/fftw.cc: added fftw ("threads", ...) getter and setter liboctave/numeric/oct-fftw.cc: added use of multithreaded FFT liboctave/numeric/oct-fftw.h: added nthreads getter and setter
author Andreas Weber <andy.weber.aw@gmail.com>
date Mon, 14 Jan 2013 21:01:49 +0100
parents 0de9c904bcf1
children e9f6c773332c
files build-aux/common.mk configure.ac libinterp/dldfcn/fftw.cc liboctave/numeric/oct-fftw.cc liboctave/numeric/oct-fftw.h
diffstat 5 files changed, 264 insertions(+), 94 deletions(-) [+]
line wrap: on
line diff
--- a/build-aux/common.mk	Mon Jan 14 20:26:36 2013 -0500
+++ b/build-aux/common.mk	Mon Jan 14 21:01:49 2013 +0100
@@ -220,7 +220,7 @@
 
 FFTW_XCPPFLAGS = $(FFTW3_CPPFLAGS) $(FFTW3F_CPPFLAGS)
 FFTW_XLDFLAGS = $(FFTW3_LDFLAGS) $(FFTW3F_LDFLAGS)
-FFTW_XLIBS = $(FFTW3_LIBS) $(FFTW3F_LIBS)
+FFTW_XLIBS = $(FFTW3_THREADS_LIBS) $(FFTW3F_THREADS_LIBS) $(FFTW3_LIBS) $(FFTW3F_LIBS)
 
 FT2_CFLAGS = @FT2_CFLAGS@
 FT2_LIBS = @FT2_LIBS@
--- a/configure.ac	Mon Jan 14 20:26:36 2013 -0500
+++ b/configure.ac	Mon Jan 14 21:01:49 2013 +0100
@@ -812,6 +812,26 @@
   [FFTW3F library not found.  The slower FFTPACK library will be used instead.],
   [fftw3.h], [fftwf_plan_dft_1d])
 
+## Check for the multithreaded FFTW library.
+## Fallback to singlethreaded if not found or disabled
+build_fftw_threads=true
+AC_ARG_ENABLE([fftw-threads],
+  [AS_HELP_STRING([--disable-fftw-threads],
+    [disable Multi-threaded FFTW])],
+  [if test "$enableval" = no; then
+     build_fftw_threads=false
+   fi],
+  [])
+
+if test $build_fftw_threads = true; then
+  OCTAVE_CHECK_LIB(fftw3_threads, FFTW3_THREADS,
+    [FFTW3_THREADS library not found.  The single-threaded library is used instead.],
+    [fftw3.h], [fftw_plan_with_nthreads])
+  OCTAVE_CHECK_LIB(fftw3f_threads, FFTW3F_THREADS,
+    [FFTW3F_THREADS library not found.  The single-threaded library is used instead.],
+    [fftw3.h], [fftwf_plan_with_nthreads])
+fi
+
 AM_CONDITIONAL([AMCOND_HAVE_FFTW],
   [test -n "$FFTW3_LIBS" && test -n "$FFTW3F_LIBS"])
 
@@ -2887,9 +2907,11 @@
   FFTW3 CPPFLAGS:              $FFTW3_CPPFLAGS
   FFTW3 LDFLAGS:               $FFTW3_LDFLAGS
   FFTW3 libraries:             $FFTW3_LIBS
+  FFTW3_THREADS libraries:     $FFTW3_THREADS_LIBS
   FFTW3F CPPFLAGS:             $FFTW3F_CPPFLAGS
   FFTW3F LDFLAGS:              $FFTW3F_LDFLAGS
   FFTW3F libraries:            $FFTW3F_LIBS
+  FFTW3F_THREADS libraries:    $FFTW3F_THREADS_LIBS
   fontconfig CFLAGS:           $FONTCONFIG_CFLAGS
   fontconfig libraries:        $FONTCONFIG_LIBS
   FreeType2 CFLAGS:            $FT2_CFLAGS
--- a/libinterp/dldfcn/fftw.cc	Mon Jan 14 20:26:36 2013 -0500
+++ b/libinterp/dldfcn/fftw.cc	Mon Jan 14 21:01:49 2013 +0100
@@ -38,6 +38,8 @@
 @deftypefnx {Loadable Function} {} fftw (\"planner\", @var{method})\n\
 @deftypefnx {Loadable Function} {@var{wisdom} =} fftw (\"dwisdom\")\n\
 @deftypefnx {Loadable Function} {} fftw (\"dwisdom\", @var{wisdom})\n\
+@deftypefnx {Loadable Function} {} fftw (\"threads\", @var{nthreads})\n\
+@deftypefnx {Loadable Function} {@var{nthreads} =} fftw (\"threads\")\n\
 \n\
 Manage @sc{fftw} wisdom data.  Wisdom data can be used to significantly\n\
 accelerate the calculation of the FFTs, but implies an initial cost\n\
@@ -60,7 +62,7 @@
 fftw (\"dwisdom\", @var{wisdom})\n\
 @end example\n\
 \n\
-If @var{wisdom} is an empty matrix, then the wisdom used is cleared.\n\
+If @var{wisdom} is an empty string, then the wisdom used is cleared.\n\
 \n\
 During the calculation of Fourier transforms further wisdom is generated.\n\
 The fashion in which this wisdom is generated is also controlled by\n\
@@ -110,6 +112,17 @@
 the wisdom data can be reloaded if it is saved to a file as described\n\
 above.  Saved wisdom files should not be used on different platforms since\n\
 they will not be efficient and the point of calculating the wisdom is lost.\n\
+\n\
+The number of threads used for computing the plans and executing the\n\
+transforms can be set with\n\
+\n\
+@example\n\
+fftw (\"threads\", @var{NTHREADS})\n\
+@end example\n\
+\n\
+Note that octave must be compiled with multi-threaded FFTW support for this feature.\n\
+The number of processors available to the current process is used per default.\n\
+\n\
 @seealso{fft, ifft, fft2, ifft2, fftn, ifftn}\n\
 @end deftypefn")
 {
@@ -127,106 +140,76 @@
   if (args(0).is_string ())
     {
       std::string arg0 = args(0).string_value ();
-
       if (!error_state)
         {
-          // Use STL function to convert to lower case
-          std::transform (arg0.begin (), arg0.end (), arg0.begin (), tolower);
-
-          if (nargin == 2)
+          if (arg0 == "planner")
             {
-              std::string arg1 = args(1).string_value ();
-              if (!error_state)
+              if (nargin == 2)  //planner setter
                 {
-                  if (arg0 == "planner")
+                  if (args(1).is_string ())
                     {
-                      std::transform (arg1.begin (), arg1.end (),
-                                      arg1.begin (), tolower);
-                      octave_fftw_planner::FftwMethod meth
-                        = octave_fftw_planner::UNKNOWN;
-                      octave_float_fftw_planner::FftwMethod methf
-                        = octave_float_fftw_planner::UNKNOWN;
-
-                      if (arg1 == "estimate")
-                        {
-                          meth = octave_fftw_planner::ESTIMATE;
-                          methf = octave_float_fftw_planner::ESTIMATE;
-                        }
-                      else if (arg1 == "measure")
-                        {
-                          meth = octave_fftw_planner::MEASURE;
-                          methf = octave_float_fftw_planner::MEASURE;
-                        }
-                      else if (arg1 == "patient")
-                        {
-                          meth = octave_fftw_planner::PATIENT;
-                          methf = octave_float_fftw_planner::PATIENT;
-                        }
-                      else if (arg1 == "exhaustive")
-                        {
-                          meth = octave_fftw_planner::EXHAUSTIVE;
-                          methf = octave_float_fftw_planner::EXHAUSTIVE;
-                        }
-                      else if (arg1 == "hybrid")
-                        {
-                          meth = octave_fftw_planner::HYBRID;
-                          methf = octave_float_fftw_planner::HYBRID;
-                        }
-                      else
-                        error ("unrecognized planner METHOD");
-
+                      // Use STL function to convert to lower case
+                      std::transform (arg0.begin (), arg0.end (), arg0.begin (), tolower);
+                      std::string arg1 = args(1).string_value ();
                       if (!error_state)
                         {
-                          meth = octave_fftw_planner::method (meth);
-                          octave_float_fftw_planner::method (methf);
+                          std::transform (arg1.begin (), arg1.end (),
+                                          arg1.begin (), tolower);
+                          octave_fftw_planner::FftwMethod meth
+                            = octave_fftw_planner::UNKNOWN;
+                          octave_float_fftw_planner::FftwMethod methf
+                            = octave_float_fftw_planner::UNKNOWN;
 
-                          if (meth == octave_fftw_planner::MEASURE)
-                            retval = octave_value ("measure");
-                          else if (meth == octave_fftw_planner::PATIENT)
-                            retval = octave_value ("patient");
-                          else if (meth == octave_fftw_planner::EXHAUSTIVE)
-                            retval = octave_value ("exhaustive");
-                          else if (meth == octave_fftw_planner::HYBRID)
-                            retval = octave_value ("hybrid");
+                          if (arg1 == "estimate")
+                            {
+                              meth = octave_fftw_planner::ESTIMATE;
+                              methf = octave_float_fftw_planner::ESTIMATE;
+                            }
+                          else if (arg1 == "measure")
+                            {
+                              meth = octave_fftw_planner::MEASURE;
+                              methf = octave_float_fftw_planner::MEASURE;
+                            }
+                          else if (arg1 == "patient")
+                            {
+                              meth = octave_fftw_planner::PATIENT;
+                              methf = octave_float_fftw_planner::PATIENT;
+                            }
+                          else if (arg1 == "exhaustive")
+                            {
+                              meth = octave_fftw_planner::EXHAUSTIVE;
+                              methf = octave_float_fftw_planner::EXHAUSTIVE;
+                            }
+                          else if (arg1 == "hybrid")
+                            {
+                              meth = octave_fftw_planner::HYBRID;
+                              methf = octave_float_fftw_planner::HYBRID;
+                            }
                           else
-                            retval = octave_value ("estimate");
+                            error ("unrecognized planner METHOD");
+
+                          if (!error_state)
+                            {
+                              meth = octave_fftw_planner::method (meth);
+                              octave_float_fftw_planner::method (methf);
+
+                              if (meth == octave_fftw_planner::MEASURE)
+                                retval = octave_value ("measure");
+                              else if (meth == octave_fftw_planner::PATIENT)
+                                retval = octave_value ("patient");
+                              else if (meth == octave_fftw_planner::EXHAUSTIVE)
+                                retval = octave_value ("exhaustive");
+                              else if (meth == octave_fftw_planner::HYBRID)
+                                retval = octave_value ("hybrid");
+                              else
+                                retval = octave_value ("estimate");
+                            }
                         }
                     }
-                  else if (arg0 == "dwisdom")
-                    {
-                      char *str = fftw_export_wisdom_to_string ();
-
-                      if (arg1.length () < 1)
-                        fftw_forget_wisdom ();
-                      else if (! fftw_import_wisdom_from_string (arg1.c_str ()))
-                        error ("could not import supplied WISDOM");
-
-                      if (!error_state)
-                        retval = octave_value (std::string (str));
-
-                      free (str);
-                    }
-                  else if (arg0 == "swisdom")
-                    {
-                      char *str = fftwf_export_wisdom_to_string ();
-
-                      if (arg1.length () < 1)
-                        fftwf_forget_wisdom ();
-                      else if (! fftwf_import_wisdom_from_string (arg1.c_str ()))
-                        error ("could not import supplied WISDOM");
-
-                      if (!error_state)
-                        retval = octave_value (std::string (str));
-
-                      free (str);
-                    }
                   else
-                    error ("unrecognized argument");
+                    error ("fftw planner expects a string value as METHOD");
                 }
-            }
-          else
-            {
-              if (arg0 == "planner")
+              else //planner getter
                 {
                   octave_fftw_planner::FftwMethod meth =
                     octave_fftw_planner::method ();
@@ -242,23 +225,111 @@
                   else
                     retval = octave_value ("estimate");
                 }
-              else if (arg0 == "dwisdom")
+            }
+          else if (arg0 == "dwisdom")
+            {
+              if (nargin == 2)  //dwisdom setter
+                {
+                  if (args(1).is_string ())
+                    {
+                      // Use STL function to convert to lower case
+                      std::transform (arg0.begin (), arg0.end (), arg0.begin (), tolower);
+                      std::string arg1 = args(1).string_value ();
+                      if (!error_state)
+                        {
+                          char *str = fftw_export_wisdom_to_string ();
+
+                          if (arg1.length () < 1)
+                            fftw_forget_wisdom ();
+                          else if (! fftw_import_wisdom_from_string (arg1.c_str ()))
+                            error ("could not import supplied WISDOM");
+
+                          if (!error_state)
+                            retval = octave_value (std::string (str));
+
+                          free (str);
+                        }
+                    }
+                }
+              else //dwisdom getter
                 {
                   char *str = fftw_export_wisdom_to_string ();
                   retval = octave_value (std::string (str));
                   free (str);
                 }
-              else if (arg0 == "swisdom")
+            }
+          else if (arg0 == "swisdom")
+            {
+              //swisdom uses fftwf_ functions (float), dwisdom fftw_ (real)
+              if (nargin == 2)  //swisdom setter
+                {
+                  if (args(1).is_string ())
+                    {
+                      // Use STL function to convert to lower case
+                      std::transform (arg0.begin (), arg0.end (), arg0.begin (), tolower);
+                      std::string arg1 = args(1).string_value ();
+                      if (!error_state)
+                        {
+                          char *str = fftwf_export_wisdom_to_string ();
+
+                          if (arg1.length () < 1)
+                            fftwf_forget_wisdom ();
+                          else if (! fftwf_import_wisdom_from_string (arg1.c_str ()))
+                            error ("could not import supplied WISDOM");
+
+                          if (!error_state)
+                            retval = octave_value (std::string (str));
+
+                          free (str);
+                        }
+                    }
+                }
+              else //swisdom getter
                 {
                   char *str = fftwf_export_wisdom_to_string ();
                   retval = octave_value (std::string (str));
                   free (str);
                 }
-              else
-                error ("unrecognized argument");
             }
+          else if (arg0 == "threads")
+            {
+              if (nargin == 2)  //threads setter
+                {
+                  if (args(1).is_real_scalar ())
+                    {
+                      int nthreads = args(1).int_value();
+                      if ( nthreads >= 1)
+                        {
+#if defined (HAVE_FFTW3_THREADS)
+                          octave_fftw_planner::threads (nthreads);
+#else
+                          warning ("this copy of Octave was not configured to use the multithreaded fftw libraries.");
+#endif
+#if defined (HAVE_FFTW3F_THREADS)
+                          octave_float_fftw_planner::threads (nthreads);
+#else
+                          warning ("this copy of Octave was not configured to use the multithreaded fftwf libraries.");
+#endif
+                        }
+                      else
+                        error ("number of threads must be >=1");
+                    }
+                  else
+                    error ("setting threads needs one integer argument.");
+                }
+              else //threads getter
+#if defined (HAVE_FFTW3_THREADS)              
+                retval = octave_value (octave_fftw_planner::threads());
+#else
+                retval = 1;
+#endif
+            }
+          else
+            error ("unrecognized argument");
         }
     }
+  else
+    error ("unrecognized argument");
 #else
 
   warning ("fftw: this copy of Octave was not configured to use the FFTW3 planner");
@@ -269,7 +340,6 @@
 }
 
 /*
-
 %!testif HAVE_FFTW
 %! def_method = fftw ("planner");
 %! unwind_protect
@@ -295,4 +365,12 @@
 %!error <Invalid call to fftw> fftw ();
 %!error <Invalid call to fftw> fftw ("planner", "estimate", "measure");
 
+%!testif HAVE_FFTW3_THREADS
+%! n = fftw ("threads");
+%! unwind_protect
+%!   fftw ("threads", 3);
+%!   assert (fftw ("threads"), 3);
+%! unwind_protect_cleanup
+%!   fftw ("threads", n);
+%! end_unwind_protect
  */
--- a/liboctave/numeric/oct-fftw.cc	Mon Jan 14 20:26:36 2013 -0500
+++ b/liboctave/numeric/oct-fftw.cc	Mon Jan 14 21:01:49 2013 +0100
@@ -35,6 +35,10 @@
 #include "oct-locbuf.h"
 #include "singleton-cleanup.h"
 
+#if defined (HAVE_FFTW3_THREADS)
+#include "nproc.h"
+#endif
+
 octave_fftw_planner *octave_fftw_planner::instance = 0;
 
 // Helper class to create and cache FFTW plans for both 1D and
@@ -65,6 +69,16 @@
   inplace[0] = inplace[1] = false;
   n[0] = n[1] = dim_vector ();
 
+#if defined (HAVE_FFTW3_THREADS)
+  int init_ret = fftw_init_threads ();
+  if (!init_ret)
+    (*current_liboctave_error_handler) ("Error initializing FFTW threads");
+  //Use number of processors available to the current process
+  //This can be later changed with fftw ("threads", nthreads)
+  nthreads = num_processors (NPROC_CURRENT);
+  fftw_plan_with_nthreads (nthreads);
+#endif
+
   // If we have a system wide wisdom file, import it.
   fftw_import_system_wisdom ();
 }
@@ -395,6 +409,16 @@
   inplace[0] = inplace[1] = false;
   n[0] = n[1] = dim_vector ();
 
+#if defined (HAVE_FFTW3F_THREADS)
+  int init_ret = fftwf_init_threads ();
+  if (!init_ret)
+    (*current_liboctave_error_handler) ("Error initializing FFTW3F threads");
+  //Use number of processors available to the current process
+  //This can be later changed with fftw ("threads", nthreads)
+  nthreads = num_processors (NPROC_CURRENT);
+  fftwf_plan_with_nthreads (nthreads);
+#endif
+
   // If we have a system wide wisdom file, import it.
   fftwf_import_system_wisdom ();
 }
--- a/liboctave/numeric/oct-fftw.h	Mon Jan 14 20:26:36 2013 -0500
+++ b/liboctave/numeric/oct-fftw.h	Mon Jan 14 21:01:49 2013 +0100
@@ -98,6 +98,24 @@
     return instance_ok () ? instance->do_method (_meth) : dummy;
   }
 
+#if defined (HAVE_FFTW3F_THREADS)
+  static void threads (int _nthreads)
+  {
+    if (instance_ok () && _nthreads != threads ())
+      {
+        instance->nthreads = _nthreads;
+        fftw_plan_with_nthreads (_nthreads);
+        //Clear the current plans
+        instance->rplan = instance->plan[0] = instance->plan[1] = 0;
+      }
+  }
+
+  static int threads ()
+  {
+    return instance_ok () ? instance->nthreads : 0;
+  }
+#endif
+
 private:
 
   // No copying!
@@ -169,6 +187,11 @@
   dim_vector rn;
 
   bool rsimd_align;
+
+#if defined (HAVE_FFTW3_THREADS)
+  //number of threads when compiled with Multi-threading support
+  int nthreads;
+#endif
 };
 
 class
@@ -235,6 +258,24 @@
     return instance_ok () ? instance->do_method (_meth) : dummy;
   }
 
+#if defined (HAVE_FFTW3F_THREADS)
+  static void threads (int _nthreads)
+  {
+    if (instance_ok () && _nthreads != threads ())
+      {
+        instance->nthreads = _nthreads;
+        fftwf_plan_with_nthreads (_nthreads);
+        //Clear the current plans
+        instance->rplan = instance->plan[0] = instance->plan[1] = 0;
+      }
+  }
+
+  static int threads ()
+  {
+    return instance_ok () ? instance->nthreads : 0;
+  }
+#endif
+
 private:
 
   // No copying!
@@ -306,6 +347,11 @@
   dim_vector rn;
 
   bool rsimd_align;
+
+#if defined (HAVE_FFTW3F_THREADS)
+  //number of threads when compiled with Multi-threading support
+  int nthreads;
+#endif
 };
 
 class