furnace/extern/fftw/tests/fftw-bench.c
2022-05-31 03:24:29 -05:00

303 lines
7.2 KiB
C

/* See bench.c. We keep a few common subroutines in this file so
that they can be re-used in the MPI test program. */
#include <math.h>
#include <stdio.h>
#include <string.h>
#include "tests/fftw-bench.h"
/* define to enable code that traps floating-point exceptions.
Disabled by default because I don't want to worry about the
portability of such code. feenableexcept() seems to be a GNU
thing */
#undef TRAP_FP_EXCEPTIONS
#ifdef TRAP_FP_EXCEPTIONS
# include <signal.h>
# include <fenv.h>
#endif
#ifdef _OPENMP
# include <omp.h>
#endif
#ifdef HAVE_SMP
int threads_ok = 1;
#endif
FFTW(plan) the_plan = 0;
static const char *wisdat = "wis.dat";
unsigned the_flags = 0;
int paranoid = 0;
int usewisdom = 0;
int havewisdom = 0;
int nthreads = 1;
int amnesia = 0;
extern void install_hook(void); /* in hook.c */
extern void uninstall_hook(void); /* in hook.c */
#ifdef FFTW_RANDOM_ESTIMATOR
extern unsigned FFTW(random_estimate_seed);
#endif
#ifdef TRAP_FP_EXCEPTIONS
static void sigfpe_handler(int sig, siginfo_t *info, void *context)
{
/* fftw code is not supposed to generate FP exceptions */
UNUSED(sig); UNUSED(info); UNUSED(context);
fprintf(stderr, "caught FPE, aborting\n");
abort();
}
static void setup_sigfpe_handler(void)
{
struct sigaction a;
feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW);
memset(&a, 0, sizeof(a));
a.sa_sigaction = sigfpe_handler;
a.sa_flags = SA_SIGINFO;
if (sigaction(SIGFPE, &a, NULL) == -1) {
fprintf(stderr, "cannot install sigfpe handler\n");
exit(1);
}
}
#else
static void setup_sigfpe_handler(void)
{
}
#endif
/* dummy serial threads backend for testing threads_set_callback */
static void serial_threads(void *(*work)(char *), char *jobdata, size_t elsize, int njobs, void *data)
{
int i;
(void) data; /* unused */
for (i = 0; i < njobs; ++i)
work(jobdata + elsize * i);
}
void useropt(const char *arg)
{
int x;
double y;
if (!strcmp(arg, "patient")) the_flags |= FFTW_PATIENT;
else if (!strcmp(arg, "estimate")) the_flags |= FFTW_ESTIMATE;
else if (!strcmp(arg, "estimatepat")) the_flags |= FFTW_ESTIMATE_PATIENT;
else if (!strcmp(arg, "exhaustive")) the_flags |= FFTW_EXHAUSTIVE;
else if (!strcmp(arg, "unaligned")) the_flags |= FFTW_UNALIGNED;
else if (!strcmp(arg, "nosimd")) the_flags |= FFTW_NO_SIMD;
else if (!strcmp(arg, "noindirectop")) the_flags |= FFTW_NO_INDIRECT_OP;
else if (!strcmp(arg, "wisdom-only")) the_flags |= FFTW_WISDOM_ONLY;
else if (sscanf(arg, "flag=%d", &x) == 1) the_flags |= x;
else if (sscanf(arg, "bflag=%d", &x) == 1) the_flags |= 1U << x;
else if (!strcmp(arg, "paranoid")) paranoid = 1;
else if (!strcmp(arg, "wisdom")) usewisdom = 1;
else if (!strcmp(arg, "amnesia")) amnesia = 1;
else if (!strcmp(arg, "threads_callback"))
#ifdef HAVE_SMP
FFTW(threads_set_callback)(serial_threads, NULL);
#else
fprintf(stderr, "Serial FFTW; ignoring threads_callback option.\n");
#endif
else if (sscanf(arg, "nthreads=%d", &x) == 1) nthreads = x;
#ifdef FFTW_RANDOM_ESTIMATOR
else if (sscanf(arg, "eseed=%d", &x) == 1) FFTW(random_estimate_seed) = x;
#endif
else if (sscanf(arg, "timelimit=%lg", &y) == 1) {
FFTW(set_timelimit)(y);
}
else fprintf(stderr, "unknown user option: %s. Ignoring.\n", arg);
}
void rdwisdom(void)
{
FILE *f;
double tim;
int success = 0;
if (havewisdom) return;
#ifdef HAVE_SMP
if (threads_ok) {
BENCH_ASSERT(FFTW(init_threads)());
FFTW(plan_with_nthreads)(nthreads);
BENCH_ASSERT(FFTW(planner_nthreads)() == nthreads);
FFTW(make_planner_thread_safe)();
#ifdef _OPENMP
omp_set_num_threads(nthreads);
#endif
}
else if (nthreads > 1 && verbose > 1) {
fprintf(stderr, "bench: WARNING - nthreads = %d, but threads not supported\n", nthreads);
nthreads = 1;
}
#endif
if (!usewisdom) return;
timer_start(USER_TIMER);
if ((f = fopen(wisdat, "r"))) {
if (!import_wisdom(f))
fprintf(stderr, "bench: ERROR reading wisdom\n");
else
success = 1;
fclose(f);
}
tim = timer_stop(USER_TIMER);
if (success) {
if (verbose > 1) printf("READ WISDOM (%g seconds): ", tim);
if (verbose > 3)
export_wisdom(stdout);
if (verbose > 1)
printf("\n");
}
havewisdom = 1;
}
void wrwisdom(void)
{
FILE *f;
double tim;
if (!havewisdom) return;
timer_start(USER_TIMER);
if ((f = fopen(wisdat, "w"))) {
export_wisdom(f);
fclose(f);
}
tim = timer_stop(USER_TIMER);
if (verbose > 1) printf("write wisdom took %g seconds\n", tim);
}
static unsigned preserve_input_flags(bench_problem *p)
{
/*
* fftw3 cannot preserve input for multidimensional c2r transforms.
* Enforce FFTW_DESTROY_INPUT
*/
if (p->kind == PROBLEM_REAL &&
p->sign > 0 &&
!p->in_place &&
p->sz->rnk > 1)
p->destroy_input = 1;
if (p->destroy_input)
return FFTW_DESTROY_INPUT;
else
return FFTW_PRESERVE_INPUT;
}
int can_do(bench_problem *p)
{
double tim;
if (verbose > 2 && p->pstring)
printf("Planning %s...\n", p->pstring);
rdwisdom();
timer_start(USER_TIMER);
the_plan = mkplan(p, preserve_input_flags(p) | the_flags | FFTW_ESTIMATE);
tim = timer_stop(USER_TIMER);
if (verbose > 2) printf("estimate-planner time: %g s\n", tim);
if (the_plan) {
FFTW(destroy_plan)(the_plan);
return 1;
}
return 0;
}
void setup(bench_problem *p)
{
double tim;
setup_sigfpe_handler();
if (amnesia) {
FFTW(forget_wisdom)();
havewisdom = 0;
}
/* Regression test: check that fftw_malloc exists and links
* properly */
{
void *ptr = FFTW(malloc(42));
BENCH_ASSERT(FFTW(alignment_of)((bench_real *)ptr) == 0);
FFTW(free(ptr));
}
rdwisdom();
install_hook();
#ifdef HAVE_SMP
if (verbose > 1 && nthreads > 1) printf("NTHREADS = %d\n", nthreads);
#endif
timer_start(USER_TIMER);
the_plan = mkplan(p, preserve_input_flags(p) | the_flags);
tim = timer_stop(USER_TIMER);
if (verbose > 1) printf("planner time: %g s\n", tim);
BENCH_ASSERT(the_plan);
{
double add, mul, nfma, cost, pcost;
FFTW(flops)(the_plan, &add, &mul, &nfma);
cost = FFTW(estimate_cost)(the_plan);
pcost = FFTW(cost)(the_plan);
if (verbose > 1) {
FFTW(print_plan)(the_plan);
printf("\n");
printf("flops: %0.0f add, %0.0f mul, %0.0f fma\n",
add, mul, nfma);
printf("estimated cost: %f, pcost = %f\n", cost, pcost);
}
}
}
void doit(int iter, bench_problem *p)
{
int i;
FFTW(plan) q = the_plan;
UNUSED(p);
for (i = 0; i < iter; ++i)
FFTW(execute)(q);
}
void done(bench_problem *p)
{
UNUSED(p);
FFTW(destroy_plan)(the_plan);
uninstall_hook();
}
void cleanup(void)
{
initial_cleanup();
wrwisdom();
#ifdef HAVE_SMP
FFTW(cleanup_threads)();
#else
FFTW(cleanup)();
#endif
# ifdef FFTW_DEBUG_MALLOC
{
/* undocumented memory checker */
FFTW_EXTERN void FFTW(malloc_print_minfo)(int v);
FFTW(malloc_print_minfo)(verbose);
}
# endif
final_cleanup();
}