summaryrefslogtreecommitdiff
path: root/src/fftw3/kernel/planner.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fftw3/kernel/planner.c')
-rw-r--r--src/fftw3/kernel/planner.c695
1 files changed, 0 insertions, 695 deletions
diff --git a/src/fftw3/kernel/planner.c b/src/fftw3/kernel/planner.c
deleted file mode 100644
index 1d01cb9..0000000
--- a/src/fftw3/kernel/planner.c
+++ /dev/null
@@ -1,695 +0,0 @@
-/*
- * Copyright (c) 2000 Matteo Frigo
- * Copyright (c) 2000 Massachusetts Institute of Technology
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-/* $Id: planner.c,v 1.1 2008/10/17 06:11:29 scuri Exp $ */
-#include "ifftw.h"
-#include <string.h>
-
-/* GNU Coding Standards, Sec. 5.2: "Please write the comments in a GNU
- program in English, because English is the one language that nearly
- all programmers in all countries can read."
-
- ingemisco tanquam reus
- culpa rubet vultus meus
- supplicanti parce [rms]
-*/
-
-#define BLESSEDP(solution) ((solution)->flags & BLESSING)
-#define VALIDP(solution) ((solution)->flags & H_VALID)
-#define UNBLESS(flags) flags &= ~BLESSING
-
-#define MAXNAM 64 /* maximum length of registrar's name.
- Used for reading wisdom. There is no point
- in doing this right */
-
-/* Flags f1 subsumes flags f2 iff f1 is less/equally impatient than
- f2, defining a partial ordering. */
-#define IMPATIENCE(flags) ((flags) & IMPATIENCE_FLAGS)
-#define NONIMPATIENCE(flags) ((flags) & NONIMPATIENCE_FLAGS)
-#define ORDERED(f1, f2) (SUBSUMES(f1, f2) || SUBSUMES(f2, f1))
-#define SUBSUMES(f1, f2) ((IMPATIENCE(f1) & (f2)) == IMPATIENCE(f1))
-
-static unsigned addmod(unsigned a, unsigned b, unsigned p)
-{
- /* gcc-2.95/sparc produces incorrect code for the fast version below. */
-#if defined(__sparc__) && defined(__GNUC__)
- /* slow version */
- return (a + b) % p;
-#else
- /* faster version */
- unsigned c = a + b;
- return c >= p ? c - p : c;
-#endif
-}
-
-/*
- slvdesc management:
-*/
-static void sgrow(planner *ego)
-{
- unsigned osiz = ego->slvdescsiz, nsiz = 1 + osiz + osiz / 4;
- slvdesc *ntab = (slvdesc *)MALLOC(nsiz * sizeof(slvdesc), SLVDESCS);
- slvdesc *otab = ego->slvdescs;
- unsigned i;
-
- ego->slvdescs = ntab;
- ego->slvdescsiz = nsiz;
- for (i = 0; i < osiz; ++i)
- ntab[i] = otab[i];
- X(ifree0)(otab);
-}
-
-static void register_solver(planner *ego, solver *s)
-{
- slvdesc *n;
- if (s) { /* add s to solver list */
- X(solver_use)(s);
-
- if (ego->nslvdesc >= ego->slvdescsiz)
- sgrow(ego);
-
- n = ego->slvdescs + ego->nslvdesc++;
-
- n->slv = s;
- n->reg_nam = ego->cur_reg_nam;
- n->reg_id = ego->cur_reg_id++;
-
- A(strlen(n->reg_nam) < MAXNAM);
- n->nam_hash = X(hash)(n->reg_nam);
- }
-}
-
-static int slookup(planner *ego, char *nam, int id)
-{
- unsigned h = X(hash)(nam); /* used to avoid strcmp in the common case */
- FORALL_SOLVERS(ego, s, sp, {
- UNUSED(s);
- if (sp->reg_id == id && sp->nam_hash == h
- && !strcmp(sp->reg_nam, nam))
- return sp - ego->slvdescs;
- });
- return -1;
-}
-
-/*
- md5-related stuff:
-*/
-
-/* first hash function */
-static unsigned h1(planner *ego, const md5sig s)
-{
- return s[0] % ego->hashsiz;
-}
-
-/* second hash function (for double hashing) */
-static unsigned h2(planner *ego, const md5sig s)
-{
- return 1U + s[1] % (ego->hashsiz - 1);
-}
-
-static void md5hash(md5 *m, const problem *p, const planner *plnr)
-{
- X(md5begin)(m);
- X(md5unsigned)(m, sizeof(R)); /* so we don't mix different precisions */
- X(md5unsigned)(m, plnr->problem_flags);
- X(md5int)(m, plnr->nthr);
- p->adt->hash(p, m);
- X(md5end)(m);
-}
-
-static int md5eq(const md5sig a, const md5sig b)
-{
- return a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3];
-}
-
-static void sigcpy(const md5sig a, md5sig b)
-{
- b[0] = a[0]; b[1] = a[1]; b[2] = a[2]; b[3] = a[3];
-}
-
-/*
- memoization routines :
-*/
-
-/*
- liber scriptus proferetur
- in quo totum continetur
- unde mundus iudicetur
-*/
-struct solution_s {
- md5sig s;
- unsigned short flags;
- short slvndx;
-};
-
-static solution *hlookup(planner *ego, const md5sig s, unsigned short flags)
-{
- unsigned g, h = h1(ego, s), d = h2(ego, s);
-
- ++ego->lookup;
-
- for (g = h; ; g = addmod(g, d, ego->hashsiz)) {
- solution *l = ego->solutions + g;
- ++ego->lookup_iter;
- if (VALIDP(l)) {
- if (md5eq(s, l->s) && ORDERED(l->flags, flags)) {
- ++ego->succ_lookup;
- return l;
- }
- } else {
- return 0;
- }
- A((g + d) % ego->hashsiz != h);
- }
-}
-
-
-static void hinsert0(planner *ego, const md5sig s, unsigned short flags,
- int slvndx, solution *l)
-{
- ++ego->insert;
- if (!l) {
- /* search for nonfull slot */
- unsigned g, h = h1(ego, s), d = h2(ego, s);
- ++ego->insert_unknown;
- for (g = h; ; g = addmod(g, d, ego->hashsiz)) {
- ++ego->insert_iter;
- l = ego->solutions + g;
- if (!VALIDP(l)) break;
- A((g + d) % ego->hashsiz != h);
- }
- }
-
- /* fill slot */
- l->flags = flags | H_VALID;
- l->slvndx = (short)slvndx;
- sigcpy(s, l->s);
-}
-
-static void rehash(planner *ego, unsigned nsiz)
-{
- unsigned osiz = ego->hashsiz, h;
- solution *osol = ego->solutions, *nsol;
-
- nsiz = (unsigned)X(next_prime)((int)nsiz);
- nsol = (solution *)MALLOC(nsiz * sizeof(solution), HASHT);
- ++ego->nrehash;
-
- /* init new table */
- for (h = 0; h < nsiz; ++h)
- nsol[h].flags = 0;
-
- /* install new table */
- ego->hashsiz = nsiz;
- ego->solutions = nsol;
-
- /* copy table */
- for (h = 0; h < osiz; ++h) {
- solution *l = osol + h;
- if (VALIDP(l))
- hinsert0(ego, l->s, l->flags, l->slvndx, 0);
- }
-
- X(ifree0)(osol);
-}
-
-static unsigned minsz(unsigned nelem)
-{
- return 1U + nelem + nelem / 8U;
-}
-
-static unsigned nextsz(unsigned nelem)
-{
- return minsz(minsz(nelem));
-}
-
-static void hgrow(planner *ego)
-{
- unsigned nelem = ego->nelem;
- if (minsz(nelem) >= ego->hashsiz)
- rehash(ego, nextsz(nelem));
-}
-
-static void hshrink(planner *ego)
-{
- unsigned nelem = ego->nelem;
- /* always rehash after deletions */
- rehash(ego, nextsz(nelem));
-}
-
-/* inherit blessing, but only if the solver is the same */
-static unsigned short merge_flags(unsigned short dstflags, int dstndx,
- unsigned short srcflags, int srcndx)
-{
- if (srcndx == dstndx)
- dstflags |= (srcflags & BLESSING); /* ne me perdas illa die */
- return dstflags;
-}
-
-static void hinsert(planner *ego, const md5sig s,
- unsigned short flags, int slvndx)
-{
- solution *l;
-
- if ((l = hlookup(ego, s, flags))) {
- if (SUBSUMES(flags, l->flags)) {
- /* overwrite old solution */
- flags = merge_flags(flags, slvndx, l->flags, l->slvndx);
- } else {
- A(SUBSUMES(l->flags, flags));
- l->flags = merge_flags(l->flags, l->slvndx, flags, slvndx);
- return;
- }
- } else {
- ++ego->nelem;
- hgrow(ego);
- }
- hinsert0(ego, s, flags, slvndx, l);
-}
-
-static void hcurse_subsumed(planner *ego)
-{
- unsigned h;
-
- /* unbless any entries that are unreachable because they
- are subsumed by less-impatient ones. */
- for (h = 0; h < ego->hashsiz; ++h) {
- solution *l = ego->solutions + h;
- if (VALIDP(l)) {
- unsigned d = h2(ego, l->s), g = addmod(h, d, ego->hashsiz);
- for (; ; g = addmod(g, d, ego->hashsiz)) {
- solution *m = ego->solutions + g;
- if (VALIDP(m)) {
- if (md5eq(l->s, m->s) &&
- SUBSUMES(l->flags, m->flags)) {
- /* ne cadant in obscurum */
- l->flags = merge_flags(l->flags, l->slvndx,
- m->flags, m->slvndx);
-
- /* cum vix justus sit securus */
- UNBLESS(m->flags);
- }
- }
- else break;
- A((g + d) % ego->hashsiz != h);
- }
- }
- }
-}
-
-
-static void invoke_hook(planner *ego, plan *pln, const problem *p,
- int optimalp)
-{
- if (ego->hook)
- ego->hook(pln, p, optimalp);
-}
-
-static void evaluate_plan(planner *ego, plan *pln, const problem *p)
-{
- if (!BELIEVE_PCOSTP(ego) || pln->pcost == 0.0) {
- ego->nplan++;
-
- if (ESTIMATEP(ego)) {
- estimate:
- /* heuristic */
- pln->pcost = 0.0
- + pln->ops.add
- + pln->ops.mul
- + 2 * pln->ops.fma
- + pln->ops.other;
- ego->epcost += pln->pcost;
- } else {
- double t = X(measure_execution_time)(pln, p);
-
- if (t < 0) { /* unavailable cycle counter */
- /* Real programmers can write FORTRAN in any language */
- goto estimate;
- }
-
- pln->pcost = t;
- ego->pcost += t;
- }
- }
-
- invoke_hook(ego, pln, p, 0);
-}
-
-/* maintain dynamic scoping of flags, nthr: */
-static plan *invoke_solver(planner *ego, problem *p, solver *s,
- unsigned short nflags)
-{
- unsigned short planner_flags = ego->planner_flags;
- unsigned problem_flags = ego->problem_flags;
- int nthr = ego->nthr;
- plan *pln;
- ego->planner_flags = nflags;
- pln = s->adt->mkplan(s, p, ego);
- ego->problem_flags = problem_flags;
- ego->nthr = nthr;
- ego->planner_flags = planner_flags;
- return pln;
-}
-
-static plan *search(planner *ego, problem *p, slvdesc **descp)
-{
- plan *best = 0;
- int best_not_yet_timed = 1;
- int pass;
-
- if (NO_SEARCHP(ego)) {
- /* D("invalid search for %P %x\n", p, ego->planner_flags); */
- return 0;
- }
-
- for (pass = 0; pass < 2; ++pass) {
- unsigned short nflags = ego->planner_flags;
-
- if (best) break;
-
- switch (pass) {
- case 0:
- /* skip pass 0 during exhaustive search */
- if (!NO_EXHAUSTIVEP(ego)) continue;
- nflags |= NO_UGLY;
- break;
- case 1:
- /* skip pass 1 if NO_UGLY */
- if (NO_UGLYP(ego)) continue;
- break;
- }
-
- FORALL_SOLVERS(ego, s, sp, {
- plan *pln = invoke_solver(ego, p, s, nflags);
-
- if (pln) {
- if (best) {
- if (best_not_yet_timed) {
- evaluate_plan(ego, best, p);
- best_not_yet_timed = 0;
- }
- evaluate_plan(ego, pln, p);
- if (pln->pcost < best->pcost) {
- X(plan_destroy_internal)(best);
- best = pln;
- *descp = sp;
- } else {
- X(plan_destroy_internal)(pln);
- }
- } else {
- best = pln;
- *descp = sp;
- }
- }
- });
- }
-
- return best;
-}
-
-static plan *mkplan(planner *ego, problem *p)
-{
- plan *pln;
- md5 m;
- slvdesc *sp;
- unsigned short flags;
- ASSERT_ALIGNED_DOUBLE;
-
- /* Canonical form. */
- if (!NO_EXHAUSTIVEP(ego)) ego->planner_flags &= ~NO_UGLY;
-
- ++ego->nprob;
- md5hash(&m, p, ego);
-
- pln = 0;
-
- {
- solution *sol; /* new scope for sol */
-
- if ((sol = hlookup(ego, m.s, ego->planner_flags))) {
- if (SUBSUMES(sol->flags, ego->planner_flags)) {
- /* wisdom is acceptable */
- if (sol->slvndx < 0)
- return 0; /* known to be infeasible */
-
- /* use solver to obtain a plan */
- sp = ego->slvdescs + sol->slvndx;
- pln =
- invoke_solver(ego, p, sp->slv,
- (0
- | NO_SEARCH
- | IMPATIENCE(sol->flags)
- | NONIMPATIENCE(ego->planner_flags) ));
-
- /* if (!pln) then the entry is bogus, but
- we currently do nothing about it. */
- /* CAVEAS: Do not use ``sol'' here, because the
- pointer is possibly dangling after the call to
- invoke_solver(). */
- } else {
- A(SUBSUMES(ego->planner_flags, sol->flags));
- }
- }
- }
-
-
- if (!pln)
- pln = search(ego, p, &sp);
-
- flags = ego->planner_flags;
-
- if (pln) {
- /* Postulate de iure that NO_UGLY subsumes ~NO_UGLY if the
- problem is feasible. Also postulate that NO_SEARCH
- subsumes ~NO_SEARCH. */
- flags &= ~(NO_UGLY | NO_SEARCH);
- }
-
- hinsert(ego, m.s, flags, pln ? sp - ego->slvdescs : -1);
-
- if (pln)
- invoke_hook(ego, pln, p, 1);
- return pln;
-}
-
-/* destroy hash table entries. If FORGET_EVERYTHING, destroy the whole
- table. If FORGET_ACCURSED, then destroy entries that are not blessed. */
-static void forget(planner *ego, amnesia a)
-{
- unsigned h;
-
- /* garbage-collect while we are at it */
- if (a != FORGET_EVERYTHING)
- hcurse_subsumed(ego);
-
- for (h = 0; h < ego->hashsiz; ++h) {
- solution *l = ego->solutions + h;
- if (VALIDP(l)) {
- if (a == FORGET_EVERYTHING ||
- (a == FORGET_ACCURSED && !BLESSEDP(l))) {
- /* confutatis maledictis
- flammis acribus addictis */
- l->flags &= ~H_VALID;
- --ego->nelem;
- }
- }
- }
- /* nil inultum remanebit */
-
- hshrink(ego);
-}
-
-static void htab_destroy(planner *ego)
-{
- forget(ego, FORGET_EVERYTHING);
- X(ifree)(ego->solutions);
- ego->nelem = 0U;
-}
-
-/* FIXME: what sort of version information should we write? */
-#define WISDOM_PREAMBLE PACKAGE "-" VERSION " " STRINGIZE(X(wisdom))
-
-/* tantus labor non sit cassus */
-static void exprt(planner *ego, printer *p)
-{
- unsigned h;
-
- hcurse_subsumed(ego);
-
- p->print(p, "(" WISDOM_PREAMBLE "%(");
- for (h = 0; h < ego->hashsiz; ++h) {
- solution *l = ego->solutions + h;
- if (VALIDP(l) && BLESSEDP(l) && l->slvndx >= 0) {
- slvdesc *sp = ego->slvdescs + l->slvndx;
- /* qui salvandos salvas gratis
- salva me fons pietatis */
- p->print(p, "(%s %d #x%x #x%M #x%M #x%M #x%M)\n",
- sp->reg_nam, sp->reg_id, (int)l->flags,
- l->s[0], l->s[1], l->s[2], l->s[3]);
- }
- }
- p->print(p, "%))\n");
-}
-
-/* mors stupebit et natura
- cum resurget creatura */
-static int imprt(planner *ego, scanner *sc)
-{
- char buf[MAXNAM + 1];
- md5uint sig[4];
- int flags;
- int reg_id;
- int slvndx;
- solution *sol;
-
- if (!sc->scan(sc, "(" WISDOM_PREAMBLE))
- return 0; /* don't need to restore hashtable */
-
- /* make a backup copy of the hash table (cache the hash) */
- {
- unsigned h, hsiz = ego->hashsiz;
- sol = (solution *)MALLOC(hsiz * sizeof(solution), HASHT);
- for (h = 0; h < hsiz; ++h)
- sol[h] = ego->solutions[h];
- }
-
- while (1) {
- if (sc->scan(sc, ")"))
- break;
-
- /* qua resurget ex favilla */
- if (!sc->scan(sc, "(%*s %d #x%x #x%M #x%M #x%M #x%M)",
- MAXNAM, buf, &reg_id, &flags,
- sig + 0, sig + 1, sig + 2, sig + 3))
- goto bad;
-
- if ((slvndx = slookup(ego, buf, reg_id)) < 0)
- goto bad;
-
- /* inter oves locum praesta */
- hinsert(ego, sig, (unsigned short)flags, slvndx);
- }
-
- X(ifree0)(sol);
- return 1;
-
- bad:
- /* ``The wisdom of FFTW must be above suspicion.'' */
- X(ifree0)(ego->solutions);
- ego->solutions = sol;
- return 0;
-}
-
-/*
- * create a planner
- */
-planner *X(mkplanner)(void)
-{
- static const planner_adt padt = {
- register_solver, mkplan, forget, exprt, imprt
- };
-
- planner *p = (planner *) MALLOC(sizeof(planner), PLANNERS);
-
- p->adt = &padt;
- p->nplan = p->nprob = p->nrehash = 0;
- p->pcost = p->epcost = 0.0;
- p->succ_lookup = p->lookup = p->lookup_iter = 0;
- p->insert = p->insert_iter = p->insert_unknown = 0;
- p->hook = 0;
- p->cur_reg_nam = 0;
-
- p->slvdescs = 0;
- p->nslvdesc = p->slvdescsiz = 0;
-
- p->solutions = 0;
- p->hashsiz = p->nelem = 0U;
-
- p->problem_flags = 0;
- p->planner_flags = 0;
- p->nthr = 1;
-
- hgrow(p); /* so that hashsiz > 0 */
-
- return p;
-}
-
-void X(planner_destroy)(planner *ego)
-{
- /* destroy hash table */
- htab_destroy(ego);
-
- /* destroy solvdesc table */
- FORALL_SOLVERS(ego, s, sp, {
- UNUSED(sp);
- X(solver_destroy)(s);
- });
-
- X(ifree0)(ego->slvdescs);
- X(ifree)(ego); /* dona eis requiem */
-}
-
-plan *X(mkplan_d)(planner *ego, problem *p)
-{
- plan *pln = ego->adt->mkplan(ego, p);
- X(problem_destroy)(p);
- return pln;
-}
-
-/*
- * Debugging code:
- */
-#ifdef FFTW_DEBUG
-
-void X(planner_dump)(planner *ego, int verbose)
-{
- unsigned valid = 0, empty = 0, infeasible = 0;
- unsigned h;
- UNUSED(verbose); /* historical */
-
- for (h = 0; h < ego->hashsiz; ++h) {
- solution *l = ego->solutions + h;
- if (VALIDP(l)) {
- ++valid;
- if (l->slvndx < 0) ++infeasible;
- } else
- ++empty;
-
- }
-
- D("nplan = %d\n", ego->nplan);
- D("nprob = %d\n", ego->nprob);
- D("pcost = %g\n", ego->pcost);
- D("epcost = %g\n", ego->epcost);
- D("lookup = %d\n", ego->lookup);
- D("succ_lookup = %d\n", ego->succ_lookup);
- D("lookup_iter = %d\n", ego->lookup_iter);
- D("insert = %d\n", ego->insert);
- D("insert_iter = %d\n", ego->insert_iter);
- D("insert_unknown = %d\n", ego->insert_unknown);
- D("nrehash = %d\n", ego->nrehash);
- D("hashsiz = %u\n", ego->hashsiz);
- D("empty = %d\n", empty);
- D("valid = %d\n", valid);
- D("infeasible = %d\n", infeasible);
- A(ego->nelem == valid);
-}
-
-#endif