/************************************************************************************* Grid physics library, www.github.com/paboyle/Grid Source file: ./tests/IO/Test_aggregate_io.cc Copyright (C) 2015 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 */ // Correctness and performance test for BINARYIO_AGGREGATE. // // Correctness, per aggregateTargetBytes: // 1. write via the MPI-IO lexicographic path -> ref.bin // 2. write via the aggregate path -> agg.bin // 3. the two files must be byte identical <- proves the layout matches // 4. read agg.bin back through the aggregate path <- proves the mirror inverts // 5. write and read the non-lexicographic path -> raw.bin. Its layout is // different by construction (each rank owns one contiguous segment in // rank order) so it cannot be compared byte for byte, but the NERSC and // SciDAC checksums are computed from the global site index and are // therefore layout independent: they must match the other two paths. // 6. a record written over a longer pre-existing file must leave the file at // exactly offset+payload, with no trailing fragment of the old contents // // Performance: three paths, both directions, timed with the client page cache // dropped before every read so that a read back reports filesystem bandwidth // rather than memory bandwidth. The non-lexicographic path is the zero // overhead reference: no transposition, no layout independence, one disjoint // contiguous segment per rank, which is the arrangement that reaches full // filesystem bandwidth on a leadership machine. It is the upper bound the // other two are trying to approach. // // Options: // --aggregate-target sweep this one target only (default: sweep // 1, 1024, 64K, 4M) // --io-reps repetitions in the performance section // (default 3; 0 disables it) // --io-no-correctness skip the correctness section, which reads the // whole file on one rank and is not affordable // at very large volume // --io-read-only time reads only, of files left in place by an // earlier job. Reading back what this job just // wrote measures the client page cache; a fresh // allocation pointed at the same directory is // the only way to get a cold read without root. // // The exchange is only meaningfully exercised when the fast dimensions are // split across ranks; --mpi 1.1.X.Y leaves the rows of size one and the test // then passes vacuously. Non-uniform AllToAllV counts additionally need an // odd process factor in a fast dimension and a small local volume. #include #include using namespace Grid; ///////////////////////////////////////////////////////////////////////////// // Compare in chunks. Slurping both files into memory is fine for a few MB // and fatal for the multi-GB records this test is meant to reach. ///////////////////////////////////////////////////////////////////////////// static bool FilesIdentical(std::string a,std::string b) { std::ifstream fa(a,std::ios::binary), fb(b,std::ios::binary); if ( !fa.good() || !fb.good() ) { std::cout< va(chunk), vb(chunk); uint64_t done=0; while ( done < sa ) { uint64_t n = std::min(chunk,sa-done); fa.read(&va[0],n); fb.read(&vb[0],n); for(uint64_t i=0;iBarrier(); int fd = ::open(file.c_str(),O_RDONLY); if ( fd >= 0 ) { #ifdef POSIX_FADV_DONTNEED ::posix_fadvise(fd,0,0,POSIX_FADV_DONTNEED); #endif ::close(fd); } grid->Barrier(); } static uint64_t OptionU64(int argc,char **argv,const char *opt,uint64_t def) { if ( GridCmdOptionExists(argv,argv+argc,opt) ) { std::string arg = GridCmdOptionPayload(argv,argv+argc,opt); return (uint64_t)std::stoull(arg); } return def; } int main(int argc,char **argv) { Grid_init(&argc,&argv); Coordinate latt = GridDefaultLatt(); Coordinate simd = GridDefaultSimd(Nd,vComplexD::Nsimd()); Coordinate mpi = GridDefaultMpi(); GridCartesian grid(latt,simd,mpi); typedef vLorentzColourMatrixD vobj; typedef LorentzColourMatrixD sobj; GridParallelRNG pRNG(&grid); pRNG.SeedFixedIntegers(std::vector({1,2,3,4})); LatticeGaugeFieldD Umu(&grid); random(pRNG,Umu); BinarySimpleMunger munge; const std::string format("IEEE64BIG"); const int lex = BinaryIO::BINARYIO_LEXICOGRAPHIC; const int agg = BinaryIO::BINARYIO_LEXICOGRAPHIC|BinaryIO::BINARYIO_AGGREGATE; const int raw = 0; // no BINARYIO_LEXICOGRAPHIC: contiguous segment per rank uint64_t payload = (uint64_t)grid._gsites*sizeof(sobj); std::vector targets = {1, 1024, 64*1024, 4*1024*1024}; if ( GridCmdOptionExists(argv,argv+argc,"--aggregate-target") ) { targets.clear(); targets.push_back(OptionU64(argc,argv,"--aggregate-target",4*1024*1024)); } uint64_t reps = OptionU64(argc,argv,"--io-reps",3); bool correctness = !GridCmdOptionExists(argv,argv+argc,"--io-no-correctness"); // Read only: time reads of files left by an earlier job. The only way to // get a cold client cache without root is to read on an allocation that did // not write the data, so run one job to write and a second, pointed at the // same directory, with this flag. bool readonly = GridCmdOptionExists(argv,argv+argc,"--io-read-only"); if ( readonly ) correctness = false; std::cout<(Umu,"ref.bin",munge,off,format,n1,a1,b1,lex); BinaryIO::aggregateTargetBytes = target; off=0; BinaryIO::writeLatticeObject(Umu,"agg.bin",munge,off,format,n2,a2,b2,agg); grid.Barrier(); if ( grid.IsBoss() ) { if ( !FilesIdentical("ref.bin","agg.bin") ) { std::cout<(Uchk,"agg.bin",munge,off,format,n3,a3,b3,agg); if ( (n3!=n1)||(a3!=a1)||(b3!=b1) ) { std::cout<(Umu,"raw.bin",munge,off,format,n4,a4,b4,raw); grid.Barrier(); if ( (n4!=n1)||(a4!=a1)||(b4!=b1) ) { std::cout<(Uchk,"raw.bin",munge,off,format,n5,a5,b5,raw); if ( (n5!=n1)||(a5!=a1)||(b5!=b1) ) { std::cout<(Umu,"trunc.bin",munge,off,format,n,a,b,agg); grid.Barrier(); if ( grid.IsBoss() ) { std::ifstream f("trunc.bin",std::ios::binary|std::ios::ate); uint64_t sz = (uint64_t)f.tellg(); f.close(); if ( sz != expect ) { std::cout< wref,wagg,wraw,rref,ragg,rraw; for(uint64_t rep=0;rep(Umu,"ref.bin",munge,off,format,n,a,b,lex); wref.push_back(BinaryIO::lastPerf.mbytesPerSecond); cn[0]=n; ca[0]=a; cb[0]=b; off=0; BinaryIO::writeLatticeObject(Umu,"agg.bin",munge,off,format,n,a,b,agg); wagg.push_back(BinaryIO::lastPerf.mbytesPerSecond); cn[1]=n; ca[1]=a; cb[1]=b; off=0; BinaryIO::writeLatticeObject(Umu,"raw.bin",munge,off,format,n,a,b,raw); wraw.push_back(BinaryIO::lastPerf.mbytesPerSecond); cn[2]=n; ca[2]=a; cb[2]=b; if ( !agreeing("write",0,2) ) failures++; } // !readonly DropCache(&grid,"ref.bin"); off=0; BinaryIO::readLatticeObject(Uio,"ref.bin",munge,off,format,n,a,b,lex); rref.push_back(BinaryIO::lastPerf.mbytesPerSecond); cn[3]=n; ca[3]=a; cb[3]=b; DropCache(&grid,"agg.bin"); off=0; BinaryIO::readLatticeObject(Uio,"agg.bin",munge,off,format,n,a,b,agg); ragg.push_back(BinaryIO::lastPerf.mbytesPerSecond); cn[4]=n; ca[4]=a; cb[4]=b; DropCache(&grid,"raw.bin"); off=0; BinaryIO::readLatticeObject(Uio,"raw.bin",munge,off,format,n,a,b,raw); rraw.push_back(BinaryIO::lastPerf.mbytesPerSecond); cn[5]=n; ca[5]=a; cb[5]=b; if ( !agreeing("read back",readonly?3:0,5) ) failures++; } if ( grid.IsBoss() ) { auto report = [&](const char *name,std::vector &v){ if ( v.empty() ) return; RealD best=0, sum=0; for(auto x : v){ if(x>best) best=x; sum+=x; } // First sample includes file creation, later ones do not; quote both // rather than a mean that mixes the two. std::cout< &v){ RealD m=0; for(auto x:v) if(x>m) m=x; return m; }; if ( !wraw.empty() && best(wraw) > 0 ) { std::cout< 0 ) { std::cout<