/************************************************************************************* Grid physics library, www.github.com/paboyle/Grid Source file: ./tests/debug/Test_allgather.cc Copyright (C) 2026 Author: Peter Boyle 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. See the full license in the file "LICENSE" in the top level distribution directory *************************************************************************************/ /* END LEGAL */ ////////////////////////////////////////////////////////////////////////////// // Regression gate for CartesianCommunicator::AllGather / AllGatherV. // // CPU build under mpirun: // mpirun -n 1 ./Test_allgather --grid 8.8.8.8 --mpi 1.1.1.1 // mpirun -n 2 ./Test_allgather --grid 8.8.8.8 --mpi 1.1.1.2 // mpirun -n 3 ./Test_allgather --grid 8.8.8.12 --mpi 1.1.1.3 // mpirun -n 4 ./Test_allgather --grid 8.8.8.8 --mpi 1.1.1.4 // // T1 : uniform AllGather, rank-ordered concatenation. // T2 : AllGatherV with non-uniform counts and displacements. // T3 : bitwise equivalence with the zero-fill + GlobalSumVector idiom // that AllGatherV is intended to replace. // T4 : the same equivalence for the GatherGemm panel layout -- a // column-major kchunk x n panel whose rows are owned in contiguous // rank-major ranges, requiring a pack on send and a repack on receive. // // T3 and T4 are the ones that matter: they certify AllGatherV as a drop-in // for the existing idiom, bit for bit, before it is used anywhere. ////////////////////////////////////////////////////////////////////////////// #include using namespace Grid; static int failures = 0; static void Report(const std::string &name, bool pass, const std::string &detail="") { std::cout << GridLogMessage << " " << name << (pass ? " PASS" : " ** FAIL **"); if ( detail.size() ) std::cout << " " << detail; std::cout << std::endl; if ( !pass ) failures++; } // A value that is unique to (rank, index) so that any misplacement shows up. static ComplexD Stamp(int rank, int64_t idx) { return ComplexD( 1000.0*(rank+1) + (double)idx, -(double)(rank+1) ); } int main(int argc, char **argv) { Grid_init(&argc, &argv); GridCartesian *grid = SpaceTimeGrid::makeFourDimGrid(GridDefaultLatt(), GridDefaultSimd(Nd, vComplexD::Nsimd()), GridDefaultMpi()); const int P = grid->ProcessorCount(); const int me = grid->ThisRank(); std::cout << GridLogMessage << "AllGather regression: " << P << " ranks" << std::endl; //////////////////////////////////////////////////////////////////////// // T1 : uniform AllGather //////////////////////////////////////////////////////////////////////// { const int64_t W = 37; // words per rank std::vector mine(W), all((uint64_t)W*P); for(int64_t i=0;iAllGather((void *)&mine[0], (void *)&all[0], (uint64_t)W, sizeof(ComplexD)); bool ok = true; for(int r=0;r counts(P), displs(P); int64_t total = 0; for(int r=0;r mine(counts[me]), all(total); for(int64_t i=0;iAllGatherV((void *)&mine[0], counts[me], (void *)&all[0], counts, displs, sizeof(ComplexD)); bool ok = true; for(int r=0;r counts(P), displs(P); int64_t total = 0; for(int r=0;r mine(counts[me]); for(int64_t i=0;i viaSum(total, ComplexD(0.0,0.0)); for(int64_t i=0;iGlobalSumVector(&viaSum[0], (int)total); // (b) the primitive std::vector viaGather(total); grid->AllGatherV((void *)&mine[0], counts[me], (void *)&viaGather[0], counts, displs, sizeof(ComplexD)); bool ok = true; for(int64_t i=0;i rowStart(P+1); rowStart[0]=0; for(int r=0;r mine((uint64_t)myRows*n); for(int64_t j=0;j viaSum((uint64_t)kchunk*n, ComplexD(0.0,0.0)); for(int64_t j=0;jGlobalSumVector(&viaSum[0], (int)(kchunk*n)); // (b) AllGatherV into rank-major order, then repack into the panel std::vector counts(P), displs(P); int64_t tot=0; for(int r=0;r rankMajor(tot); grid->AllGatherV((void *)&mine[0], counts[me], (void *)&rankMajor[0], counts, displs, sizeof(ComplexD)); std::vector viaGather((uint64_t)kchunk*n); for(int r=0;r counts(P,0), displs(P,0); int64_t total=0; for(int r=0;r dmine(W), dall(total); std::vector hmine(W), hall(total); for(int64_t i=0;iAllGatherV((void *)&dmine[0], counts[me], (void *)&dall[0], counts, displs, sizeof(ComplexD)); acceleratorCopyFromDevice(&dall[0],&hall[0],total*sizeof(ComplexD)); bool ok=true; for(int r=0;r P ? P : AGN); std::vector counts(P,0), displs(P,0); int64_t total=0; for(int r=0;r dmine(W>0?W:1), dall(total>0?total:1); std::vector hmine(W), hall(total); for(int64_t i=0;iAllGatherV((void *)&dmine[0], counts[me], (void *)&dall[0], counts, displs, sizeof(ComplexD)); acceleratorCopyFromDevice(&dall[0],&hall[0],total*sizeof(ComplexD)); bool ok=true; for(int r=0;r