From 499d656949793de5fc4897824450151c846c7475 Mon Sep 17 00:00:00 2001 From: Peter Boyle Date: Wed, 12 Aug 2026 12:47:16 -0400 Subject: [PATCH] Batch blas test --- tests/debug/Test_batched_blas.cc | 256 +++++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 tests/debug/Test_batched_blas.cc diff --git a/tests/debug/Test_batched_blas.cc b/tests/debug/Test_batched_blas.cc new file mode 100644 index 000000000..474dcaf5a --- /dev/null +++ b/tests/debug/Test_batched_blas.cc @@ -0,0 +1,256 @@ +/************************************************************************************* + + Grid physics library, www.github.com/paboyle/Grid + + Source file: ./tests/debug/Test_batched_blas.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. + + 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + See the full license in the file "LICENSE" in the top level distribution directory + *************************************************************************************/ +/* END LEGAL */ + +// +// Unit tests for the blas-layer dense primitives: +// +// T1 : GridBLASInverse::inverseBatched (ComplexF and ComplexD) -- +// || A A^{-1} - I ||_max over a random well-conditioned batch. +// On a CPU build this exercises the Eigen reference (the semantic +// oracle); the SAME binary exercises rocSOLVER/cuBLAS/oneMKL on a +// device build. +// T2 : explicit-leading-dimension gemmBatched == SOFTWARE SPLIT-K in +// miniature. Y = op(A,T).X computed (a) in one compact batch=1 call +// and (b) as NK K-chunks by POINTER OFFSET with lda = full K, partials +// reduced on the host. (a)==(b) validates the strided overload AND +// the production dense-slab apply pattern (arXiv:2409.03904 fig 11). +// +// Hard asserts throughout: regression gate for Grid/algorithms/blas. +// +#include +#include + +#include + +using namespace std; +using namespace Grid; + +int main (int argc, char ** argv) +{ + Grid_init(&argc,&argv); + + GridBLAS blas; + GridBLASInverse inverse; + + std::mt19937 rng(12345); + std::uniform_real_distribution dist(-1.0,1.0); + + //////////////////////////////////////////////////////////////// + // T1a : batched inversion, ComplexF + //////////////////////////////////////////////////////////////// + { + const int64_t N = 64; + const int batch = 4; + const uint64_t elems = (uint64_t)batch*N*N; + + // Random diagonally-dominant batch: A = N*I + R, |R_ij| <= 1 + std::vector Ahost(elems); + for(uint64_t i=0;i Adev(elems); // gets inverted in place + deviceVector Aorig(elems); // untouched copy for the residual + deviceVector Cdev(elems); + acceleratorCopyToDevice(&Ahost[0],&Adev[0], elems*sizeof(ComplexF)); + acceleratorCopyToDevice(&Ahost[0],&Aorig[0],elems*sizeof(ComplexF)); + + deviceVector Ap(batch); + deviceVector Op(batch); + deviceVector Cp(batch); + std::vector ptr_h(batch); + for(int b=0;b Chost(elems); + acceleratorCopyFromDevice(&Cdev[0],&Chost[0],elems*sizeof(ComplexF)); + + double maxdev = 0.0; + for(int b=0;b Ahost(elems); + for(uint64_t i=0;i Adev(elems); + deviceVector Aorig(elems); + deviceVector Cdev(elems); + acceleratorCopyToDevice(&Ahost[0],&Adev[0], elems*sizeof(ComplexD)); + acceleratorCopyToDevice(&Ahost[0],&Aorig[0],elems*sizeof(ComplexD)); + + deviceVector Ap(batch); + deviceVector Op(batch); + deviceVector Cp(batch); + std::vector ptr_h(batch); + for(int b=0;b Chost(elems); + acceleratorCopyFromDevice(&Cdev[0],&Chost[0],elems*sizeof(ComplexD)); + + double maxdev = 0.0; + for(int b=0;b Ahost((uint64_t)K*nrows); + std::vector Xhost((uint64_t)K*nrhs); + for(auto &z : Ahost) z = ComplexF(dist(rng),dist(rng)); + for(auto &z : Xhost) z = ComplexF(dist(rng),dist(rng)); + + deviceVector Adev(Ahost.size()); + deviceVector Xdev(Xhost.size()); + deviceVector Yref((uint64_t)nrows*nrhs); + deviceVector Ypart((uint64_t)NK*nrows*nrhs); + acceleratorCopyToDevice(&Ahost[0],&Adev[0],Ahost.size()*sizeof(ComplexF)); + acceleratorCopyToDevice(&Xhost[0],&Xdev[0],Xhost.size()*sizeof(ComplexF)); + + ComplexF one (1.0,0.0); + ComplexF zero(0.0,0.0); + + // (a) reference: one compact batch=1 call (compact lda == K for OP_T) + { + deviceVector Ap(1), Xp(1), Yp(1); + std::vector h(1); + h[0]=&Adev[0]; acceleratorCopyToDevice(&h[0],&Ap[0],sizeof(ComplexF*)); + h[0]=&Xdev[0]; acceleratorCopyToDevice(&h[0],&Xp[0],sizeof(ComplexF*)); + h[0]=&Yref[0]; acceleratorCopyToDevice(&h[0],&Yp[0],sizeof(ComplexF*)); + blas.gemmBatched(GridBLAS_OP_T,GridBLAS_OP_N, + nrows,nrhs,K, + one, Ap, Xp, zero, Yp); + blas.synchronise(); + } + + // (b) split-K: NK chunk-pointers into the SAME allocations, lda/ldb = K + { + deviceVector Ap(NK), Xp(NK), Yp(NK); + std::vector h(NK); + for(int j=0;j Yref_h((uint64_t)nrows*nrhs); + std::vector Ypart_h((uint64_t)NK*nrows*nrhs); + acceleratorCopyFromDevice(&Yref[0], &Yref_h[0], Yref_h.size()*sizeof(ComplexF)); + acceleratorCopyFromDevice(&Ypart[0],&Ypart_h[0],Ypart_h.size()*sizeof(ComplexF)); + + double maxdev = 0.0; + double maxval = 0.0; + for(int i=0;i