Mercurial > hg > octave-jordi
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 +++ b/build-aux/common.mk @@ -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 +++ b/configure.ac @@ -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 +++ b/libinterp/dldfcn/fftw.cc @@ -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 +++ b/liboctave/numeric/oct-fftw.cc @@ -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 +++ b/liboctave/numeric/oct-fftw.h @@ -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