From 953a40136633b45ac14d899bf9882436a4665782 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Thu, 3 Sep 2026 20:59:42 -0400 Subject: [PATCH] Use PlannedFFT in momentum space propagator. --- .../fermion/ContinuedFractionFermion5D.h | 5 +++-- Grid/qcd/action/fermion/DomainWallFermion.h | 3 ++- Grid/qcd/action/fermion/FermionOperator.h | 20 ++++++++++++++++--- .../action/fermion/PartialFractionFermion5D.h | 5 +++-- tests/core/Test_fft_prop.cc | 2 +- 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/Grid/qcd/action/fermion/ContinuedFractionFermion5D.h b/Grid/qcd/action/fermion/ContinuedFractionFermion5D.h index 6a658672b..24886ee28 100644 --- a/Grid/qcd/action/fermion/ContinuedFractionFermion5D.h +++ b/Grid/qcd/action/fermion/ContinuedFractionFermion5D.h @@ -65,8 +65,9 @@ public: std::cout << "Free Propagator for PartialFraction"<FermionGrid()); + PlannedFFT &theFFT = this->FermionGridFFT(); //phase for boundary condition ComplexField coor(in.Grid()); diff --git a/Grid/qcd/action/fermion/DomainWallFermion.h b/Grid/qcd/action/fermion/DomainWallFermion.h index 540ba3a6b..6180824dd 100644 --- a/Grid/qcd/action/fermion/DomainWallFermion.h +++ b/Grid/qcd/action/fermion/DomainWallFermion.h @@ -45,7 +45,8 @@ public: FermionField in_k(in.Grid()); FermionField prop_k(in.Grid()); - FFT theFFT((GridCartesian *) in.Grid()); + GRID_ASSERT(in.Grid() == this->FermionGrid()); + PlannedFFT &theFFT = this->FermionGridFFT(); //phase for boundary condition ComplexField coor(in.Grid()); diff --git a/Grid/qcd/action/fermion/FermionOperator.h b/Grid/qcd/action/fermion/FermionOperator.h index 98d4be146..867f1183d 100644 --- a/Grid/qcd/action/fermion/FermionOperator.h +++ b/Grid/qcd/action/fermion/FermionOperator.h @@ -45,7 +45,7 @@ public: INHERIT_IMPL_TYPES(Impl); FermionOperator(const ImplParams &p= ImplParams()) : Impl(p) {}; - virtual ~FermionOperator(void) = default; + virtual ~FermionOperator(void) { if ( _fermionGridFFT ) delete _fermionGridFFT; } virtual FermionField &tmp(void) = 0; @@ -95,9 +95,23 @@ public: virtual void MomentumSpacePropagator(FermionField &out,const FermionField &in,RealD _m,std::vector twist) { GRID_ASSERT(0);}; - virtual void FreePropagator(const FermionField &in,FermionField &out,RealD mass,std::vector boundary,std::vector twist) +protected: + // Cached planned FFT on the fermion grid -- a general utility (FreePropagator + // today; smoother, smearings and other users anticipated). Frontier FFTW plan + // create+destroy is ~22 ms/call (measured, Test_fft_prop PLANCOST) -- ~4x the + // transform itself and ~80% of an unplanned call -- so a per-call `FFT theFFT(grid)` + // dominates. Lazily built once on FermionGrid() and reused; owned, deleted in the dtor. + PlannedFFT *_fermionGridFFT{nullptr}; + PlannedFFT & FermionGridFFT(void) { + if ( _fermionGridFFT == nullptr ) + _fermionGridFFT = new PlannedFFT((GridCartesian *)this->FermionGrid()); + return *_fermionGridFFT; + } +public: + virtual void FreePropagator(const FermionField &in,FermionField &out,RealD mass,std::vector boundary,std::vector twist) { - FFT theFFT((GridCartesian *) in.Grid()); + GRID_ASSERT(in.Grid() == this->FermionGrid()); + PlannedFFT &theFFT = this->FermionGridFFT(); typedef typename Simd::scalar_type Scalar; diff --git a/Grid/qcd/action/fermion/PartialFractionFermion5D.h b/Grid/qcd/action/fermion/PartialFractionFermion5D.h index f2656a455..68c9680c3 100644 --- a/Grid/qcd/action/fermion/PartialFractionFermion5D.h +++ b/Grid/qcd/action/fermion/PartialFractionFermion5D.h @@ -95,8 +95,9 @@ public: std::cout << "Free Propagator for PartialFraction"<FermionGrid()); + PlannedFFT &theFFT = this->FermionGridFFT(); //phase for boundary condition ComplexField coor(in.Grid()); diff --git a/tests/core/Test_fft_prop.cc b/tests/core/Test_fft_prop.cc index 72bcbd4a2..ca8d3aa64 100644 --- a/tests/core/Test_fft_prop.cc +++ b/tests/core/Test_fft_prop.cc @@ -46,7 +46,7 @@ void bench(GridCartesian *grid, std::string name) LatticeObject S(grid); gaussian(RNG,S); typedef typename LatticeObject::vector_object vobj; - const int nrep = 10; + const int nrep = 50; // Correctness + Parseval pass (unplanned), also the WARMUP that absorbs // FFTW/allocator/MPI first-touch so the two timed passes below are both warm