FFTW implements a method to save plans to disk and restore them. In
fact, what FFTW does is more general than just saving and loading plans.
The mechanism is called wisdom
. Here, we describe this
feature at a high level. See section FFTW Reference, for a less casual (but
more complete) discussion of how to use wisdom
in FFTW.
Plans created with the FFTW_MEASURE
option produce near-optimal
FFT performance, but it can take a long time to compute a plan because
FFTW must actually measure the runtime of many possible plans and select
the best one. This is designed for the situations where so many
transforms of the same size must be computed that the start-up time is
irrelevant. For short initialization times but slightly slower
transforms, we have provided FFTW_ESTIMATE
. The wisdom
mechanism is a way to get the best of both worlds. There are, however,
certain caveats that the user must be aware of in using wisdom
.
For this reason, wisdom
is an optional feature which is not
enabled by default.
At its simplest, wisdom
provides a way of saving plans to disk so
that they can be reused in other program runs. You create a plan with
the flags FFTW_MEASURE
and FFTW_USE_WISDOM
, and then save
the wisdom
using fftw_export_wisdom
. The next time you
run the program, you can restore the wisdom
with
fftw_import_wisdom
, and then recreate the plan using the same
flags as before. This time, however, the same optimal plan will be
created (relatively) instantaneously, without measurements.
Wisdom is more than mere rote memorization, however. FFTW's
wisdom
encompasses all of the knowledge and measurements that
were used to create the plan for a given size. Therefore, existing
wisdom
is also applied to the creation of other plans of
different sizes.
Whenever a plan is created with the FFTW_MEASURE
and
FFTW_USE_WISDOM
flags, wisdom
is generated. Thereafter,
plans for any transform with a similar factorization will be computed
more quickly, so long as they use the FFTW_USE_WISDOM
flag. In
fact, for transforms with the same factors and of equal or lesser size,
no measurements at all need to be made and an optimal plan can be
created with negligible delay!
For example, suppose that you create a plan for
N = 216.
Then, for any equal or smaller power of two, FFTW can create a
plan (with the same direction and flags) instantaneously, using the
precomputed wisdom
. Even for larger powers of two, or sizes that
are a power of two times some other prime factors, plans will be
computed more quickly than they would otherwise (although some
measurements still have to be made).
The wisdom
is cumulative, and is stored in a global, private data
structure managed internally by FFTW. The storage space required is
minimal, proportional to the log of the sizes the wisdom
was
generated from. The wisdom
can be forgotten (and its associated
memory freed) by a call to fftw_forget_wisdom()
; otherwise, it is
remembered until the program terminates. It can also be exported to a
file, a string, or any other medium using fftw_export_wisdom
and
restored during a subsequent execution of the program (or a different
program) using fftw_import_wisdom
(these functions are described
below).
Because wisdom
is incorporated into FFTW at a very low level, the
same wisdom
can be used for one-dimensional transforms,
multi-dimensional transforms, and even the real-complex and parallel
extensions to FFTW. Just include FFTW_USE_WISDOM
in the flags
for whatever plans you create (i.e., always plan wisely).
Plans created with the FFTW_ESTIMATE
plan can use wisdom
,
but cannot generate it; only FFTW_MEASURE
plans actually produce
wisdom
. Also, plans can only use wisdom
generated from
plans created with the same direction and flags. For example, a size
42
FFTW_BACKWARD
transform will not use wisdom
produced by a size 42
FFTW_FORWARD
transform. The only
exception to this rule is that FFTW_ESTIMATE
plans can use
wisdom
from FFTW_MEASURE
plans.
For in much wisdom is much grief, and he that increaseth knowledge increaseth sorrow. [Ecclesiastes 1:18]
There are pitfalls to using wisdom
, in that it can negate FFTW's
ability to adapt to changing hardware and other conditions. For example,
it would be perfectly possible to export wisdom
from a program
running on one processor and import it into a program running on another
processor. Doing so, however, would mean that the second program would
use plans optimized for the first processor, instead of the one it is
running on.
It should be safe to reuse wisdom
as long as the hardware and
program binaries remain unchanged. (Actually, the optimal plan may
change even between runs of the same binary on identical hardware, due
to differences in the virtual memory environment, etcetera. Users
really interested in performance should worry about this problem, too.)
It is likely that, if the same wisdom
is used for two different
program binaries, even running on the same machine, the plans may be
sub-optimal because of differing code alignments. It is therefore wise
to recreate wisdom
every time an application is recompiled. The
more the underlying hardware and software changes between the creation
of wisdom
and its use, the greater grows the risk of sub-optimal
plans.
On an unrelated note, we should point out that the accumulation of
wisdom
is not thread-safe, although the utilization of
wisdom
is.
void fftw_export_wisdom_to_file(FILE *output_file); fftw_status fftw_import_wisdom_from_file(FILE *input_file);
fftw_export_wisdom_to_file
writes the wisdom
to
output_file
, which must be a file open for
writing. fftw_import_wisdom_from_file
reads the wisdom
from input_file
, which must be a file open for reading, and
returns FFTW_SUCCESS
if successful and FFTW_FAILURE
otherwise. In both cases, the file is left open and must be closed by
the caller. It is perfectly fine if other data lie before or after the
wisdom
in the file, as long as the file is positioned at the
beginning of the wisdom
data before import.
char *fftw_export_wisdom_to_string(void); fftw_status fftw_import_wisdom_from_string(const char *input_string)
fftw_export_wisdom_to_string
allocates a string, exports the
wisdom
to it in NULL
-terminated format, and returns a
pointer to the string. If there is an error in allocating or writing
the data, it returns NULL
. The caller is responsible for
deallocating the string (with fftw_free
) when she is done with
it. fftw_import_wisdom_from_string
imports the wisdom
from
input_string
, returning FFTW_SUCCESS
if successful and
FFTW_FAILURE
otherwise.
Exporting wisdom
does not affect the store of wisdom
. Imported
wisdom
supplements the current store rather than replacing it
(except when there is conflicting wisdom
, in which case the older
wisdom
is discarded). The format of the exported wisdom
is
"nerd-readable" LISP-like ASCII text; we will not document it here
except to note that it is insensitive to white space (interested users
can contact us for more details).
See section FFTW Reference, for more information, and for a description of
how you can implement wisdom
import/export for other media
besides files and strings.
The following is a brief example in which the wisdom
is read from
a file, a plan is created (possibly generating more wisdom
), and
then the wisdom
is exported to a string and printed to
stdout
.
{ fftw_plan plan; char *wisdom_string; FILE *input_file; /* open file to read wisdom from */ input_file = fopen("sample.wisdom", "r"); if (FFTW_FAILURE == fftw_import_wisdom_from_file(input_file)) printf("Error reading wisdom!\n"); fclose(input_file); /* be sure to close the file! */ /* create a plan for N=64, possibly creating and/or using wisdom */ plan = fftw_create_plan(64,FFTW_FORWARD, FFTW_MEASURE | FFTW_USE_WISDOM); /* ... do some computations with the plan ... */ /* always destroy plans when you are done */ fftw_destroy_plan(plan); /* write the wisdom to a string */ wisdom_string = fftw_export_wisdom_to_string(); if (wisdom_string != NULL) { printf("Accumulated wisdom: %s\n",wisdom_string); /* Just for fun, destroy and restore the wisdom */ fftw_forget_wisdom(); /* all gone! */ fftw_import_wisdom_from_string(wisdom_string); /* wisdom is back! */ fftw_free(wisdom_string); /* deallocate it since we're done */ } }