From e6cae41480c258b9ffc59724983ec3705d7350d6 Mon Sep 17 00:00:00 2001 From: Antonin Portelli Date: Wed, 21 Oct 2026 14:23:15 +0900 Subject: [PATCH] comms buffer copy faces in parallel with prefetching, guarded by GRID_FUSED_HALO_LOOPS and only activates on A64FX, but maybe beneficial to other arch --- Grid/qcd/action/fermion/WilsonCompressor.h | 8 +++ Grid/stencil/SimpleCompressor.h | 76 ++++++++++++++++++++++ Grid/stencil/Stencil.h | 9 +++ 3 files changed, 93 insertions(+) diff --git a/Grid/qcd/action/fermion/WilsonCompressor.h b/Grid/qcd/action/fermion/WilsonCompressor.h index 7bc3e23bb..c87dff325 100644 --- a/Grid/qcd/action/fermion/WilsonCompressor.h +++ b/Grid/qcd/action/fermion/WilsonCompressor.h @@ -253,6 +253,11 @@ public: int dag = compress.dag; int face_idx=0; +#ifdef GRID_FUSED_HALO_LOOPS + // queue the face gathers and run them together below (not on the first call, which builds the face tables) + const bool fused = this->face_table_computed; + if ( fused ) FusedHaloLoops::begin(); +#endif #define vet_same_node(a,b) \ { auto tmp = b; } if ( dag ) { @@ -274,6 +279,9 @@ public: vet_same_node(this->same_node[Zm],this->HaloGatherDir(source,ZpCompress,Zm,face_idx)); vet_same_node(this->same_node[Tm],this->HaloGatherDir(source,TpCompress,Tm,face_idx)); } +#ifdef GRID_FUSED_HALO_LOOPS + if ( fused ) FusedHaloLoops::end(); +#endif this->face_table_computed=1; GRID_ASSERT(this->u_comm_offset==this->_unified_buffer_size); accelerator_barrier(); diff --git a/Grid/stencil/SimpleCompressor.h b/Grid/stencil/SimpleCompressor.h index 41c1fbe1d..5536c1ce4 100644 --- a/Grid/stencil/SimpleCompressor.h +++ b/Grid/stencil/SimpleCompressor.h @@ -11,6 +11,43 @@ public: }; +// On A64FX (OpenMP CPU builds) the face gathers of one halo exchange, and the merges, run in one parallel +// region: each loop is queued, then every thread takes its static share of each loop, with one barrier at +// the end instead of one parallel region per face. The gathers also prefetch the source sites 4 entries +// ahead. CPU only: the queued loops run after the views of the source field are closed, which is safe +// only because a CPU view is a plain pointer to host memory. +#if defined(A64FX) && defined(_OPENMP) && !defined(GRID_SIMT) +#define GRID_FUSED_HALO_LOOPS +struct FusedHaloLoops { + // a queued loop over sites [0,n): body(lo,hi) runs sites lo..hi-1 + typedef std::pair > Loop; + static std::vector &queue(void) { static std::vector q; return q; } + static bool &deferring(void) { static bool d = false; return d; } + // between begin() and end(), the face loops are queued (push) instead of run; end() runs them all + static void begin(void) { deferring() = true; } + static void end(void) { deferring() = false; run(); } + static void push(uint64_t n,std::function body) { queue().push_back({n,body}); } + static const int prefetch_distance = 4; + template static inline void prefetch(const T *p) { + for(int o=0;o<(int)sizeof(T);o+=256) __builtin_prefetch((const char *)p+o,0,3); // 256-byte lines + } + static void run(void) { + auto &q = queue(); + if ( q.empty() ) return; + // one chunk per thread, as in the lattice reductions (Lattice_reduction.h) + const int nthread = GridThread::GetThreads(); + thread_for(thr,nthread,{ + for(auto &loop : q) { + int mywork, myoff; + GridThread::GetWork((int)loop.first,thr,mywork,myoff); + if ( mywork > 0 ) loop.second(myoff,myoff+mywork); + } + }); + q.clear(); + } +}; +#endif + // Compressors will inherit buffer management policies // Standard comms buffer management class FaceGatherSimple @@ -29,6 +66,20 @@ public: std::pair *table_v = & table[0]; auto rhs_v = rhs.View(AcceleratorRead); +#ifdef GRID_FUSED_HALO_LOOPS + if ( FusedHaloLoops::deferring() ) { + const vobj *rp = &rhs_v[0]; + const int pf = FusedHaloLoops::prefetch_distance; + FusedHaloLoops::push((uint64_t)num, [=](uint64_t lo,uint64_t hi) { + for(uint64_t i=lo;i void CommsMerge(decompressor decompress,std::vector &mm,std::vector &dd) { +#ifdef GRID_FUSED_HALO_LOOPS + if ( dd.size()==0 ) { // merges only: run them in one parallel region (SimpleCompressor.h) + FusedHaloLoops::begin(); + for(int i=0;i