# HG changeset patch # User Andreas Weber # Date 1355702249 -3600 # Node ID 66a8643c5075d557172a1378cc2acb3920bb4775 # Parent ac9e34f835224e48d51cacda54c49452cf24241c added Multi-threaded support for FFTW and fixed one bug with do_method () diff -r ac9e34f83522 -r 66a8643c5075 build-aux/common.mk --- a/build-aux/common.mk Sat Dec 15 22:47:26 2012 -0800 +++ b/build-aux/common.mk Mon Dec 17 00:57:29 2012 +0100 @@ -209,7 +209,7 @@ FFTW_XCPPFLAGS = $(FFTW3_CPPFLAGS) $(FFTW3F_CPPFLAGS) FFTW_XLDFLAGS = $(FFTW3_LDFLAGS) $(FFTW3F_LDFLAGS) -FFTW_XLIBS = $(FFTW3_LIBS) $(FFTW3F_LIBS) +FFTW_XLIBS = $(FFTW3_LIBS) $(FFTW3F_LIBS) $(FFTW3_THREADS_LIBS) $(FFTW3F_THREADS_LIBS) FT2_CFLAGS = @FT2_CFLAGS@ FT2_LIBS = @FT2_LIBS@ diff -r ac9e34f83522 -r 66a8643c5075 configure.ac --- a/configure.ac Sat Dec 15 22:47:26 2012 -0800 +++ b/configure.ac Mon Dec 17 00:57:29 2012 +0100 @@ -791,6 +791,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"]) @@ -2794,9 +2814,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 diff -r ac9e34f83522 -r 66a8643c5075 libinterp/dldfcn/fftw.cc --- a/libinterp/dldfcn/fftw.cc Sat Dec 15 22:47:26 2012 -0800 +++ b/libinterp/dldfcn/fftw.cc Mon Dec 17 00:57:29 2012 +0100 @@ -38,6 +38,8 @@ @deftypefnx {Loadable Function} {} fftw (\"planner\", @var{method})\n\ @deftypefnx {Loadable Function} address@hidden =} fftw (\"dwisdom\")\n\ @deftypefnx {Loadable Function} {} fftw (\"dwisdom\", @var{wisdom})\n\ address@hidden {Loadable Function} {} fftw (\"threads\", @var{NTHREADS})\n\ address@hidden {Loadable Function} address@hidden =} 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\ @@ -110,6 +112,14 @@ 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 transforms can be set with\n\ +\n\ address@hidden +fftw (\"threads\", @var{NTHREADS})\n\ address@hidden example\n\ +\n\ +Note that octave must be compiled for Multi-threaded FFTW support for this feature.\n\ @seealso{fft, ifft, fft2, ifft2, fftn, ifftn}\n\ @end deftypefn") { @@ -127,106 +137,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 +222,107 @@ 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 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 ("fftw: 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 ("fftw: this copy of Octave was not configured to use the multithreaded fftw libraries."); +#endif + } + else + error ("fftw: number of threads must be >=1"); + } + else + error ("fftw: setting threads needs one integer argument."); + } else - error ("unrecognized argument"); + retval = octave_value (octave_fftw_planner::threads()); } + else + error ("unrecognized argument"); } } + else + error ("unrecognized argument"); #else warning ("fftw: this copy of Octave was not configured to use the FFTW3 planner"); diff -r ac9e34f83522 -r 66a8643c5075 liboctave/numeric/oct-fftw.cc --- a/liboctave/numeric/oct-fftw.cc Sat Dec 15 22:47:26 2012 -0800 +++ b/liboctave/numeric/oct-fftw.cc Mon Dec 17 00:57:29 2012 +0100 @@ -57,7 +57,7 @@ octave_fftw_planner::octave_fftw_planner (void) : meth (ESTIMATE), rplan (0), rd (0), rs (0), rr (0), rh (0), rn (), - rsimd_align (false) + rsimd_align (false), nthreads(NUM_DEFAULT_THREADS) { plan[0] = plan[1] = 0; d[0] = d[1] = s[0] = s[1] = r[0] = r[1] = h[0] = h[1] = 0; @@ -65,6 +65,15 @@ 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 NUM_DEFAULT_THREADS threads. + //This can be later changed with fftw("threads",nthreads) + fftw_plan_with_nthreads(nthreads); +#endif + // If we have a system wide wisdom file, import it. fftw_import_system_wisdom (); } @@ -387,7 +396,7 @@ octave_float_fftw_planner::octave_float_fftw_planner (void) : meth (ESTIMATE), rplan (0), rd (0), rs (0), rr (0), rh (0), rn (), - rsimd_align (false) + rsimd_align (false), nthreads(NUM_DEFAULT_THREADS) { plan[0] = plan[1] = 0; d[0] = d[1] = s[0] = s[1] = r[0] = r[1] = h[0] = h[1] = 0; @@ -395,6 +404,15 @@ 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 NUM_DEFAULT_THREADS threads. + //This can be later changed with fftw("threads",nthreads) + fftwf_plan_with_nthreads(nthreads); +#endif + // If we have a system wide wisdom file, import it. fftwf_import_system_wisdom (); } diff -r ac9e34f83522 -r 66a8643c5075 liboctave/numeric/oct-fftw.h --- a/liboctave/numeric/oct-fftw.h Sat Dec 15 22:47:26 2012 -0800 +++ b/liboctave/numeric/oct-fftw.h Mon Dec 17 00:57:29 2012 +0100 @@ -32,6 +32,8 @@ #include "oct-cmplx.h" #include "dim-vector.h" +#define NUM_DEFAULT_THREADS 2 + #if defined (HAVE_FFTW) class @@ -97,6 +99,22 @@ return instance_ok () ? instance->do_method (_meth) : dummy; } + + static void threads (int _nthreads) + { + if (instance_ok ()) + { + 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; + } private: @@ -126,7 +144,7 @@ FftwMethod do_method (FftwMethod _meth); FftwMethod meth; - + // FIXME -- perhaps this should be split into two classes? // Plan for fft and ifft of complex values @@ -169,6 +187,9 @@ dim_vector rn; bool rsimd_align; + + //number of threads when compiled with Multi-threading support + int nthreads; }; class @@ -225,14 +246,30 @@ { static FftwMethod dummy; - return instance_ok () ? instance->method () : dummy; + return instance_ok () ? instance->do_method () : dummy; } static FftwMethod method (FftwMethod _meth) { static FftwMethod dummy; - return instance_ok () ? instance->method (_meth) : dummy; + return instance_ok () ? instance->do_method (_meth) : dummy; + } + + static void threads (int _nthreads) + { + if (instance_ok ()) + { + 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; } private: @@ -306,6 +343,9 @@ dim_vector rn; bool rsimd_align; + + //number of threads when compiled with Multi-threading support + int nthreads; }; class