Compare commits

..
6 Commits
557 changed files with 6679 additions and 46837 deletions

No files matched your search

+24 -24
View File
@@ -120,7 +120,7 @@ inline void acceleratorMemSet(void *base,int value,size_t bytes) { cudaMemset(ba
cudaGetErrorString( err )); \ cudaGetErrorString( err )); \
printf("File %s Line %d\n",__FILE__,__LINE__); \ printf("File %s Line %d\n",__FILE__,__LINE__); \
fflush(stdout); \ fflush(stdout); \
if (acceleratorAbortOnGpuError) GRID_ASSERT(err==cudaSuccess); \ if (acceleratorAbortOnGpuError) assert(err==cudaSuccess); \
} \ } \
} }
@@ -168,7 +168,7 @@ public:
if ( (_Tp*)ptr == (_Tp *) NULL ) { if ( (_Tp*)ptr == (_Tp *) NULL ) {
printf("Grid Device Allocator got NULL for %lu bytes\n",(unsigned long) bytes ); printf("Grid Device Allocator got NULL for %lu bytes\n",(unsigned long) bytes );
} }
GRID_ASSERT( ( (_Tp*)ptr != (_Tp *)NULL ) ); assert( ( (_Tp*)ptr != (_Tp *)NULL ) );
return ptr; return ptr;
} }
@@ -276,11 +276,11 @@ public:
{ {
#ifdef GRID_HIP #ifdef GRID_HIP
auto err = hipDeviceSynchronize(); auto err = hipDeviceSynchronize();
GRID_ASSERT(err==hipSuccess); assert(err==hipSuccess);
#endif #endif
#ifdef GRID_CUDA #ifdef GRID_CUDA
auto err = cudaDeviceSynchronize(); auto err = cudaDeviceSynchronize();
GRID_ASSERT(err==cudaSuccess); assert(err==cudaSuccess);
#endif #endif
#ifdef GRID_SYCL #ifdef GRID_SYCL
accelerator_barrier(); accelerator_barrier();
@@ -305,8 +305,8 @@ public:
{ {
RealD t2=usecond(); RealD t2=usecond();
GRID_ASSERT(OpA!=GridBLAS_OP_T); // Complex case expect no transpose assert(OpA!=GridBLAS_OP_T); // Complex case expect no transpose
GRID_ASSERT(OpB!=GridBLAS_OP_T); assert(OpB!=GridBLAS_OP_T);
int lda = m; // m x k column major int lda = m; // m x k column major
int ldb = k; // k x n column major int ldb = k; // k x n column major
@@ -341,7 +341,7 @@ public:
(hipblasDoubleComplex *) Bkn, ldb, (hipblasDoubleComplex *) Bkn, ldb,
(hipblasDoubleComplex *) &beta_p[0], (hipblasDoubleComplex *) &beta_p[0],
(hipblasDoubleComplex *) Cmn, ldc); (hipblasDoubleComplex *) Cmn, ldc);
GRID_ASSERT(err==HIPBLAS_STATUS_SUCCESS); assert(err==HIPBLAS_STATUS_SUCCESS);
#endif #endif
#ifdef GRID_CUDA #ifdef GRID_CUDA
cublasOperation_t hOpA; cublasOperation_t hOpA;
@@ -361,7 +361,7 @@ public:
(cuDoubleComplex *) Bkn, ldb, (cuDoubleComplex *) Bkn, ldb,
(cuDoubleComplex *) &beta_p[0], (cuDoubleComplex *) &beta_p[0],
(cuDoubleComplex *) Cmn, ldc); (cuDoubleComplex *) Cmn, ldc);
GRID_ASSERT(err==CUBLAS_STATUS_SUCCESS); assert(err==CUBLAS_STATUS_SUCCESS);
#endif #endif
#ifdef GRID_SYCL #ifdef GRID_SYCL
int64_t m64=m; int64_t m64=m;
@@ -433,8 +433,8 @@ public:
{ {
RealD t2=usecond(); RealD t2=usecond();
GRID_ASSERT(OpA!=GridBLAS_OP_T); // Complex case expect no transpose assert(OpA!=GridBLAS_OP_T); // Complex case expect no transpose
GRID_ASSERT(OpB!=GridBLAS_OP_T); assert(OpB!=GridBLAS_OP_T);
int lda = m; // m x k column major int lda = m; // m x k column major
int ldb = k; // k x n column major int ldb = k; // k x n column major
@@ -469,7 +469,7 @@ public:
(hipblasComplex *) Bkn, ldb, (hipblasComplex *) Bkn, ldb,
(hipblasComplex *) &beta_p[0], (hipblasComplex *) &beta_p[0],
(hipblasComplex *) Cmn, ldc); (hipblasComplex *) Cmn, ldc);
GRID_ASSERT(err==HIPBLAS_STATUS_SUCCESS); assert(err==HIPBLAS_STATUS_SUCCESS);
#endif #endif
#ifdef GRID_CUDA #ifdef GRID_CUDA
cublasOperation_t hOpA; cublasOperation_t hOpA;
@@ -489,7 +489,7 @@ public:
(cuComplex *) Bkn, ldb, (cuComplex *) Bkn, ldb,
(cuComplex *) &beta_p[0], (cuComplex *) &beta_p[0],
(cuComplex *) Cmn, ldc); (cuComplex *) Cmn, ldc);
GRID_ASSERT(err==CUBLAS_STATUS_SUCCESS); assert(err==CUBLAS_STATUS_SUCCESS);
#endif #endif
#ifdef GRID_SYCL #ifdef GRID_SYCL
int64_t m64=m; int64_t m64=m;
@@ -595,11 +595,11 @@ public:
{ {
RealD t2=usecond(); RealD t2=usecond();
int32_t batchCount = Amk.size(); int32_t batchCount = Amk.size();
GRID_ASSERT(Bkn.size()==batchCount); assert(Bkn.size()==batchCount);
GRID_ASSERT(Cmn.size()==batchCount); assert(Cmn.size()==batchCount);
GRID_ASSERT(OpA!=GridBLAS_OP_T); // Complex case expect no transpose assert(OpA!=GridBLAS_OP_T); // Complex case expect no transpose
GRID_ASSERT(OpB!=GridBLAS_OP_T); assert(OpB!=GridBLAS_OP_T);
int lda = m; // m x k column major int lda = m; // m x k column major
int ldb = k; // k x n column major int ldb = k; // k x n column major
@@ -636,7 +636,7 @@ public:
(hipblasDoubleComplex **)&Cmn[0], ldc, (hipblasDoubleComplex **)&Cmn[0], ldc,
batchCount); batchCount);
// std::cout << " hipblas return code " <<(int)err<<std::endl; // std::cout << " hipblas return code " <<(int)err<<std::endl;
GRID_ASSERT(err==HIPBLAS_STATUS_SUCCESS); assert(err==HIPBLAS_STATUS_SUCCESS);
#endif #endif
#ifdef GRID_CUDA #ifdef GRID_CUDA
cublasOperation_t hOpA; cublasOperation_t hOpA;
@@ -657,7 +657,7 @@ public:
(cuDoubleComplex *) &beta_p[0], (cuDoubleComplex *) &beta_p[0],
(cuDoubleComplex **)&Cmn[0], ldc, (cuDoubleComplex **)&Cmn[0], ldc,
batchCount); batchCount);
GRID_ASSERT(err==CUBLAS_STATUS_SUCCESS); assert(err==CUBLAS_STATUS_SUCCESS);
#endif #endif
#ifdef GRID_SYCL #ifdef GRID_SYCL
int64_t m64=m; int64_t m64=m;
@@ -804,8 +804,8 @@ public:
RealD t2=usecond(); RealD t2=usecond();
int32_t batchCount = Amk.size(); int32_t batchCount = Amk.size();
GRID_ASSERT(OpA!=GridBLAS_OP_T); // Complex case expect no transpose assert(OpA!=GridBLAS_OP_T); // Complex case expect no transpose
GRID_ASSERT(OpB!=GridBLAS_OP_T); assert(OpB!=GridBLAS_OP_T);
int lda = m; // m x k column major int lda = m; // m x k column major
int ldb = k; // k x n column major int ldb = k; // k x n column major
@@ -821,8 +821,8 @@ public:
acceleratorCopyToDevice((void *)&beta ,(void *)&beta_p[0],sizeof(ComplexF)); acceleratorCopyToDevice((void *)&beta ,(void *)&beta_p[0],sizeof(ComplexF));
RealD t0=usecond(); RealD t0=usecond();
GRID_ASSERT(Bkn.size()==batchCount); assert(Bkn.size()==batchCount);
GRID_ASSERT(Cmn.size()==batchCount); assert(Cmn.size()==batchCount);
#ifdef GRID_HIP #ifdef GRID_HIP
hipblasOperation_t hOpA; hipblasOperation_t hOpA;
hipblasOperation_t hOpB; hipblasOperation_t hOpB;
@@ -843,7 +843,7 @@ public:
(hipblasComplex **)&Cmn[0], ldc, (hipblasComplex **)&Cmn[0], ldc,
batchCount); batchCount);
GRID_ASSERT(err==HIPBLAS_STATUS_SUCCESS); assert(err==HIPBLAS_STATUS_SUCCESS);
#endif #endif
#ifdef GRID_CUDA #ifdef GRID_CUDA
cublasOperation_t hOpA; cublasOperation_t hOpA;
@@ -864,7 +864,7 @@ public:
(cuComplex *) &beta_p[0], (cuComplex *) &beta_p[0],
(cuComplex **)&Cmn[0], ldc, (cuComplex **)&Cmn[0], ldc,
batchCount); batchCount);
GRID_ASSERT(err==CUBLAS_STATUS_SUCCESS); assert(err==CUBLAS_STATUS_SUCCESS);
#endif #endif
#ifdef GRID_SYCL #ifdef GRID_SYCL
int64_t m64=m; int64_t m64=m;
-185
View File
@@ -1,185 +0,0 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## What This Is
Grid is a data-parallel C++ library for lattice QCD. It provides SIMD-vectorised lattice containers, MPI-based domain decomposition, GPU acceleration (CUDA/HIP/SYCL), and a full suite of QCD algorithms including HMC.
## Build
Uses GNU Autotools. The bootstrap step only needs to run once (or after `configure.ac` changes).
```bash
./bootstrap.sh # downloads Eigen 3.4.0, generates configure
mkdir build && cd build
../configure [options]
make -j$(nproc)
make check # run root-level tests
make install
```
Key configure options:
| Option | Common values |
|--------|---------------|
| `--enable-simd=` | `AVX2`, `AVX512`, `KNL`, `A64FX`, `NEONv8`, `GPU` |
| `--enable-comms=` | `mpi-auto`, `mpi3-auto`, `none` |
| `--enable-accelerator=` | `cuda`, `hip`, `sycl` |
| `--enable-shm=` | `shmopen`, `hugetlbfs`, `nvlink` |
| `--enable-Nc=` | `3` (default), `2`, `4`, `5` |
| `--with-gmp=`, `--with-mpfr=`, `--with-fftw=`, `--with-lime=` | paths to libs |
| `--enable-hdf5`, `--enable-mkl`, `--enable-lapack` | optional features |
GPU builds additionally need `--enable-gen-simd-width=64` (sets 512-bit SIMD width for GPU warp/wavefront sizing) and `--enable-unified=no --enable-shm=nvlink` for multi-GPU runs.
To speed up compilation, `--disable-fermion-reps --disable-gparity` skips instantiating G-parity and higher-representation fermion operators.
Platform recipes from `README.md`:
- **KNL**: `--enable-simd=KNL --enable-comms=mpi3-auto --enable-mkl`
- **Skylake/Haswell**: `--enable-simd=AVX512` or `AVX2` + `--enable-comms=mpi3-auto`
- **AMD EPYC**: `--enable-simd=AVX2 --enable-comms=mpi3`
- **A64FX (Fugaku)**: `--enable-simd=A64FX --enable-comms=mpi3 --enable-shm=shmget` (see `SVE_README.txt`)
Complete, working `configure` invocations for specific HPC systems (Frontier/ROCm, Perlmutter/CUDA, Summit, SDCC-A100, etc.) live in `systems/<platform>/config-command`. These are the canonical references for production builds.
Required external libs: GMP, MPFR, OpenSSL, zlib.
### Use `systems/` for real machines
`systems/<machine>/` holds the known-good build for each production platform (`Frontier`, `Aurora`, `Perlmutter`, `Summit`, `Tursa`, `Lumi`, `Booster`, `Crusher`, `SDCC-*`, `mac-arm`, …). Each contains a `config-command` (the exact `../../configure` invocation) and a `sourceme.sh` (module loads and env). **Prefer copying/adapting these over hand-rolling configure flags** — they encode compiler workarounds, `LDFLAGS`, and shared-memory settings that are easy to get wrong. `systems/WorkArounds.txt` records known vendor bugs.
Note the GPU builds use `--enable-simd=GPU --enable-gen-simd-width=64`, so `Nsimd` is *not* 1 on device (it is `64/sizeof(scalar)`).
### Regenerating `Make.inc` — required after adding or deleting source files
`Make.inc` files are generated, not tracked in git (`.gitignore`d). `scripts/filelist` walks `Grid/`, `tests/*`, `benchmarks/`, `examples/`, and `HMC/` and writes the file lists and per-test `bin_PROGRAMS` rules. Every new `.cc`/`.h` in `Grid/`, and every new `Test_*.cc` / `Benchmark_*.cc` / `Example_*.cc`, is invisible to the build until you run:
```bash
./scripts/filelist # from the source root, then re-run configure/make
```
`bootstrap.sh` runs it for you on the first setup.
## Running Tests and Benchmarks
```bash
# From build directory
make check # root-level tests (Test_simd, Test_cshift, etc.)
make -C tests/<subdir> tests # build tests in a subdirectory
make tests # build all tests across all subdirectories
./tests/core/Test_simd # run a single test binary directly
mpirun -n 4 ./tests/core/Test_cshift --grid 16.16.16.16 --mpi 1.1.1.4
```
`make check` is a thin smoke test — building a subdirectory with `make -C tests/<subdir> tests` and running the relevant binaries directly is the normal development loop. Test binaries take Grid's standard command-line arguments (`--grid`, `--mpi`, `--accelerator-threads`, `--threads`, `--debug-signals`, `--log`); see `Grid/util/Init.cc`.
Test subdirectories and their focus: `core` (SIMD, stencil, comms), `solver` (CG, GMRES, eigensolvers), `hmc` (MD integrators), `forces` (fermion forces), `lanczos`, `IO`, `smearing`, `sp2n`, `debug`.
Tests and benchmarks that need optional fermion representations are guarded by `disable_tests_without_instantiations.h` / `disable_benchmarks_without_instantiations.h`, so a `--disable-fermion-reps --disable-gparity` build silently compiles them to no-ops.
## Architecture
### Layer stack (bottom to top)
1. **SIMD layer** (`Grid/simd/`) — platform-specific intrinsics wrapped into `vRealF`, `vComplexD`, etc. The SIMD width and layout are compile-time constants controlled by `--enable-simd`.
2. **Tensor layer** (`Grid/tensors/`) — Lorentz/colour/spin tensor algebra built on top of SIMD types. `iMatrix`, `iVector`, `iScalar` templates compose into QCD types like `ColourMatrix`, `SpinColourVector`.
3. **Lattice layer** (`Grid/lattice/`) — `Lattice<T>` container: a site-local tensor replicated across a distributed Cartesian grid. All arithmetic is site-parallel and expression-template-fused.
4. **Cartesian/comms layer** (`Grid/cartesian/`, `Grid/communicator/`) — `GridCartesian` holds the MPI topology and local/global geometry. `Grid/cshift/` implements nearest-neighbour halo exchange; `Grid/stencil/` is the optimised multi-hop stencil used by Dirac operators.
5. **Algorithm layer** (`Grid/algorithms/`) — iterative solvers (CG, GMRES, BiCGSTAB, mixed-precision), eigensolvers (Lanczos, LAPACK), FFT, smearing, and multigrid.
6. **QCD layer** (`Grid/qcd/`) — gauge and fermion actions, HMC integrators, observables.
### QCD subsystem (`Grid/qcd/`)
- `action/fermion/` — Wilson, Clover, DWF (Mobius), Staggered, twisted-mass, G-parity variants
- `action/gauge/` — Wilson gauge, Symanzik, Iwasaki, DBW2, plaquette+rect
- `representations/` — Fundamental, Adjoint, Two-index, Sp(2n)
- `hmc/` — Leapfrog, OMF2/OMF4 integrators; pseudofermion refreshment; Metropolis accept/reject
- `smearing/` — APE, Stout, HEX, gradient flow
- `observables/` — Polyakov loop, plaquette, topological charge
### GPU acceleration and the view/memory-manager discipline
### Multigrid (`Grid/algorithms/multigrid/`)
Aggregation-based algebraic multigrid for Wilson-type fermions. Key files: `CoarsenedMatrix.h` (coarse operator), `GeneralCoarsenedMatrix.h` and `GeneralCoarsenedMatrixMultiRHS.h` (general coarsening supporting multi-RHS solves), `Aggregates.h` (near-null vector construction), `Geometry.h` (coarse-grid geometry). `MultiGrid.h` is the top-level include.
### GPU acceleration
GPU support is injected via macros in `Grid/threads/Accelerator.h` — `accelerator_for(i, n, nsimd, {...})`, `accelerator_forNB` (non-blocking, must be followed by `accelerator_barrier()`), `accelerator_for2dNB`, and `accelerator_inline`. On a CPU build these degrade to `thread_for` (OpenMP). Unified virtual memory is on by default (`--enable-unified=yes`); device-aware MPI (`--enable-accelerator-aware-mpi`) avoids device→host copies on transfers.
Lattice data is **not** directly addressable inside a kernel. You must open a view with the correct access mode so `Grid/allocator/MemoryManager.h` can move/mark the data:
```cpp
autoView(out_v, out, AcceleratorWriteDiscard); // RAII; closes at end of scope
autoView(in_v, in, AcceleratorRead);
accelerator_for(ss, grid->oSites(), Nsimd, {
coalescedWrite(out_v[ss], coalescedRead(in_v[ss]));
});
```
Modes are `AcceleratorRead/Write/WriteDiscard` and `CpuRead/Write/WriteDiscard`. Getting the mode wrong (e.g. `AcceleratorRead` on a field you write) produces stale-data bugs that only appear on GPU builds. Inside kernels use `coalescedRead`/`coalescedWrite` rather than raw `operator[]` — they map the SIMD lane onto `threadIdx.x` so accesses stay coalesced.
### Repo-local debugging skills (`skills/`)
`skills/` contains hard-won, Grid-specific playbooks written as invocable skill files. Consult them before debugging in these areas rather than reasoning from first principles:
| File | Covers |
|---|---|
| `gpu-memory-performance.md` | `acceleratorThreads()`, LambdaApply thread mapping, `coalescedRead` idiom, fused vs staged HBM access |
| `gpu-runtime-correctness.md` | GPU runtime returning early from sync, silent wrong answers |
| `communication-overlap.md` | 7-phase halo pipeline, per-packet events, host-staging vs GPU-direct RDMA |
| `mpi-heterogeneous.md` | `MPI_Sendrecv` device-buffer aliasing, deterministic reductions |
| `compiler-validation.md` | Isolating GPU compiler codegen bugs, minimal reproducers |
| `correctness-verification.md` | Double-run fingerprinting, per-packet checksums, flight recorder |
| `hang-diagnosis.md` | Diagnosing MPI/accelerator hangs |
The key loop macros (defined in `Grid/threads/Accelerator.h`) are:
- `accelerator_for(iter, num, nsimd, {...})` — maps to CUDA/HIP kernel or OpenMP loop; `nsimd` is the innermost SIMD lane count
- `accelerator_forNB(...)` — non-blocking variant (no implicit barrier)
- `accelerator_for2dNB(iter1, num1, iter2, num2, nsimd, {...})` — 2D kernel launch
- `thread_for(iter, num, {...})` — CPU OpenMP loop (never dispatches to GPU)
On CPU builds, `accelerator_for` aliases to `thread_for`.
### Solver patterns
`SchurRedBlack` (`Grid/algorithms/iterative/SchurRedBlack.h`) implements red-black (even/odd) preconditioning for fermion operators. Most production fermion solves use `SchurRedBlackDiagMooeeSolve` or similar wrappers that internally call a `ConjugateGradient` on the Schur complement.
Mixed-precision solvers (`ConjugateGradientMixedPrec`, `BiCGSTABMixedPrec`) drive a double-precision outer loop with single-precision inner solves.
### Memory and I/O
- `Grid/allocator/` — aligned/NUMA-aware allocators; caching allocator via `--enable-alloc-cache`
- `Grid/parallelIO/` — distributed parallel reader/writer for ILDG (via LIME), SciDAC, and native binary formats
- `Grid/serialisation/` — text, binary, HDF5, XML/JSON serialisation of arbitrary Grid objects
### Executables
- `HMC/` — production HMC driver programmes (e.g. `Mobius2p1f.cc`, `DWF_plus_DSDR_nf2plus1_Shamir_Gparity.cc`)
- `benchmarks/` — `Benchmark_dwf`, `Benchmark_ITT`, `Benchmark_comms`, `Benchmark_memory_bandwidth`, … used to qualify a new machine
- `examples/` — small, readable programmes (`Example_plaquette.cc`, `Example_Mobius_spectrum.cc`) that are the best starting point for learning the API
Each of these directories auto-builds every top-level `.cc` as its own binary via `scripts/filelist`.
Every programme is wrapped in `Grid_init(&argc, &argv)` / `Grid_finalize()` (`Grid/util/Init.h`).
## Key Conventions
- **C++17** is required throughout.
- Template structure: most classes are templated on `<_FImpl>` (fermion impl) or `<Gimpl>` (gauge impl), which encode the representation and precision. Instantiation is controlled by `--enable-fermion-instantiations`.
- **Tensor indices are positional, not labelled.** The `Grid/tensors/` arithmetic recurses structurally over the `iScalar`/`iVector`/`iMatrix` nest: each level defines only the {scalar,vector,matrix}² products at its own level, with element types resolved by automatic type deduction, so every colour/spin/lorentz combination composes from ~200 lines (versus the pre-C++11 QDP++/PETE approach of machine-generating every case). An index's meaning derives entirely from its nesting depth counted from the outside; `iScalar` is the identity/broadcast case at every level. Never insert or remove a nesting level casually — the multiplication tables contract by position.
- **Multigrid coarsening deepens the tensor nest by one level.** A coarse site vector is `iVector<CComplex,nbasis>`, and `innerProduct` on it returns `iScalar<CComplex>` — one level deeper than the fine block scalar. So the block-inner-product scalar type gains one `iScalar` wrapper per MG level (fine: `vTComplex`; level 2: `iScalar<vTComplex>`; see `examples/Example_pvdagm_3level.cc`). When calling `blockInnerProduct`/`blockZAXPY`/`blockOrthogonalise` on coarse fields, the coarse scalar type must match `decltype(innerProduct(siteVector(),siteVector()))` exactly; a wrong depth fails to compile (no viable `operator=` deep in the instantiation chain) rather than mis-contracting.
- **Grids are borrowed, never owned.** `conformable` is pointer identity, so every object that interoperates must hold the *same* `GridCartesian *`; a class that minted its own grid internally could never conform with anything else. Ownership is therefore not available, and lifetime is managed by scope discipline instead of reference counting: whoever creates a grid retains it beyond every object it handed a reference to. Anything *derived* from a grid inherits this — `~PaddedCell` dereferences its `unpadded_grid`, so a `PaddedCell` cannot even be **destroyed** after its parent grid, only used. Where a consumer must let go early, it offers an explicit hand-back (`MultiGeneralCoarsenedOperatorV2::ReleaseGrid()`) to be called *before* the grid is destroyed.
- The `RealD`/`RealF`/`ComplexD`/`ComplexF` typedefs are used everywhere; avoid raw `double`/`float`.
- Use `GRID_ASSERT(cond)` (defined in `Grid/GridStd.h`), not bare `assert` — it prints a Grid-formatted message and aborts cleanly under MPI.
- Logging is stream-based, not macro-based: `std::cout << GridLogMessage << ... << std::endl;`. Channels declared in `Grid/log/Log.h` include `GridLogError`, `GridLogWarning`, `GridLogDebug`, `GridLogPerformance`, `GridLogIterative`, `GridLogSolver`, `GridLogHMC`, `GridLogComms`, `GridLogMemory`, `GridLogDslash`, `GridLogIRL`, `GridLogMG`. A subset is switched on at runtime with e.g. `--log Error,Warning,Message,Performance,Iterative,Integrator,Debug,Colours` (names given without the `GridLog` prefix).
- Performance-critical paths use `GRID_TRACE(name)` from `Grid/perfmon/Tracing.h` (compiled out unless `--enable-tracing` selects a backend) and the `GridStopWatch` timers in `Grid/perfmon/Timer.h`.
- Reductions across MPI ranks go through `GridBase::GlobalSum` / `GlobalMax`; never reduce with bare MPI calls inside library code.
- Everything lives in `NAMESPACE_BEGIN(Grid)` / `NAMESPACE_END(Grid)` macros; follow the surrounding file rather than writing `namespace Grid { }`.
- British spelling is used in identifiers and comments (`colour`, `neighbour`, `serialisation`).
-2
View File
@@ -51,13 +51,11 @@ directory
#pragma nv_diag_suppress cast_to_qualified_type #pragma nv_diag_suppress cast_to_qualified_type
//disables nvcc specific warning in many files //disables nvcc specific warning in many files
#pragma nv_diag_suppress esa_on_defaulted_function_ignored #pragma nv_diag_suppress esa_on_defaulted_function_ignored
#pragma nv_diag_suppress declared_but_not_referenced
#pragma nv_diag_suppress extra_semicolon #pragma nv_diag_suppress extra_semicolon
#else #else
//disables nvcc specific warning in json.hpp //disables nvcc specific warning in json.hpp
#pragma diag_suppress unsigned_compare_with_zero #pragma diag_suppress unsigned_compare_with_zero
#pragma diag_suppress cast_to_qualified_type #pragma diag_suppress cast_to_qualified_type
#pragma diag_suppress declared_but_not_referenced
//disables nvcc specific warning in many files //disables nvcc specific warning in many files
#pragma diag_suppress esa_on_defaulted_function_ignored #pragma diag_suppress esa_on_defaulted_function_ignored
#pragma diag_suppress extra_semicolon #pragma diag_suppress extra_semicolon
+1 -2
View File
@@ -54,8 +54,7 @@ Author: paboyle <paboyle@ph.ed.ac.uk>
#include <Grid/serialisation/Serialisation.h> #include <Grid/serialisation/Serialisation.h>
#include <Grid/util/Sha.h> #include <Grid/util/Sha.h>
#include <Grid/communicator/Communicator.h> #include <Grid/communicator/Communicator.h>
#include <Grid/communicator/RingAllReduce.h> #include <Grid/cartesian/Cartesian.h>
#include <Grid/cartesian/Cartesian.h>
#include <Grid/tensors/Tensors.h> #include <Grid/tensors/Tensors.h>
#include <Grid/lattice/Lattice.h> #include <Grid/lattice/Lattice.h>
#include <Grid/cshift/Cshift.h> #include <Grid/cshift/Cshift.h>
+4 -39
View File
@@ -1,17 +1,9 @@
#ifndef GRID_STD_H #ifndef GRID_STD_H
#define GRID_STD_H #define GRID_STD_H
///////////////////
// Grid config
///////////////////
#include "Config.h"
/////////////////// ///////////////////
// Std C++ dependencies // Std C++ dependencies
/////////////////// ///////////////////
#define _NBACKTRACE (256)
extern void * Grid_backtrace_buffer[_NBACKTRACE];
#include <cassert> #include <cassert>
#include <complex> #include <complex>
#include <memory> #include <memory>
@@ -23,9 +15,7 @@ extern void * Grid_backtrace_buffer[_NBACKTRACE];
#include <random> #include <random>
#include <functional> #include <functional>
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <strings.h> #include <strings.h>
#include <stdio.h> #include <stdio.h>
#include <signal.h> #include <signal.h>
@@ -33,36 +23,11 @@ extern void * Grid_backtrace_buffer[_NBACKTRACE];
#include <sys/time.h> #include <sys/time.h>
#include <chrono> #include <chrono>
#include <zlib.h> #include <zlib.h>
#ifdef HAVE_EXECINFO_H
#include <execinfo.h>
#endif
void GridAbort(void);
#define ASSLOG(A) ::write(STDERR_FILENO,A,::strlen(A));
#ifdef HAVE_EXECINFO_H
#define GRID_ASSERT(b) if(!(b)) { \
fflush(stdout); \
ASSLOG(" GRID_ASSERT failure: "); \
ASSLOG(__FILE__); \
ASSLOG(" : "); \
ASSLOG(#b); \
ASSLOG(" : "); \
int symbols = backtrace(Grid_backtrace_buffer,_NBACKTRACE); \
backtrace_symbols_fd(Grid_backtrace_buffer,symbols,STDERR_FILENO); \
GridAbort(); \
};
#else
#define GRID_ASSERT(b) if(!(b)) { \
ASSLOG(" GRID_ASSERT failure: "); \
ASSLOG(__FILE__); \
ASSLOG(" : "); \
ASSLOG(#b); \
ASSLOG(" : "); \
GridAbort(); \
};
#endif
///////////////////
// Grid config
///////////////////
#include "Config.h"
#ifdef TOFU #ifdef TOFU
#undef GRID_COMMS_THREADS #undef GRID_COMMS_THREADS
+7 -11
View File
@@ -54,25 +54,21 @@ Version.h: version-cache
include Make.inc include Make.inc
include Eigen.inc include Eigen.inc
if BUILD_FERMION_INSTANTIATIONS extra_sources+=$(WILS_FERMION_FILES)
extra_sources+=$(WILS_FERMION_FILES) extra_sources+=$(STAG_FERMION_FILES)
extra_sources+=$(STAG_FERMION_FILES)
if BUILD_ZMOBIUS if BUILD_ZMOBIUS
extra_sources+=$(ZWILS_FERMION_FILES) extra_sources+=$(ZWILS_FERMION_FILES)
endif endif
if BUILD_GPARITY if BUILD_GPARITY
extra_sources+=$(GP_FERMION_FILES) extra_sources+=$(GP_FERMION_FILES)
endif endif
if BUILD_FERMION_REPS if BUILD_FERMION_REPS
extra_sources+=$(ADJ_FERMION_FILES) extra_sources+=$(ADJ_FERMION_FILES)
extra_sources+=$(TWOIND_FERMION_FILES) extra_sources+=$(TWOIND_FERMION_FILES)
endif endif
if BUILD_SP if BUILD_SP
extra_sources+=$(SP_FERMION_FILES) extra_sources+=$(SP_FERMION_FILES)
if BUILD_FERMION_REPS extra_sources+=$(SP_TWOIND_FERMION_FILES)
extra_sources+=$(SP_TWOIND_FERMION_FILES)
endif
endif
endif endif
lib_LIBRARIES = libGrid.a lib_LIBRARIES = libGrid.a
+1 -1
View File
@@ -29,8 +29,8 @@ directory
#pragma once #pragma once
#include <type_traits> #include <type_traits>
#include <exception>
#include <cassert> #include <cassert>
#include <exception>
#define NAMESPACE_BEGIN(A) namespace A { #define NAMESPACE_BEGIN(A) namespace A {
#define NAMESPACE_END(A) } #define NAMESPACE_END(A) }
+1 -2
View File
@@ -51,8 +51,6 @@ NAMESPACE_CHECK(approx);
#include <Grid/algorithms/deflation/MultiRHSBlockProject.h> #include <Grid/algorithms/deflation/MultiRHSBlockProject.h>
#include <Grid/algorithms/deflation/MultiRHSDeflation.h> #include <Grid/algorithms/deflation/MultiRHSDeflation.h>
#include <Grid/algorithms/deflation/MultiRHSBlockCGLinalg.h> #include <Grid/algorithms/deflation/MultiRHSBlockCGLinalg.h>
// Not really deflation, but useful
#include <Grid/algorithms/blas/MomentumProject.h>
NAMESPACE_CHECK(deflation); NAMESPACE_CHECK(deflation);
#include <Grid/algorithms/iterative/ConjugateGradient.h> #include <Grid/algorithms/iterative/ConjugateGradient.h>
NAMESPACE_CHECK(ConjGrad); NAMESPACE_CHECK(ConjGrad);
@@ -75,6 +73,7 @@ NAMESPACE_CHECK(BiCGSTAB);
#include <Grid/algorithms/iterative/FlexibleCommunicationAvoidingGeneralisedMinimalResidual.h> #include <Grid/algorithms/iterative/FlexibleCommunicationAvoidingGeneralisedMinimalResidual.h>
#include <Grid/algorithms/iterative/MixedPrecisionFlexibleGeneralisedMinimalResidual.h> #include <Grid/algorithms/iterative/MixedPrecisionFlexibleGeneralisedMinimalResidual.h>
#include <Grid/algorithms/iterative/ImplicitlyRestartedLanczos.h> #include <Grid/algorithms/iterative/ImplicitlyRestartedLanczos.h>
#include <Grid/algorithms/iterative/SimpleLanczos.h>
#include <Grid/algorithms/iterative/PowerMethod.h> #include <Grid/algorithms/iterative/PowerMethod.h>
#include <Grid/algorithms/iterative/AdefGeneric.h> #include <Grid/algorithms/iterative/AdefGeneric.h>
#include <Grid/algorithms/iterative/AdefMrhs.h> #include <Grid/algorithms/iterative/AdefMrhs.h>
+232 -424
View File
@@ -1,6 +1,6 @@
/************************************************************************************* /*************************************************************************************
Grid physics library, www.github.com/paboyle/Grid Grid physics library, www.github.com/paboyle/Grid
Source file: ./lib/Cshift.h Source file: ./lib/Cshift.h
@@ -28,15 +28,6 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
#ifndef _GRID_FFT_H_ #ifndef _GRID_FFT_H_
#define _GRID_FFT_H_ #define _GRID_FFT_H_
#ifdef GRID_CUDA
#include <cufft.h>
#endif
#ifdef GRID_HIP
#include <hipfft/hipfft.h>
#endif
#if !defined(GRID_CUDA) && !defined(GRID_HIP)
#ifdef HAVE_FFTW #ifdef HAVE_FFTW
#if defined(USE_MKL) || defined(GRID_SYCL) #if defined(USE_MKL) || defined(GRID_SYCL)
#include <fftw/fftw3.h> #include <fftw/fftw3.h>
@@ -44,449 +35,266 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
#include <fftw3.h> #include <fftw3.h>
#endif #endif
#endif #endif
#endif
NAMESPACE_BEGIN(Grid); NAMESPACE_BEGIN(Grid);
template<class scalar> struct FFTW { };
#ifdef HAVE_FFTW
template<> struct FFTW<ComplexD> {
public:
typedef fftw_complex FFTW_scalar;
typedef fftw_plan FFTW_plan;
static FFTW_plan fftw_plan_many_dft(int rank, const int *n,int howmany,
FFTW_scalar *in, const int *inembed,
int istride, int idist,
FFTW_scalar *out, const int *onembed,
int ostride, int odist,
int sign, unsigned flags) {
return ::fftw_plan_many_dft(rank,n,howmany,in,inembed,istride,idist,out,onembed,ostride,odist,sign,flags);
}
static void fftw_flops(const FFTW_plan p,double *add, double *mul, double *fmas){
::fftw_flops(p,add,mul,fmas);
}
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out) {
::fftw_execute_dft(p,in,out);
}
inline static void fftw_destroy_plan(const FFTW_plan p) {
::fftw_destroy_plan(p);
}
};
template<> struct FFTW<ComplexF> {
public:
typedef fftwf_complex FFTW_scalar;
typedef fftwf_plan FFTW_plan;
static FFTW_plan fftw_plan_many_dft(int rank, const int *n,int howmany,
FFTW_scalar *in, const int *inembed,
int istride, int idist,
FFTW_scalar *out, const int *onembed,
int ostride, int odist,
int sign, unsigned flags) {
return ::fftwf_plan_many_dft(rank,n,howmany,in,inembed,istride,idist,out,onembed,ostride,odist,sign,flags);
}
static void fftw_flops(const FFTW_plan p,double *add, double *mul, double *fmas){
::fftwf_flops(p,add,mul,fmas);
}
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out) {
::fftwf_execute_dft(p,in,out);
}
inline static void fftw_destroy_plan(const FFTW_plan p) {
::fftwf_destroy_plan(p);
}
};
#endif
#ifndef FFTW_FORWARD #ifndef FFTW_FORWARD
#define FFTW_FORWARD (-1) #define FFTW_FORWARD (-1)
#define FFTW_BACKWARD (+1) #define FFTW_BACKWARD (+1)
#define FFTW_ESTIMATE (0)
#endif #endif
template<class scalar> struct FFTW { class FFT {
}; private:
#ifdef GRID_HIP GridCartesian *vgrid;
template<> struct FFTW<ComplexD> { GridCartesian *sgrid;
int Nd;
double flops;
double flops_call;
uint64_t usec;
Coordinate dimensions;
Coordinate processors;
Coordinate processor_coor;
public: public:
static const int forward=FFTW_FORWARD; static const int forward=FFTW_FORWARD;
static const int backward=FFTW_BACKWARD; static const int backward=FFTW_BACKWARD;
typedef hipfftDoubleComplex FFTW_scalar;
typedef hipfftHandle FFTW_plan; double Flops(void) {return flops;}
static FFTW_plan fftw_plan_many_dft(int rank, int *n,int howmany, double MFlops(void) {return flops/usec;}
FFTW_scalar *in, int *inembed, double USec(void) {return (double)usec;}
int istride, int idist,
FFTW_scalar *out, int *onembed,
int ostride, int odist,
int sign, unsigned flags) {
FFTW_plan p;
auto rv = hipfftPlanMany(&p,rank,n,n,istride,idist,n,ostride,odist,HIPFFT_Z2Z,howmany);
GRID_ASSERT(rv==HIPFFT_SUCCESS);
return p;
}
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
hipfftResult rv;
if ( sign == forward ) rv =hipfftExecZ2Z(p,in,out,HIPFFT_FORWARD);
else rv =hipfftExecZ2Z(p,in,out,HIPFFT_BACKWARD);
accelerator_barrier();
GRID_ASSERT(rv==HIPFFT_SUCCESS);
}
inline static void fftw_destroy_plan(const FFTW_plan p) { hipfftDestroy(p); }
};
template<> struct FFTW<ComplexF> {
public:
static const int forward=FFTW_FORWARD;
static const int backward=FFTW_BACKWARD;
typedef hipfftComplex FFTW_scalar;
typedef hipfftHandle FFTW_plan;
static FFTW_plan fftw_plan_many_dft(int rank, int *n,int howmany,
FFTW_scalar *in, int *inembed,
int istride, int idist,
FFTW_scalar *out, int *onembed,
int ostride, int odist,
int sign, unsigned flags) {
FFTW_plan p;
auto rv = hipfftPlanMany(&p,rank,n,n,istride,idist,n,ostride,odist,HIPFFT_C2C,howmany);
GRID_ASSERT(rv==HIPFFT_SUCCESS);
return p;
}
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
hipfftResult rv;
if ( sign == forward ) rv =hipfftExecC2C(p,in,out,HIPFFT_FORWARD);
else rv =hipfftExecC2C(p,in,out,HIPFFT_BACKWARD);
accelerator_barrier();
GRID_ASSERT(rv==HIPFFT_SUCCESS);
}
inline static void fftw_destroy_plan(const FFTW_plan p) { hipfftDestroy(p); }
};
#endif
#ifdef GRID_CUDA FFT ( GridCartesian * grid ) :
template<> struct FFTW<ComplexD> { vgrid(grid),
public: Nd(grid->_ndimension),
static const int forward=FFTW_FORWARD; dimensions(grid->_fdimensions),
static const int backward=FFTW_BACKWARD; processors(grid->_processors),
typedef cufftDoubleComplex FFTW_scalar; processor_coor(grid->_processor_coor)
typedef cufftHandle FFTW_plan;
static FFTW_plan fftw_plan_many_dft(int rank, int *n,int howmany,
FFTW_scalar *in, int *inembed,
int istride, int idist,
FFTW_scalar *out, int *onembed,
int ostride, int odist,
int sign, unsigned flags) {
FFTW_plan p;
cufftPlanMany(&p,rank,n,n,istride,idist,n,ostride,odist,CUFFT_Z2Z,howmany);
return p;
}
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
if ( sign == forward ) cufftExecZ2Z(p,in,out,CUFFT_FORWARD);
else cufftExecZ2Z(p,in,out,CUFFT_INVERSE);
accelerator_barrier();
}
inline static void fftw_destroy_plan(const FFTW_plan p) { cufftDestroy(p); }
};
template<> struct FFTW<ComplexF> {
public:
static const int forward=FFTW_FORWARD;
static const int backward=FFTW_BACKWARD;
typedef cufftComplex FFTW_scalar;
typedef cufftHandle FFTW_plan;
static FFTW_plan fftw_plan_many_dft(int rank, int *n,int howmany,
FFTW_scalar *in, int *inembed,
int istride, int idist,
FFTW_scalar *out, int *onembed,
int ostride, int odist,
int sign, unsigned flags) {
FFTW_plan p;
cufftPlanMany(&p,rank,n,n,istride,idist,n,ostride,odist,CUFFT_C2C,howmany);
return p;
}
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
if ( sign == forward ) cufftExecC2C(p,in,out,CUFFT_FORWARD);
else cufftExecC2C(p,in,out,CUFFT_INVERSE);
accelerator_barrier();
}
inline static void fftw_destroy_plan(const FFTW_plan p) { cufftDestroy(p); }
};
#endif
#if !defined(GRID_CUDA) && !defined(GRID_HIP)
#ifdef HAVE_FFTW
template<> struct FFTW<ComplexD> {
public:
typedef fftw_complex FFTW_scalar;
typedef fftw_plan FFTW_plan;
static FFTW_plan fftw_plan_many_dft(int rank, int *n,int howmany,
FFTW_scalar *in, int *inembed,
int istride, int idist,
FFTW_scalar *out, int *onembed,
int ostride, int odist,
int sign, unsigned flags) {
return ::fftw_plan_many_dft(rank,n,howmany,in,inembed,istride,idist,out,onembed,ostride,odist,sign,flags);
}
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
::fftw_execute_dft(p,in,out);
}
inline static void fftw_destroy_plan(const FFTW_plan p) { ::fftw_destroy_plan(p); }
};
template<> struct FFTW<ComplexF> {
public:
typedef fftwf_complex FFTW_scalar;
typedef fftwf_plan FFTW_plan;
static FFTW_plan fftw_plan_many_dft(int rank, int *n,int howmany,
FFTW_scalar *in, int *inembed,
int istride, int idist,
FFTW_scalar *out, int *onembed,
int ostride, int odist,
int sign, unsigned flags) {
return ::fftwf_plan_many_dft(rank,n,howmany,in,inembed,istride,idist,out,onembed,ostride,odist,sign,flags);
}
inline static void fftw_execute_dft(const FFTW_plan p,FFTW_scalar *in,FFTW_scalar *out, int sign) {
::fftwf_execute_dft(p,in,out);
}
inline static void fftw_destroy_plan(const FFTW_plan p) { ::fftwf_destroy_plan(p); }
};
#endif
#endif
struct FFTbase {
double flops;
double flops_call;
uint64_t usec;
GridCartesian *_grid;
static const int forward = FFTW_FORWARD;
static const int backward = FFTW_BACKWARD;
double Flops(void) { return flops; }
double MFlops(void) { return flops / usec; }
double USec(void) { return (double)usec; }
FFTbase(GridCartesian *grid) : _grid(grid), flops(0), flops_call(0), usec(0) {}
};
// Barrel-shift gather, FFT execute, and insert. Called by both FFT and PlannedFFT.
// The caller is responsible for plan acquisition and destruction.
template<class vobj>
static void FFT_dim_execute(
Lattice<vobj> &result,
const Lattice<vobj> &source,
int dim, int sign,
typename FFTW<typename vobj::scalar_type>::FFTW_plan p,
GridCartesian *grid,
double &flops, double &flops_call, uint64_t &usec)
{
typedef typename vobj::scalar_type scalar;
typedef typename vobj::scalar_object sobj;
typedef typename vobj::scalar_type scalar_type;
typedef typename vobj::vector_type vector_type;
typedef typename FFTW<scalar>::FFTW_scalar FFTW_scalar;
const int Ndim = grid->Nd();
int L = grid->_ldimensions[dim];
int G = grid->_fdimensions[dim];
int Ncomp = sizeof(sobj) / sizeof(scalar);
int64_t Nlow = 1, Nhigh = 1;
for (int d = 0; d < dim; d++) Nlow *= grid->_ldimensions[d];
for (int d = dim+1; d < Ndim; d++) Nhigh *= grid->_ldimensions[d];
int64_t Nperp = Nlow * Nhigh;
deviceVector<scalar> pgbuf(Nperp * Ncomp * G);
scalar *pgbuf_v = &pgbuf[0];
int howmany = Ncomp * Nperp;
scalar div;
if (sign == FFTW_BACKWARD) div = 1.0 / G;
else if (sign == FFTW_FORWARD) div = 1.0;
else GRID_ASSERT(0);
double t_pencil = 0, t_fft = 0, t_copy = 0, t_shift = 0;
double t_total = -usecond();
result = source;
int pc = grid->_processor_coor[dim];
const Coordinate ldims = grid->_ldimensions;
const Coordinate rdims = grid->_rdimensions;
const Coordinate sdims = grid->_simd_layout;
const Coordinate processors = grid->_processors;
Coordinate pgdims(Ndim);
pgdims[0] = G;
for (int d = 0, dd = 1; d < Ndim; d++)
if (d != dim) pgdims[dd++] = ldims[d];
int64_t pgvol = 1;
for (int d = 0; d < Ndim; d++) pgvol *= pgdims[d];
const int Nsimd = vobj::Nsimd();
t_pencil = -usecond();
for (int p_idx = 0; p_idx < processors[dim]; p_idx++) {
t_copy -= usecond();
autoView(r_v, result, AcceleratorRead);
accelerator_for(idx, grid->oSites(), vobj::Nsimd(), {
#ifdef GRID_SIMT
{
int lane = acceleratorSIMTlane(Nsimd);
#else
for (int lane = 0; lane < Nsimd; lane++) {
#endif
Coordinate icoor, ocoor, pgcoor;
Lexicographic::CoorFromIndex(icoor, lane, sdims);
Lexicographic::CoorFromIndex(ocoor, idx, rdims);
pgcoor[0] = ocoor[dim] + icoor[dim]*rdims[dim] + ((pc+p_idx)%processors[dim])*L;
for (int d = 0, dd = 1; d < Ndim; d++)
if (d != dim) { pgcoor[dd] = ocoor[d] + icoor[d]*rdims[d]; dd++; }
int64_t pgidx;
Lexicographic::IndexFromCoor(pgcoor, pgidx, pgdims);
vector_type *from = (vector_type *)&r_v[idx];
scalar_type stmp;
for (int w = 0; w < Ncomp; w++) {
stmp = getlane(from[w], lane);
pgbuf_v[pgidx + w*pgvol] = stmp;
}
#ifdef GRID_SIMT
}
#else
}
#endif
});
t_copy += usecond();
if (p_idx != processors[dim] - 1) {
Lattice<vobj> temp(grid);
t_shift -= usecond();
temp = Cshift(result, dim, L); result = temp;
t_shift += usecond();
}
}
t_pencil += usecond();
FFTW_scalar *in = (FFTW_scalar *)pgbuf_v;
FFTW_scalar *out = (FFTW_scalar *)pgbuf_v;
t_fft = -usecond();
FFTW<scalar>::fftw_execute_dft(p, in, out, sign);
t_fft += usecond();
flops_call = 5.0 * howmany * G * log2(G);
usec = t_fft;
flops = flops_call;
result = Zero();
double t_insert = -usecond();
{ {
autoView(r_v, result, AcceleratorWrite); flops=0;
accelerator_for(idx, grid->oSites(), Nsimd, { usec =0;
#ifdef GRID_SIMT Coordinate layout(Nd,1);
{ sgrid = new GridCartesian(dimensions,layout,processors,*grid);
int lane = acceleratorSIMTlane(Nsimd); };
#else
for (int lane = 0; lane < Nsimd; lane++) { ~FFT ( void) {
#endif delete sgrid;
Coordinate icoor(Ndim), ocoor(Ndim), pgcoor(Ndim);
Lexicographic::CoorFromIndex(icoor, lane, sdims);
Lexicographic::CoorFromIndex(ocoor, idx, rdims);
pgcoor[0] = ocoor[dim] + icoor[dim]*rdims[dim] + pc*L;
for (int d = 0, dd = 1; d < Ndim; d++)
if (d != dim) { pgcoor[dd] = ocoor[d] + icoor[d]*rdims[d]; dd++; }
int64_t pgidx;
Lexicographic::IndexFromCoor(pgcoor, pgidx, pgdims);
vector_type *to = (vector_type *)&r_v[idx];
scalar_type stmp;
for (int w = 0; w < Ncomp; w++) {
stmp = pgbuf_v[pgidx + w*pgvol];
putlane(to[w], stmp, lane);
}
#ifdef GRID_SIMT
}
#else
}
#endif
});
} }
result = result * div;
t_insert += usecond();
t_total += usecond();
std::cout << GridLogPerformance << " FFT took " << t_total/1.0e6 << " s" << std::endl;
std::cout << GridLogPerformance << " FFT pencil " << t_pencil/1.0e6 << " s" << std::endl;
std::cout << GridLogPerformance << " of which copy " << t_copy/1.0e6 << " s" << std::endl;
std::cout << GridLogPerformance << " of which shift" << t_shift/1.0e6 << " s" << std::endl;
std::cout << GridLogPerformance << " FFT kernels " << t_fft/1.0e6 << " s" << std::endl;
std::cout << GridLogPerformance << " FFT insert " << t_insert/1.0e6 << " s" << std::endl;
}
class FFT : public FFTbase {
public:
FFT(GridCartesian *grid) : FFTbase(grid) {}
~FFT() {}
template<class vobj> template<class vobj>
void FFT_dim_mask(Lattice<vobj> &result, const Lattice<vobj> &source, Coordinate mask, int sign) { void FFT_dim_mask(Lattice<vobj> &result,const Lattice<vobj> &source,Coordinate mask,int sign){
const int Ndim = _grid->Nd();
Lattice<vobj> tmp = source; conformable(result.Grid(),vgrid);
for (int d = 0; d < Ndim; d++) { conformable(source.Grid(),vgrid);
if (mask[d]) { Lattice<vobj> tmp(vgrid);
FFT_dim(result, tmp, d, sign); tmp = source;
tmp = result; for(int d=0;d<Nd;d++){
if( mask[d] ) {
FFT_dim(result,tmp,d,sign);
tmp=result;
} }
} }
} }
template<class vobj> template<class vobj>
void FFT_all_dim(Lattice<vobj> &result, const Lattice<vobj> &source, int sign) { void FFT_all_dim(Lattice<vobj> &result,const Lattice<vobj> &source,int sign){
Coordinate mask(_grid->Nd(), 1); Coordinate mask(Nd,1);
FFT_dim_mask(result, source, mask, sign); FFT_dim_mask(result,source,mask,sign);
} }
template<class vobj>
void FFT_dim(Lattice<vobj> &result, const Lattice<vobj> &source, int dim, int sign) {
GRID_ASSERT(source.Grid() == _grid);
GRID_ASSERT(result.Grid() == _grid);
conformable(result.Grid(), source.Grid());
typedef typename vobj::scalar_type scalar; template<class vobj>
void FFT_dim(Lattice<vobj> &result,const Lattice<vobj> &source,int dim, int sign){
#ifndef HAVE_FFTW
std::cerr << "FFTW is not compiled but is called"<<std::endl;
assert(0);
#else
conformable(result.Grid(),vgrid);
conformable(source.Grid(),vgrid);
int L = vgrid->_ldimensions[dim];
int G = vgrid->_fdimensions[dim];
Coordinate layout(Nd,1);
Coordinate pencil_gd(vgrid->_fdimensions);
pencil_gd[dim] = G*processors[dim];
// Pencil global vol LxLxGxLxL per node
GridCartesian pencil_g(pencil_gd,layout,processors,*vgrid);
// Construct pencils
typedef typename vobj::scalar_object sobj; typedef typename vobj::scalar_object sobj;
typedef typename sobj::scalar_type scalar;
Lattice<sobj> pgbuf(&pencil_g);
autoView(pgbuf_v , pgbuf, CpuWrite);
//std::cout << "CPU view" << std::endl;
typedef typename FFTW<scalar>::FFTW_scalar FFTW_scalar; typedef typename FFTW<scalar>::FFTW_scalar FFTW_scalar;
typedef typename FFTW<scalar>::FFTW_plan FFTW_plan; typedef typename FFTW<scalar>::FFTW_plan FFTW_plan;
const int Ndim = _grid->Nd(); int Ncomp = sizeof(sobj)/sizeof(scalar);
int G = _grid->_fdimensions[dim]; int Nlow = 1;
int Ncomp = sizeof(sobj) / sizeof(scalar); for(int d=0;d<dim;d++){
int64_t Nperp = 1; Nlow*=vgrid->_ldimensions[d];
for (int d = 0; d < Ndim; d++)
if (d != dim) Nperp *= _grid->_ldimensions[d];
int n[] = {G};
int howmany = Ncomp * Nperp;
deviceVector<scalar> dummy(2);
FFTW_scalar *buf = (FFTW_scalar *)&dummy[0];
FFTW_plan p = FFTW<scalar>::fftw_plan_many_dft(1, n, howmany,
buf, n, 1, G,
buf, n, 1, G,
sign, FFTW_ESTIMATE);
FFT_dim_execute(result, source, dim, sign, p, _grid, flops, flops_call, usec);
FFTW<scalar>::fftw_destroy_plan(p);
}
};
template<class vobj>
class PlannedFFT : public FFTbase {
private:
typedef typename vobj::scalar_type scalar;
typedef typename vobj::scalar_object sobj;
typedef typename vobj::vector_type vector_type;
typedef typename FFTW<scalar>::FFTW_scalar FFTW_scalar;
typedef typename FFTW<scalar>::FFTW_plan FFTW_plan;
std::vector<FFTW_plan> forward_plans;
std::vector<FFTW_plan> backward_plans;
void PlanCreate() {
const int Ndim = _grid->Nd();
forward_plans.resize(Ndim);
backward_plans.resize(Ndim);
for (int d = 0; d < Ndim; d++) {
int G = _grid->_fdimensions[d];
int Ncomp = sizeof(sobj) / sizeof(scalar);
int64_t Nperp = 1;
for (int dd = 0; dd < Ndim; dd++)
if (dd != d) Nperp *= _grid->_ldimensions[dd];
int howmany = Ncomp * (int)Nperp;
int n[] = {G};
deviceVector<scalar> dummy(2);
FFTW_scalar *buf = (FFTW_scalar *)&dummy[0];
forward_plans[d] = FFTW<scalar>::fftw_plan_many_dft(1, n, howmany, buf, n, 1, G, buf, n, 1, G, FFTW_FORWARD, FFTW_ESTIMATE);
backward_plans[d] = FFTW<scalar>::fftw_plan_many_dft(1, n, howmany, buf, n, 1, G, buf, n, 1, G, FFTW_BACKWARD, FFTW_ESTIMATE);
} }
}
int rank = 1; /* 1d transforms */
void PlanDestroy() { int n[] = {G}; /* 1d transforms of length G */
for (auto p : forward_plans) FFTW<scalar>::fftw_destroy_plan(p); int howmany = Ncomp;
for (auto p : backward_plans) FFTW<scalar>::fftw_destroy_plan(p); int odist,idist,istride,ostride;
forward_plans.clear(); idist = odist = 1; /* Distance between consecutive FT's */
backward_plans.clear(); istride = ostride = Ncomp*Nlow; /* distance between two elements in the same FT */
} int *inembed = n, *onembed = n;
public: scalar div;
PlannedFFT(GridCartesian *grid) : FFTbase(grid) { PlanCreate(); } if ( sign == backward ) div = 1.0/G;
~PlannedFFT() { PlanDestroy(); } else if ( sign == forward ) div = 1.0;
else assert(0);
void FFT_dim_mask(Lattice<vobj> &result, const Lattice<vobj> &source, Coordinate mask, int sign) {
const int Ndim = _grid->Nd(); //std::cout << GridLogPerformance<<"Making FFTW plan" << std::endl;
Lattice<vobj> tmp = source; FFTW_plan p;
for (int d = 0; d < Ndim; d++) { {
if (mask[d]) { FFTW_scalar *in = (FFTW_scalar *)&pgbuf_v[0];
FFT_dim(result, tmp, d, sign); FFTW_scalar *out= (FFTW_scalar *)&pgbuf_v[0];
tmp = result; p = FFTW<scalar>::fftw_plan_many_dft(rank,n,howmany,
in,inembed,
istride,idist,
out,onembed,
ostride, odist,
sign,FFTW_ESTIMATE);
}
// Barrel shift and collect global pencil
//std::cout << GridLogPerformance<<"Making pencil" << std::endl;
Coordinate lcoor(Nd), gcoor(Nd);
result = source;
int pc = processor_coor[dim];
for(int p=0;p<processors[dim];p++) {
{
autoView(r_v,result,CpuRead);
autoView(p_v,pgbuf,CpuWrite);
thread_for(idx, sgrid->lSites(),{
Coordinate cbuf(Nd);
sobj s;
sgrid->LocalIndexToLocalCoor(idx,cbuf);
peekLocalSite(s,r_v,cbuf);
cbuf[dim]+=((pc+p) % processors[dim])*L;
pokeLocalSite(s,p_v,cbuf);
});
}
if (p != processors[dim] - 1) {
result = Cshift(result,dim,L);
} }
} }
}
//std::cout <<GridLogPerformance<< "Looping orthog" << std::endl;
void FFT_all_dim(Lattice<vobj> &result, const Lattice<vobj> &source, int sign) { // Loop over orthog coords
Coordinate mask(_grid->Nd(), 1); int NN=pencil_g.lSites();
FFT_dim_mask(result, source, mask, sign); GridStopWatch timer;
} timer.Start();
thread_for( idx,NN,{
void FFT_dim(Lattice<vobj> &result, const Lattice<vobj> &source, int dim, int sign) { Coordinate cbuf(Nd);
GRID_ASSERT(source.Grid() == _grid); pencil_g.LocalIndexToLocalCoor(idx, cbuf);
GRID_ASSERT(result.Grid() == _grid); if ( cbuf[dim] == 0 ) { // restricts loop to plane at lcoor[dim]==0
GRID_ASSERT((int)forward_plans.size() == _grid->Nd()); FFTW_scalar *in = (FFTW_scalar *)&pgbuf_v[idx];
conformable(result.Grid(), source.Grid()); FFTW_scalar *out= (FFTW_scalar *)&pgbuf_v[idx];
FFTW_plan p = (sign == forward ? forward_plans : backward_plans)[dim]; FFTW<scalar>::fftw_execute_dft(p,in,out);
FFT_dim_execute(result, source, dim, sign, p, _grid, flops, flops_call, usec); }
});
timer.Stop();
// performance counting
double add,mul,fma;
FFTW<scalar>::fftw_flops(p,&add,&mul,&fma);
flops_call = add+mul+2.0*fma;
usec += timer.useconds();
flops+= flops_call*NN;
//std::cout <<GridLogPerformance<< "Writing back results " << std::endl;
// writing out result
{
autoView(pgbuf_v,pgbuf,CpuRead);
autoView(result_v,result,CpuWrite);
thread_for(idx,sgrid->lSites(),{
Coordinate clbuf(Nd), cgbuf(Nd);
sobj s;
sgrid->LocalIndexToLocalCoor(idx,clbuf);
cgbuf = clbuf;
cgbuf[dim] = clbuf[dim]+L*pc;
peekLocalSite(s,pgbuf_v,cgbuf);
pokeLocalSite(s,result_v,clbuf);
});
}
result = result*div;
//std::cout <<GridLogPerformance<< "Destroying plan " << std::endl;
// destroying plan
FFTW<scalar>::fftw_destroy_plan(p);
#endif
} }
}; };
+25 -25
View File
@@ -64,7 +64,7 @@ public:
// //
// I'm not entirely happy with implementation; to share the Schur code between herm and non-herm // I'm not entirely happy with implementation; to share the Schur code between herm and non-herm
// while still having a "OpAndNorm" in the abstract base I had to implement it in both cases // while still having a "OpAndNorm" in the abstract base I had to implement it in both cases
// with an GRID_ASSERT trap in the non-herm. This isn't right; there must be a better C++ way to // with an assert trap in the non-herm. This isn't right; there must be a better C++ way to
// do it, but I fear it required multiple inheritance and mixed in abstract base classes // do it, but I fear it required multiple inheritance and mixed in abstract base classes
///////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////
@@ -148,22 +148,22 @@ public:
// Support for coarsening to a multigrid // Support for coarsening to a multigrid
void OpDiag (const Field &in, Field &out) { void OpDiag (const Field &in, Field &out) {
_Mat.Mdiag(in,out); _Mat.Mdiag(in,out);
GRID_ASSERT(0); assert(0);
} }
void OpDir (const Field &in, Field &out,int dir,int disp) { void OpDir (const Field &in, Field &out,int dir,int disp) {
_Mat.Mdir(in,out,dir,disp); _Mat.Mdir(in,out,dir,disp);
GRID_ASSERT(0); assert(0);
} }
void OpDirAll (const Field &in, std::vector<Field> &out){ void OpDirAll (const Field &in, std::vector<Field> &out){
GRID_ASSERT(0); assert(0);
}; };
void Op (const Field &in, Field &out){ void Op (const Field &in, Field &out){
_Mat.M(in,out); _Mat.M(in,out);
GRID_ASSERT(0); assert(0);
} }
void AdjOp (const Field &in, Field &out){ void AdjOp (const Field &in, Field &out){
_Mat.Mdag(in,out); _Mat.Mdag(in,out);
GRID_ASSERT(0); assert(0);
} }
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){
HermOp(in,out); HermOp(in,out);
@@ -188,13 +188,13 @@ public:
ShiftedHermOpLinearOperator(LinearOperatorBase<Field> &Mat,RealD shift): _Mat(Mat), _shift(shift){}; ShiftedHermOpLinearOperator(LinearOperatorBase<Field> &Mat,RealD shift): _Mat(Mat), _shift(shift){};
// Support for coarsening to a multigrid // Support for coarsening to a multigrid
void OpDiag (const Field &in, Field &out) { void OpDiag (const Field &in, Field &out) {
GRID_ASSERT(0); assert(0);
} }
void OpDir (const Field &in, Field &out,int dir,int disp) { void OpDir (const Field &in, Field &out,int dir,int disp) {
GRID_ASSERT(0); assert(0);
} }
void OpDirAll (const Field &in, std::vector<Field> &out){ void OpDirAll (const Field &in, std::vector<Field> &out){
GRID_ASSERT(0); assert(0);
}; };
void Op (const Field &in, Field &out){ void Op (const Field &in, Field &out){
HermOp(in,out); HermOp(in,out);
@@ -271,10 +271,10 @@ public:
_Mat.Mdag(in,out); _Mat.Mdag(in,out);
} }
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){
GRID_ASSERT(0); assert(0);
} }
void HermOp(const Field &in, Field &out){ void HermOp(const Field &in, Field &out){
GRID_ASSERT(0); assert(0);
} }
}; };
template<class Matrix,class Field> template<class Matrix,class Field>
@@ -303,10 +303,10 @@ public:
out = out + shift * in; out = out + shift * in;
} }
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){
GRID_ASSERT(0); assert(0);
} }
void HermOp(const Field &in, Field &out){ void HermOp(const Field &in, Field &out){
GRID_ASSERT(0); assert(0);
} }
}; };
@@ -345,13 +345,13 @@ class SchurOperatorBase : public LinearOperatorBase<Field> {
} }
// Support for coarsening to a multigrid // Support for coarsening to a multigrid
void OpDiag (const Field &in, Field &out) { void OpDiag (const Field &in, Field &out) {
GRID_ASSERT(0); // must coarsen the unpreconditioned system assert(0); // must coarsen the unpreconditioned system
} }
void OpDir (const Field &in, Field &out,int dir,int disp) { void OpDir (const Field &in, Field &out,int dir,int disp) {
GRID_ASSERT(0); assert(0);
} }
void OpDirAll (const Field &in, std::vector<Field> &out){ void OpDirAll (const Field &in, std::vector<Field> &out){
GRID_ASSERT(0); assert(0);
}; };
}; };
template<class Matrix,class Field> template<class Matrix,class Field>
@@ -447,10 +447,10 @@ class NonHermitianSchurOperatorBase : public LinearOperatorBase<Field>
MpcDag(tmp,out); MpcDag(tmp,out);
} }
virtual void HermOpAndNorm(const Field& in, Field& out, RealD& n1, RealD& n2) { virtual void HermOpAndNorm(const Field& in, Field& out, RealD& n1, RealD& n2) {
GRID_ASSERT(0); assert(0);
} }
virtual void HermOp(const Field& in, Field& out) { virtual void HermOp(const Field& in, Field& out) {
GRID_ASSERT(0); assert(0);
} }
void Op(const Field& in, Field& out) { void Op(const Field& in, Field& out) {
Mpc(in, out); Mpc(in, out);
@@ -460,13 +460,13 @@ class NonHermitianSchurOperatorBase : public LinearOperatorBase<Field>
} }
// Support for coarsening to a multigrid // Support for coarsening to a multigrid
void OpDiag(const Field& in, Field& out) { void OpDiag(const Field& in, Field& out) {
GRID_ASSERT(0); // must coarsen the unpreconditioned system assert(0); // must coarsen the unpreconditioned system
} }
void OpDir(const Field& in, Field& out, int dir, int disp) { void OpDir(const Field& in, Field& out, int dir, int disp) {
GRID_ASSERT(0); assert(0);
} }
void OpDirAll(const Field& in, std::vector<Field>& out){ void OpDirAll(const Field& in, std::vector<Field>& out){
GRID_ASSERT(0); assert(0);
}; };
}; };
@@ -580,7 +580,7 @@ class SchurStaggeredOperator : public SchurOperatorBase<Field> {
public: public:
SchurStaggeredOperator (Matrix &Mat): _Mat(Mat), tmp(_Mat.RedBlackGrid()) SchurStaggeredOperator (Matrix &Mat): _Mat(Mat), tmp(_Mat.RedBlackGrid())
{ {
GRID_ASSERT( _Mat.isTrivialEE() ); assert( _Mat.isTrivialEE() );
mass = _Mat.Mass(); mass = _Mat.Mass();
} }
virtual void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){ virtual void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2){
@@ -611,7 +611,7 @@ class SchurStaggeredOperator : public SchurOperatorBase<Field> {
Mpc(in,out); Mpc(in,out);
} }
virtual void MpcDagMpc(const Field &in, Field &out) { virtual void MpcDagMpc(const Field &in, Field &out) {
GRID_ASSERT(0);// Never need with staggered assert(0);// Never need with staggered
} }
}; };
template<class Matrix,class Field> using SchurStagOperator = SchurStaggeredOperator<Matrix,Field>; template<class Matrix,class Field> using SchurStagOperator = SchurStaggeredOperator<Matrix,Field>;
@@ -623,7 +623,7 @@ template<class Field> class OperatorFunction {
public: public:
virtual void operator() (LinearOperatorBase<Field> &Linop, const Field &in, Field &out) = 0; virtual void operator() (LinearOperatorBase<Field> &Linop, const Field &in, Field &out) = 0;
virtual void operator() (LinearOperatorBase<Field> &Linop, const std::vector<Field> &in,std::vector<Field> &out) { virtual void operator() (LinearOperatorBase<Field> &Linop, const std::vector<Field> &in,std::vector<Field> &out) {
GRID_ASSERT(in.size()==out.size()); assert(in.size()==out.size());
for(int k=0;k<in.size();k++){ for(int k=0;k<in.size();k++){
(*this)(Linop,in[k],out[k]); (*this)(Linop,in[k],out[k]);
} }
@@ -637,7 +637,7 @@ public:
virtual void operator() (const std::vector<Field> &in, std::vector<Field> &out) virtual void operator() (const std::vector<Field> &in, std::vector<Field> &out)
{ {
GRID_ASSERT(in.size() == out.size()); assert(in.size() == out.size());
for (unsigned int i = 0; i < in.size(); ++i) for (unsigned int i = 0; i < in.size(); ++i)
{ {
-2
View File
@@ -269,9 +269,7 @@ public:
RealD xscale = 2.0/(hi-lo); RealD xscale = 2.0/(hi-lo);
RealD mscale = -(hi+lo)/(hi-lo); RealD mscale = -(hi+lo)/(hi-lo);
Linop.HermOp(T0,y); Linop.HermOp(T0,y);
grid->Barrier();
axpby(T1,xscale,mscale,y,in); axpby(T1,xscale,mscale,y,in);
grid->Barrier();
// sum = .5 c[0] T0 + c[1] T1 // sum = .5 c[0] T0 + c[1] T1
// out = ()*T0 + Coeffs[1]*T1; // out = ()*T0 + Coeffs[1]*T1;
+3 -3
View File
@@ -121,7 +121,7 @@ double AlgRemez::generateApprox(int num_degree, int den_degree,
// Reallocate arrays, since degree has changed // Reallocate arrays, since degree has changed
if (num_degree != n || den_degree != d) allocate(num_degree,den_degree); if (num_degree != n || den_degree != d) allocate(num_degree,den_degree);
GRID_ASSERT(a_len<=SUM_MAX); assert(a_len<=SUM_MAX);
step = new bigfloat[num_degree+den_degree+2]; step = new bigfloat[num_degree+den_degree+2];
@@ -151,9 +151,9 @@ double AlgRemez::generateApprox(int num_degree, int den_degree,
equations(); equations();
if (delta < tolerance) { if (delta < tolerance) {
std::cout<<"Delta too small, try increasing precision\n"; std::cout<<"Delta too small, try increasing precision\n";
GRID_ASSERT(0); assert(0);
}; };
GRID_ASSERT( delta>= tolerance); assert( delta>= tolerance);
search(step); search(step);
} }
+1 -1
View File
@@ -134,7 +134,7 @@ class AlgRemez
virtual ~AlgRemez(); virtual ~AlgRemez();
int getDegree(void){ int getDegree(void){
GRID_ASSERT(n==d); assert(n==d);
return n; return n;
} }
// Reset the bounds of the approximation // Reset the bounds of the approximation
+8 -8
View File
@@ -28,11 +28,11 @@ void AlgRemezGeneral::setupPolyProperties(int num_degree, int den_degree, PolyTy
pow_n = num_degree; pow_n = num_degree;
pow_d = den_degree; pow_d = den_degree;
if(pow_n % 2 == 0 && num_type_in == PolyType::Odd) GRID_ASSERT(0); if(pow_n % 2 == 0 && num_type_in == PolyType::Odd) assert(0);
if(pow_n % 2 == 1 && num_type_in == PolyType::Even) GRID_ASSERT(0); if(pow_n % 2 == 1 && num_type_in == PolyType::Even) assert(0);
if(pow_d % 2 == 0 && den_type_in == PolyType::Odd) GRID_ASSERT(0); if(pow_d % 2 == 0 && den_type_in == PolyType::Odd) assert(0);
if(pow_d % 2 == 1 && den_type_in == PolyType::Even) GRID_ASSERT(0); if(pow_d % 2 == 1 && den_type_in == PolyType::Even) assert(0);
num_type = num_type_in; num_type = num_type_in;
den_type = den_type_in; den_type = den_type_in;
@@ -112,9 +112,9 @@ double AlgRemezGeneral::generateApprox(const int num_degree, const int den_degre
equations(); equations();
if (delta < tolerance) { if (delta < tolerance) {
std::cout<<"Iteration " << iter-1 << " delta too small (" << delta << "<" << tolerance << "), try increasing precision\n"; std::cout<<"Iteration " << iter-1 << " delta too small (" << delta << "<" << tolerance << "), try increasing precision\n";
GRID_ASSERT(0); assert(0);
}; };
GRID_ASSERT( delta>= tolerance ); assert( delta>= tolerance );
search(); search();
} }
@@ -278,7 +278,7 @@ void AlgRemezGeneral::equations(){
if(num_pows[j] != -1){ *aa++ = z; t++; } if(num_pows[j] != -1){ *aa++ = z; t++; }
z *= x; z *= x;
} }
GRID_ASSERT(t == n+1); assert(t == n+1);
z = (bigfloat)1l; z = (bigfloat)1l;
t = 0; t = 0;
@@ -286,7 +286,7 @@ void AlgRemezGeneral::equations(){
if(den_pows[j] != -1){ *aa++ = -y * z; t++; } if(den_pows[j] != -1){ *aa++ = -y * z; t++; }
z *= x; z *= x;
} }
GRID_ASSERT(t == d); assert(t == d);
B[i] = y * z; // Right hand side vector B[i] = y * z; // Right hand side vector
} }
+1 -1
View File
@@ -106,7 +106,7 @@ class AlgRemezGeneral{
bigfloat (*f)(bigfloat x, void *data), void *data); bigfloat (*f)(bigfloat x, void *data), void *data);
inline int getDegree(void) const{ inline int getDegree(void) const{
GRID_ASSERT(n==d); assert(n==d);
return n; return n;
} }
// Reset the bounds of the approximation // Reset the bounds of the approximation
+1 -1
View File
@@ -74,7 +74,7 @@ bigfloat epsilonMobius(bigfloat x, void* data){
void computeZmobiusOmega(std::vector<ComplexD> &omega_out, const int Ls_out, void computeZmobiusOmega(std::vector<ComplexD> &omega_out, const int Ls_out,
const std::vector<RealD> &omega_in, const int Ls_in, const std::vector<RealD> &omega_in, const int Ls_in,
const RealD lambda_bound){ const RealD lambda_bound){
GRID_ASSERT(omega_in.size() == Ls_in); assert(omega_in.size() == Ls_in);
omega_out.resize(Ls_out); omega_out.resize(Ls_out);
//Use the Remez algorithm to generate the appropriate rational polynomial //Use the Remez algorithm to generate the appropriate rational polynomial
File diff suppressed because it is too large. Load diff
-282
View File
@@ -1,282 +0,0 @@
/*************************************************************************************
Grid physics library, www.github.com/paboyle/Grid
Source file: BatchedInverse.h
Copyright (C) 2026
Author: Peter Boyle <pboyle@bnl.gov>
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 */
#pragma once
#include <Grid/algorithms/blas/BatchedBlas.h>
#ifdef GRID_HIP
#include <rocsolver/rocsolver.h>
#endif
// GRID_CUDA: batched LU inversion lives in cuBLAS (getrfBatched/getriBatched);
// cublas_v2.h already included via BatchedBlas.h.
// GRID_SYCL: oneapi/mkl.hpp already included via BatchedBlas.h (lapack::getrf/getri).
NAMESPACE_BEGIN(Grid);
///////////////////////////////////////////////////////////////////////////////
// GridBLASInverse: cross-platform batched dense matrix inversion.
//
// HIGH LEVEL contract (deliberately NOT a getrf/getrs interface): invert a
// batch of dense N x N matrices IN PLACE,
//
// A[i] <- A[i]^{-1} i = 0 .. batchCount-1
//
// Layout: column major, lda = N, contiguous per batch element; pointer list
// exactly as GridBLAS::gemmBatched (deviceVector<T*> of device pointers).
// Each backend chooses HOW:
// HIP : rocSOLVER getrf_batched + getri_batched
// CUDA : cuBLAS getrfBatched + getriBatched (out-of-place getri; workspace
// hidden here, result copied back so the surface stays in-place)
// SYCL : oneMKL LAPACK getrf + getri per batch element (USM, in-order queue)
// CPU : Eigen PartialPivLU (the correctness oracle for all of the above)
//
// The int32 vendor-batched entry points bound N < 2^31 (asserted); the huge
// single-matrix ILP64 path (getrf_64 + blocked identity-getrs harvest, proven
// in the dense coarse-coarse setup at N=69120) migrates here as a batch==1
// large-N dispatch in a follow-up -- the recursive Schur leaves are the
// batched consumers this surface is shaped for.
//
// NB GPU-backend call signatures are written to vendor documentation but the
// air-gapped development loop compiles only the CPU/Eigen path; verify the
// rocSOLVER/cuBLAS/oneMKL calls against headers on first device compile.
// Semantics are locked by the CPU unit test (Test_batched_blas).
///////////////////////////////////////////////////////////////////////////////
class GridBLASInverse {
public:
#ifdef GRID_HIP
// rocSOLVER runs on a rocblas_handle (distinct type from hipblasHandle_t)
static rocblas_handle & Handle(void) {
static rocblas_handle h;
static int init = 0;
if ( !init ) {
auto st = rocblas_create_handle(&h);
GRID_ASSERT(st == rocblas_status_success);
init = 1;
}
return h;
}
#endif
#ifdef GRID_CUDA
// cuBLAS batched LU shares the GridBLAS handle
static cublasHandle_t & Handle(void) {
GridBLAS::Init();
return GridBLAS::gridblasHandle;
}
#endif
#ifdef GRID_SYCL
static sycl::queue * & Handle(void) {
GridBLAS::Init();
return GridBLAS::gridblasHandle;
}
#endif
GridBLASInverse() {};
~GridBLASInverse() {};
void inverseBatched(int64_t N, deviceVector<ComplexF*> &Amat)
{
int32_t batchCount = Amat.size();
GRID_ASSERT(batchCount > 0);
#ifdef GRID_HIP
GRID_ASSERT( N < 2147483647L );
rocblas_int n = (rocblas_int)N;
rocblas_int lda = (rocblas_int)N;
deviceVector<rocblas_int> ipiv((uint64_t)batchCount*N);
deviceVector<rocblas_int> info(batchCount);
auto st1 = rocsolver_cgetrf_batched(Handle(), n, n,
(rocblas_float_complex *const *)&Amat[0], lda,
&ipiv[0], (rocblas_stride)N,
&info[0], batchCount);
GRID_ASSERT(st1 == rocblas_status_success);
auto st2 = rocsolver_cgetri_batched(Handle(), n,
(rocblas_float_complex *const *)&Amat[0], lda,
&ipiv[0], (rocblas_stride)N,
&info[0], batchCount);
GRID_ASSERT(st2 == rocblas_status_success);
accelerator_barrier();
std::vector<rocblas_int> info_h(batchCount);
acceleratorCopyFromDevice(&info[0],&info_h[0],batchCount*sizeof(rocblas_int));
for(int i=0;i<batchCount;i++) GRID_ASSERT(info_h[i]==0); // singular pivot => abort loudly
#endif
#ifdef GRID_CUDA
GRID_ASSERT( N < 2147483647L );
int n = (int)N;
deviceVector<int> ipiv((uint64_t)batchCount*N);
deviceVector<int> info(batchCount);
auto st1 = cublasCgetrfBatched(Handle(), n,
(cuComplex **)&Amat[0], n,
&ipiv[0], &info[0], batchCount);
GRID_ASSERT(st1 == CUBLAS_STATUS_SUCCESS);
// getri is OUT of place: hidden workspace keeps the surface in-place
deviceVector<ComplexF> work((uint64_t)batchCount*N*N);
deviceVector<ComplexF*> Cptr(batchCount);
std::vector<ComplexF*> Cptr_h(batchCount);
std::vector<ComplexF*> Aptr_h(batchCount);
for(int i=0;i<batchCount;i++) Cptr_h[i] = &work[(uint64_t)i*N*N];
acceleratorCopyToDevice(&Cptr_h[0],&Cptr[0],batchCount*sizeof(ComplexF*));
acceleratorCopyFromDevice(&Amat[0],&Aptr_h[0],batchCount*sizeof(ComplexF*));
auto st2 = cublasCgetriBatched(Handle(), n,
(const cuComplex *const *)&Amat[0], n,
&ipiv[0],
(cuComplex **)&Cptr[0], n,
&info[0], batchCount);
GRID_ASSERT(st2 == CUBLAS_STATUS_SUCCESS);
accelerator_barrier();
std::vector<int> info_h(batchCount);
acceleratorCopyFromDevice(&info[0],&info_h[0],batchCount*sizeof(int));
for(int i=0;i<batchCount;i++) GRID_ASSERT(info_h[i]==0);
for(int i=0;i<batchCount;i++)
acceleratorCopyDeviceToDevice(Cptr_h[i],Aptr_h[i],(uint64_t)N*N*sizeof(ComplexF));
#endif
#ifdef GRID_SYCL
// Per-element oneMKL LAPACK on the in-order queue; group API optimisation later.
sycl::queue *q = Handle();
std::vector<ComplexF*> Aptr_h(batchCount);
acceleratorCopyFromDevice(&Amat[0],&Aptr_h[0],batchCount*sizeof(ComplexF*));
int64_t lwf = oneapi::mkl::lapack::getrf_scratchpad_size<std::complex<float> >(*q,N,N,N);
int64_t lwi = oneapi::mkl::lapack::getri_scratchpad_size<std::complex<float> >(*q,N,N);
deviceVector<ComplexF> scratchf(lwf);
deviceVector<ComplexF> scratchi(lwi);
deviceVector<int64_t> ipiv(N);
for(int i=0;i<batchCount;i++){
oneapi::mkl::lapack::getrf(*q,N,N,(std::complex<float>*)Aptr_h[i],N,&ipiv[0],
(std::complex<float>*)&scratchf[0],lwf);
oneapi::mkl::lapack::getri(*q,N, (std::complex<float>*)Aptr_h[i],N,&ipiv[0],
(std::complex<float>*)&scratchi[0],lwi);
}
q->wait();
#endif
#if !defined(GRID_SYCL) && !defined(GRID_CUDA) && !defined(GRID_HIP)
// Reference implementation; the oracle the unit test locks semantics with.
thread_for (p, batchCount, {
Eigen::Map<Eigen::MatrixXcf> eA(Amat[p],N,N);
Eigen::PartialPivLU<Eigen::MatrixXcf> lu(eA);
eA = lu.inverse();
});
#endif
}
void inverseBatched(int64_t N, deviceVector<ComplexD*> &Amat)
{
int32_t batchCount = Amat.size();
GRID_ASSERT(batchCount > 0);
#ifdef GRID_HIP
GRID_ASSERT( N < 2147483647L );
rocblas_int n = (rocblas_int)N;
rocblas_int lda = (rocblas_int)N;
deviceVector<rocblas_int> ipiv((uint64_t)batchCount*N);
deviceVector<rocblas_int> info(batchCount);
auto st1 = rocsolver_zgetrf_batched(Handle(), n, n,
(rocblas_double_complex *const *)&Amat[0], lda,
&ipiv[0], (rocblas_stride)N,
&info[0], batchCount);
GRID_ASSERT(st1 == rocblas_status_success);
auto st2 = rocsolver_zgetri_batched(Handle(), n,
(rocblas_double_complex *const *)&Amat[0], lda,
&ipiv[0], (rocblas_stride)N,
&info[0], batchCount);
GRID_ASSERT(st2 == rocblas_status_success);
accelerator_barrier();
std::vector<rocblas_int> info_h(batchCount);
acceleratorCopyFromDevice(&info[0],&info_h[0],batchCount*sizeof(rocblas_int));
for(int i=0;i<batchCount;i++) GRID_ASSERT(info_h[i]==0);
#endif
#ifdef GRID_CUDA
GRID_ASSERT( N < 2147483647L );
int n = (int)N;
deviceVector<int> ipiv((uint64_t)batchCount*N);
deviceVector<int> info(batchCount);
auto st1 = cublasZgetrfBatched(Handle(), n,
(cuDoubleComplex **)&Amat[0], n,
&ipiv[0], &info[0], batchCount);
GRID_ASSERT(st1 == CUBLAS_STATUS_SUCCESS);
deviceVector<ComplexD> work((uint64_t)batchCount*N*N);
deviceVector<ComplexD*> Cptr(batchCount);
std::vector<ComplexD*> Cptr_h(batchCount);
std::vector<ComplexD*> Aptr_h(batchCount);
for(int i=0;i<batchCount;i++) Cptr_h[i] = &work[(uint64_t)i*N*N];
acceleratorCopyToDevice(&Cptr_h[0],&Cptr[0],batchCount*sizeof(ComplexD*));
acceleratorCopyFromDevice(&Amat[0],&Aptr_h[0],batchCount*sizeof(ComplexD*));
auto st2 = cublasZgetriBatched(Handle(), n,
(const cuDoubleComplex *const *)&Amat[0], n,
&ipiv[0],
(cuDoubleComplex **)&Cptr[0], n,
&info[0], batchCount);
GRID_ASSERT(st2 == CUBLAS_STATUS_SUCCESS);
accelerator_barrier();
std::vector<int> info_h(batchCount);
acceleratorCopyFromDevice(&info[0],&info_h[0],batchCount*sizeof(int));
for(int i=0;i<batchCount;i++) GRID_ASSERT(info_h[i]==0);
for(int i=0;i<batchCount;i++)
acceleratorCopyDeviceToDevice(Cptr_h[i],Aptr_h[i],(uint64_t)N*N*sizeof(ComplexD));
#endif
#ifdef GRID_SYCL
sycl::queue *q = Handle();
std::vector<ComplexD*> Aptr_h(batchCount);
acceleratorCopyFromDevice(&Amat[0],&Aptr_h[0],batchCount*sizeof(ComplexD*));
int64_t lwf = oneapi::mkl::lapack::getrf_scratchpad_size<std::complex<double> >(*q,N,N,N);
int64_t lwi = oneapi::mkl::lapack::getri_scratchpad_size<std::complex<double> >(*q,N,N);
deviceVector<ComplexD> scratchf(lwf);
deviceVector<ComplexD> scratchi(lwi);
deviceVector<int64_t> ipiv(N);
for(int i=0;i<batchCount;i++){
oneapi::mkl::lapack::getrf(*q,N,N,(std::complex<double>*)Aptr_h[i],N,&ipiv[0],
(std::complex<double>*)&scratchf[0],lwf);
oneapi::mkl::lapack::getri(*q,N, (std::complex<double>*)Aptr_h[i],N,&ipiv[0],
(std::complex<double>*)&scratchi[0],lwi);
}
q->wait();
#endif
#if !defined(GRID_SYCL) && !defined(GRID_CUDA) && !defined(GRID_HIP)
thread_for (p, batchCount, {
Eigen::Map<Eigen::MatrixXcd> eA(Amat[p],N,N);
Eigen::PartialPivLU<Eigen::MatrixXcd> lu(eA);
eA = lu.inverse();
});
#endif
}
};
NAMESPACE_END(Grid);
-300
View File
@@ -1,300 +0,0 @@
/*************************************************************************************
Grid physics library, www.github.com/paboyle/Grid
Source file: MomentumProject.h
Copyright (C) 2025
Author: Peter Boyle <pboyle@bnl.gov>
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 */
#pragma once
NAMESPACE_BEGIN(Grid);
/*
MultiMomProject
Import vectors -> nxyz x (ncomponent x nt)
Import complex phases -> nmom x nxy
apply = via (possibly batched) GEMM
*/
template<class Field, class ComplexField>
class MomentumProject
{
public:
typedef typename Field::scalar_type scalar;
typedef typename Field::scalar_object scalar_object;
GridBase *grid;
uint64_t nmom;
uint64_t nxyz;
uint64_t nt;
uint64_t nbtw;
uint64_t words;
deviceVector<scalar> BLAS_V; //
deviceVector<scalar> BLAS_M; //
deviceVector<scalar> BLAS_P; //
MomentumProject(){};
~MomentumProject(){ Deallocate(); };
void Deallocate(void)
{
grid=nullptr;
nmom=0;
nxyz=0;
nt=0;
nbtw=0;
words=0;
BLAS_V.resize(0);
BLAS_M.resize(0);
BLAS_P.resize(0);
}
void Allocate(int _nmom,GridBase *_grid)
{
grid=_grid;
Coordinate ldims = grid->LocalDimensions();
nmom=_nmom;
nt = ldims[grid->Nd()-1];
nxyz = grid->lSites()/nt;
words = sizeof(scalar_object)/sizeof(scalar);
nbtw = nt * words;
BLAS_V.resize (nxyz * nt * words );
BLAS_M.resize (nmom * nxyz );
BLAS_P.resize (nmom * nt * words );
}
void ImportMomenta(const std::vector <ComplexField> &momenta)
{
GRID_ASSERT(momenta.size()==nmom);
// might as well just make the momenta here
typedef typename Field::vector_object vobj;
int nd = grid->_ndimension;
uint64_t sz = BLAS_M.size();
GRID_ASSERT(momenta.size()==nmom)
GRID_ASSERT(momenta[0].Grid()==grid);
GRID_ASSERT(sz = nxyz * nmom);
Coordinate rdimensions = grid->_rdimensions;
Coordinate ldims = grid->LocalDimensions();
int64_t osites = grid->oSites();
Coordinate simd = grid->_simd_layout;
const int Nsimd = vobj::Nsimd();
uint64_t lwords = words; // local variable for copy in to GPU
int64_t Nxyz = nxyz;
auto blasData_p = &BLAS_M[0];
for(int m=0;m<momenta.size();m++){
autoView( Data , momenta[m], AcceleratorRead);
auto Data_p = &Data[0];
accelerator_for(xyz,nxyz,1,{
//////////////////////////////////////////
// isite -- map lane within buffer to lane within lattice
////////////////////////////////////////////
Coordinate lcoor(nd,0);
Lexicographic::CoorFromIndex(lcoor,xyz,ldims);
Coordinate icoor(nd);
Coordinate ocoor(nd);
for (int d = 0; d < nd; d++) {
icoor[d] = lcoor[d]/rdimensions[d];
ocoor[d] = lcoor[d]%rdimensions[d];
}
int64_t osite;
int64_t isite;
Lexicographic::IndexFromCoor(ocoor,osite,rdimensions);
Lexicographic::IndexFromCoor(icoor,isite,simd);
// BLAS_M[nmom][slice_vol]
// Fortran Column major BLAS layout is M_xyz,mom
scalar data = extractLane(isite,Data[osite]);
uint64_t idx = xyz+m*Nxyz;
blasData_p[idx] = data;
});
}
}
void ImportVector(Field &vec)
{
typedef typename Field::vector_object vobj;
int nd = grid->_ndimension;
uint64_t sz = BLAS_V.size();
GRID_ASSERT(sz = nxyz * words * nt);
Coordinate rdimensions = grid->_rdimensions;
Coordinate ldims= grid->LocalDimensions();
int64_t osites = grid->oSites();
Coordinate simd = grid->_simd_layout;
const int Nsimd = vobj::Nsimd();
uint64_t lwords= words; // local variable for copy in to GPU
auto blasData_p = &BLAS_V[0];
autoView( Data , vec, AcceleratorRead);
auto Data_p = &Data[0];
int64_t nwords = words;// for capture
int64_t Nt = nt;// for capture
accelerator_for(sf,osites,Nsimd,{
#ifdef GRID_SIMT
{
int lane=acceleratorSIMTlane(Nsimd); // buffer lane
#else
for(int lane=0;lane<Nsimd;lane++) {
#endif
//////////////////////////////////////////
// isite -- map lane within buffer to lane within lattice
////////////////////////////////////////////
Coordinate lcoor(nd,0);
Coordinate icoor(nd);
Coordinate ocoor(nd);
Lexicographic::CoorFromIndex(icoor,lane,simd);
Lexicographic::CoorFromIndex(ocoor,sf,rdimensions);
int64_t l_xyz = 0;
for (int d = 0; d < nd; d++) {
lcoor[d] = rdimensions[d]*icoor[d] + ocoor[d];
}
uint64_t l_t = lcoor[nd-1];
Coordinate xyz_coor = lcoor;
xyz_coor[nd-1] =0;
Lexicographic::IndexFromCoor(xyz_coor,l_xyz,ldims);
scalar_object data = extractLane(lane,Data[sf]);
scalar *data_words = (scalar *) &data;
for(int w = 0 ; w < nwords; w++) {
// BLAS_V[slice_vol][nt][words]
// Fortran Column major BLAS layout is V_(t,w)_xyz
uint64_t idx = w+l_t*nwords + l_xyz * nwords * Nt;
blasData_p[idx] = data_words[w];
}
#ifdef GRID_SIMT
}
#else
}
#endif
});
}
void ExportMomentumProjection(std::vector<typename Field::scalar_object> &projection)
{
projection.resize(nmom*nt);
acceleratorCopyFromDevice(&BLAS_P[0],(scalar *)&projection[0],BLAS_P.size()*sizeof(scalar));
// Could decide on a layout late?
}
// Row major layout "C" order:
// BLAS_V[slice_vol][nt][words]
// BLAS_M[nmom][slice_vol]
// BLAS_P[nmom][nt][words]
//
// Fortran Column major BLAS layout is V_(w,t)_xyz
// Fortran Column major BLAS layout is M_xyz,mom
// Fortran Column major BLAS layout is P_(w,t),mom
//
// Projected
//
// P = (V * M)_(w,t),mom
//
void Project(Field &data,std::vector< typename Field::scalar_object > & projected_gdata)
{
double t_import=0;
double t_export=0;
double t_gemm =0;
double t_allreduce=0;
t_import-=usecond();
this->ImportVector(data);
std::vector< typename Field::scalar_object > projected_planes;
deviceVector<scalar *> Vd(1);
deviceVector<scalar *> Md(1);
deviceVector<scalar *> Pd(1);
scalar * Vh = & BLAS_V[0];
scalar * Mh = & BLAS_M[0];
scalar * Ph = & BLAS_P[0];
acceleratorPut(Vd[0],Vh);
acceleratorPut(Md[0],Mh);
acceleratorPut(Pd[0],Ph);
t_import+=usecond();
GridBLAS BLAS;
/////////////////////////////////////////
// P_im = VMmx . Vxi
/////////////////////////////////////////
t_gemm-=usecond();
BLAS.gemmBatched(GridBLAS_OP_N,GridBLAS_OP_N,
words*nt,nmom,nxyz,
scalar(1.0),
Vd,
Md,
scalar(0.0), // wipe out result
Pd);
BLAS.synchronise();
t_gemm+=usecond();
t_export-=usecond();
ExportMomentumProjection(projected_planes); // resizes
t_export+=usecond();
/////////////////////////////////
// Reduce across MPI ranks
/////////////////////////////////
int nd = grid->Nd();
int gt = grid->GlobalDimensions()[nd-1];
int lt = grid->LocalDimensions()[nd-1];
projected_gdata.resize(gt*nmom);
for(int t=0;t<gt*nmom;t++){ // global Nt array with zeroes for stuff not on this node
projected_gdata[t]=Zero();
}
for(int t=0;t<lt;t++){
for(int m=0;m<nmom;m++){
int st = grid->LocalStarts()[nd-1];
projected_gdata[t+st + gt*m] = projected_planes[t+lt*m];
}}
t_allreduce-=usecond();
grid->GlobalSumVector((scalar *)&projected_gdata[0],gt*nmom*words);
t_allreduce+=usecond();
std::cout << GridLogPerformance<<" MomentumProject t_import "<<t_import<<"us"<<std::endl;
std::cout << GridLogPerformance<<" MomentumProject t_export "<<t_export<<"us"<<std::endl;
std::cout << GridLogPerformance<<" MomentumProject t_gemm "<<t_gemm<<"us"<<std::endl;
std::cout << GridLogPerformance<<" MomentumProject t_reduce "<<t_allreduce<<"us"<<std::endl;
}
};
NAMESPACE_END(Grid);
+7 -6
View File
@@ -69,8 +69,8 @@ public:
DeflatedGuesser(const std::vector<Field> & _evec, const std::vector<RealD> & _eval, const unsigned int _N) DeflatedGuesser(const std::vector<Field> & _evec, const std::vector<RealD> & _eval, const unsigned int _N)
: evec(_evec), eval(_eval), N(_N) : evec(_evec), eval(_eval), N(_N)
{ {
GRID_ASSERT(evec.size()==eval.size()); assert(evec.size()==eval.size());
GRID_ASSERT(N <= evec.size()); assert(N <= evec.size());
} }
virtual void operator()(const Field &src,Field &guess) { virtual void operator()(const Field &src,Field &guess) {
@@ -141,10 +141,11 @@ public:
} }
//postprocessing //postprocessing
std::cout << GridLogMessage << "Start BlockPromote for loop" << std::endl; std::cout << GridLogMessage << "Start BlockPromote for loop" << std::endl;
for (int j=0;j<Nsrc;j++) { for (int j=0;j<Nsrc;j++)
std::cout << GridLogMessage << "BlockProject iter: " << j << std::endl; {
blockPromote(guess_coarse[j],guess[j],subspace); std::cout << GridLogMessage << "BlockProject iter: " << j << std::endl;
guess[j].Checkerboard() = src[j].Checkerboard(); blockPromote(guess_coarse[j],guess[j],subspace);
guess[j].Checkerboard() = src[j].Checkerboard();
} }
}; };
@@ -160,7 +160,7 @@ public:
uint64_t words; uint64_t words;
nrhs = X.size(); nrhs = X.size();
GRID_ASSERT(X.size()==Y.size()); assert(X.size()==Y.size());
conformable(X[0],Y[0]); conformable(X[0],Y[0]);
grid = X[0].Grid(); grid = X[0].Grid();
@@ -259,7 +259,7 @@ public:
uint64_t words; uint64_t words;
nrhs = X.size(); nrhs = X.size();
GRID_ASSERT(X.size()==Y.size()); assert(X.size()==Y.size());
conformable(X[0],Y[0]); conformable(X[0],Y[0]);
grid = X[0].Grid(); grid = X[0].Grid();
@@ -267,7 +267,7 @@ public:
vol = grid->oSites()/rd0; vol = grid->oSites()/rd0;
words = rd0*sizeof(vector_object)/sizeof(scalar); words = rd0*sizeof(vector_object)/sizeof(scalar);
int64_t vw = vol * words; int64_t vw = vol * words;
GRID_ASSERT(vw == grid->lSites()*sizeof(scalar_object)/sizeof(scalar)); assert(vw == grid->lSites()*sizeof(scalar_object)/sizeof(scalar));
RealD t0 = usecond(); RealD t0 = usecond();
BLAS_X.resize(nrhs * vw); // cost free if size doesn't change BLAS_X.resize(nrhs * vw); // cost free if size doesn't change
+77 -617
View File
@@ -61,17 +61,6 @@ public:
uint64_t coarse_vol; uint64_t coarse_vol;
uint64_t words; uint64_t words;
////////////////////////////////////////////////////////////////////////////
// Blocking geometry in full local coordinates. Addressing in lSites rather
// than (lane,oSite) lets the fine and coarse spaces carry different SIMD
// layouts, so an unvectorised coarse space may block a vectorised fine one.
////////////////////////////////////////////////////////////////////////////
Coordinate fine_ldimensions;
Coordinate coarse_ldimensions;
Coordinate block_ldimensions;
Coordinate fine_simd;
Coordinate coarse_simd;
// Row major layout "C" order: // Row major layout "C" order:
// BLAS_V[coarse_vol][nbasis][block_vol][words] // BLAS_V[coarse_vol][nbasis][block_vol][words]
// BLAS_F[coarse_vol][nrhs][block_vol][words] // BLAS_F[coarse_vol][nrhs][block_vol][words]
@@ -131,47 +120,35 @@ public:
fine_vol = fine_grid->lSites(); fine_vol = fine_grid->lSites();
coarse_vol = coarse_grid->lSites(); coarse_vol = coarse_grid->lSites();
block_vol = fine_vol/coarse_vol; block_vol = fine_vol/coarse_vol;
words = sizeof(scalar_object)/sizeof(scalar); words = sizeof(scalar_object)/sizeof(scalar);
int nd = coarse_grid->_ndimension;
GRID_ASSERT(fine_grid->_ndimension == nd);
fine_ldimensions.resize(nd);
coarse_ldimensions.resize(nd);
block_ldimensions.resize(nd);
fine_simd = fine_grid->_simd_layout;
coarse_simd = coarse_grid->_simd_layout;
for(int d=0;d<nd;d++){
GRID_ASSERT(fine_grid->_processors[d] == coarse_grid->_processors[d]);
fine_ldimensions [d] = fine_grid->_rdimensions [d]*fine_grid->_simd_layout [d];
coarse_ldimensions[d] = coarse_grid->_rdimensions[d]*coarse_grid->_simd_layout[d];
block_ldimensions [d] = fine_ldimensions[d]/coarse_ldimensions[d];
GRID_ASSERT(block_ldimensions[d]*coarse_ldimensions[d] == fine_ldimensions[d]);
}
GRID_ASSERT(block_vol == fine_vol/coarse_vol);
BLAS_V.resize (fine_vol * words * nbasis ); BLAS_V.resize (fine_vol * words * nbasis );
} }
void ImportFineGridVectors(std::vector <Field > &vecs, deviceVector<scalar> &blas) void ImportFineGridVectors(std::vector <Field > &vecs, deviceVector<scalar> &blas)
{ {
GRID_TRACE("ImportFineGridVectors");
int nvec = vecs.size(); int nvec = vecs.size();
typedef typename Field::vector_object vobj; typedef typename Field::vector_object vobj;
// std::cout << GridLogMessage <<" BlockProjector importing "<<nvec<< " fine grid vectors" <<std::endl; // std::cout << GridLogMessage <<" BlockProjector importing "<<nvec<< " fine grid vectors" <<std::endl;
GRID_ASSERT(vecs[0].Grid()==fine_grid); assert(vecs[0].Grid()==fine_grid);
subdivides(coarse_grid,fine_grid); // require they map
int _ndimension = coarse_grid->_ndimension; int _ndimension = coarse_grid->_ndimension;
assert(block_vol == fine_grid->oSites() / coarse_grid->oSites());
Coordinate block_r (_ndimension);
for(int d=0 ; d<_ndimension;d++){
block_r[d] = fine_grid->_rdimensions[d] / coarse_grid->_rdimensions[d];
}
uint64_t sz = blas.size(); uint64_t sz = blas.size();
acceleratorMemSet(&blas[0],0,blas.size()*sizeof(scalar)); acceleratorMemSet(&blas[0],0,blas.size()*sizeof(scalar));
Coordinate fine_rdimensions = fine_grid->_rdimensions; Coordinate fine_rdimensions = fine_grid->_rdimensions;
Coordinate coarse_l = coarse_ldimensions; Coordinate coarse_rdimensions = coarse_grid->_rdimensions;
Coordinate block_l = block_ldimensions;
Coordinate fsimd = fine_simd;
int64_t bv= block_vol; int64_t bv= block_vol;
for(int v=0;v<vecs.size();v++){ for(int v=0;v<vecs.size();v++){
@@ -185,7 +162,9 @@ public:
// loop over fine sites // loop over fine sites
const int Nsimd = vobj::Nsimd(); const int Nsimd = vobj::Nsimd();
GRID_ASSERT(sz == coarse_vol * block_vol * nvec * words); // std::cout << "sz "<<sz<<std::endl;
// std::cout << "prod "<<Nsimd * coarse_grid->oSites() * block_vol * nvec * words<<std::endl;
assert(sz == Nsimd * coarse_grid->oSites() * block_vol * nvec * words);
uint64_t lwords= words; // local variable for copy in to GPU uint64_t lwords= words; // local variable for copy in to GPU
accelerator_for(sf,osites,Nsimd,{ accelerator_for(sf,osites,Nsimd,{
#ifdef GRID_SIMT #ifdef GRID_SIMT
@@ -196,30 +175,30 @@ public:
#endif #endif
// One thread per fine site // One thread per fine site
Coordinate coor_f(_ndimension); Coordinate coor_f(_ndimension);
Coordinate coor_l(_ndimension);
Coordinate coor_b(_ndimension); Coordinate coor_b(_ndimension);
Coordinate coor_c(_ndimension); Coordinate coor_c(_ndimension);
// Fine (oSite,lane) to full local coor // Fine site to fine coor
Lexicographic::CoorFromIndex(coor_f,sf,fine_rdimensions); Lexicographic::CoorFromIndex(coor_f,sf,fine_rdimensions);
Lexicographic::CoorFromIndex(coor_l,lane,fsimd);
for(int d=0;d<_ndimension;d++) coor_f[d] += fine_rdimensions[d]*coor_l[d];
for(int d=0;d<_ndimension;d++) coor_b[d] = coor_f[d]%block_l[d];
for(int d=0;d<_ndimension;d++) coor_c[d] = coor_f[d]/block_l[d];
for(int d=0;d<_ndimension;d++) coor_b[d] = coor_f[d]%block_r[d];
for(int d=0;d<_ndimension;d++) coor_c[d] = coor_f[d]/block_r[d];
int sc;// coarse site int sc;// coarse site
int sb;// block site int sb;// block site
Lexicographic::IndexFromCoor(coor_c,sc,coarse_l); Lexicographic::IndexFromCoor(coor_c,sc,coarse_rdimensions);
Lexicographic::IndexFromCoor(coor_b,sb,block_l); Lexicographic::IndexFromCoor(coor_b,sb,block_r);
scalar_object data = extractLane(lane,fineData[sf]); scalar_object data = extractLane(lane,fineData[sf]);
// BLAS_F[coarse_vol][nvec][block_vol][words] // BLAS layout address calculation
int64_t site = (sc*nvec + v)*bv // words * block_vol * nbasis x coarse_vol
// coarse oSite x block vole x lanes
int64_t site = (lane*osites + sc*bv)*nvec
+ v*bv
+ sb; + sb;
// GRID_ASSERT(site*lwords<sz); // assert(site*lwords<sz);
scalar_object * ptr = (scalar_object *)&blasData_p[site*lwords]; scalar_object * ptr = (scalar_object *)&blasData_p[site*lwords];
@@ -234,333 +213,25 @@ public:
// std::cout << " BlockProjector imported vector"<<v<<std::endl; // std::cout << " BlockProjector imported vector"<<v<<std::endl;
} }
} }
////////////////////////////////////////////////////////////////////////////
// Import direct from multiRHS fine order, avoiding the unpack to a vector
// of single RHS fields.
//
// fine mrhs grid : rhs is dimension 0, undistributed, unvectorised
// Grid order : F[fine_vol][nrhs][words]
// BLAS order : BLAS_F[lane][coarse_vol][nrhs][block_vol][words]
//
// The gather of block_vol from fine_vol, and the transpose of nrhs against
// block_vol, are the irreducible part: this is not the identity.
////////////////////////////////////////////////////////////////////////////
void ImportFineGridMrhsVectors(Field &vec_mrhs, deviceVector<scalar> &blas)
{
typedef typename Field::vector_object vobj;
GridBase *fine_mrhs_grid = vec_mrhs.Grid();
int _ndimension = coarse_grid->_ndimension;
GRID_ASSERT(fine_mrhs_grid->_ndimension == _ndimension+1);
GRID_ASSERT(fine_mrhs_grid->_simd_layout[0] == 1);
GRID_ASSERT(fine_mrhs_grid->_processors[0] == 1);
for(int d=0;d<_ndimension;d++){
GRID_ASSERT(fine_mrhs_grid->_rdimensions[d+1] == fine_grid->_rdimensions[d]);
GRID_ASSERT(fine_mrhs_grid->_simd_layout[d+1] == fine_grid->_simd_layout[d]);
}
int nvec = fine_mrhs_grid->_rdimensions[0]; // nrhs
uint64_t sz = blas.size();
acceleratorMemSet(&blas[0],0,blas.size()*sizeof(scalar));
Coordinate fine_mrhs_rdimensions = fine_mrhs_grid->_rdimensions;
Coordinate fine_rdimensions = fine_grid->_rdimensions;
Coordinate coarse_l = coarse_ldimensions;
Coordinate block_l = block_ldimensions;
Coordinate fsimd = fine_simd;
int64_t bv= block_vol;
autoView( fineData , vec_mrhs, AcceleratorRead);
auto blasData_p = &blas[0];
auto fineData_p = &fineData[0];
int64_t osites = fine_grid->oSites(); // D dimensional
int64_t osites_hi = fine_mrhs_grid->oSites(); // nvec * osites
const int Nsimd = vobj::Nsimd();
GRID_ASSERT(sz == coarse_vol * block_vol * nvec * words);
uint64_t lwords= words;
int64_t lnvec = nvec;
accelerator_for(sfr,osites_hi,Nsimd,{
#ifdef GRID_SIMT
{
int lane=acceleratorSIMTlane(Nsimd); // buffer lane
#else
for(int lane=0;lane<Nsimd;lane++) {
#endif
Coordinate coor_hi(_ndimension+1);
Coordinate coor_f(_ndimension);
Coordinate coor_l(_ndimension);
Coordinate coor_b(_ndimension);
Coordinate coor_c(_ndimension);
// rhs is dimension 0 of the D+1 grid
Lexicographic::CoorFromIndex(coor_hi,sfr,fine_mrhs_rdimensions);
int v = coor_hi[0];
for(int d=0;d<_ndimension;d++) coor_f[d] = coor_hi[d+1];
Lexicographic::CoorFromIndex(coor_l,lane,fsimd);
for(int d=0;d<_ndimension;d++) coor_f[d] += fine_rdimensions[d]*coor_l[d];
for(int d=0;d<_ndimension;d++) coor_b[d] = coor_f[d]%block_l[d];
for(int d=0;d<_ndimension;d++) coor_c[d] = coor_f[d]/block_l[d];
int sc;// coarse site
int sb;// block site
Lexicographic::IndexFromCoor(coor_c,sc,coarse_l);
Lexicographic::IndexFromCoor(coor_b,sb,block_l);
scalar_object data = extractLane(lane,fineData[sfr]);
int64_t site = (sc*lnvec + v)*bv
+ sb;
scalar_object * ptr = (scalar_object *)&blasData_p[site*lwords];
*ptr = data;
#ifdef GRID_SIMT
}
#else
}
#endif
});
}
////////////////////////////////////////////////////////////////////////////
// Export direct to multiRHS coarse order.
//
// Grid order : C[coarse_vol][nrhs][nbasis]
// BLAS order : BLAS_C[lane][coarse_vol][nrhs][nbasis]
//
// At Nsimd()==1 these are the same sequence of addresses.
////////////////////////////////////////////////////////////////////////////
template<class vobj>
void ExportCoarseGridMrhsVectors(Lattice<vobj> &vec_mrhs, deviceVector<scalar> &blas)
{
typedef typename vobj::scalar_object coarse_scalar_object;
GridBase *coarse_mrhs_grid = vec_mrhs.Grid();
int _ndimension = coarse_grid->_ndimension;
GRID_ASSERT(coarse_mrhs_grid->_ndimension == _ndimension+1);
GRID_ASSERT(coarse_mrhs_grid->_simd_layout[0] == 1);
GRID_ASSERT(coarse_mrhs_grid->_processors[0] == 1);
for(int d=0;d<_ndimension;d++){
GRID_ASSERT(coarse_mrhs_grid->_rdimensions[d+1] == coarse_grid->_rdimensions[d]);
GRID_ASSERT(coarse_mrhs_grid->_simd_layout[d+1] == coarse_grid->_simd_layout[d]);
}
int nvec = coarse_mrhs_grid->_rdimensions[0]; // nrhs
Coordinate coarse_mrhs_rdimensions = coarse_mrhs_grid->_rdimensions;
Coordinate coarse_rdimensions = coarse_grid->_rdimensions;
Coordinate coarse_l = coarse_ldimensions;
Coordinate csimd = coarse_simd;
autoView( coarseData , vec_mrhs, AcceleratorWrite);
auto blasData_p = &blas[0];
auto coarseData_p = &coarseData[0];
int64_t osites = coarse_grid->oSites(); // D dimensional
int64_t osites_hi = coarse_mrhs_grid->oSites(); // nvec * osites
const int Nsimd = vobj::Nsimd();
uint64_t cwords=sizeof(typename vobj::scalar_object)/sizeof(scalar);
GRID_ASSERT(cwords==nbasis);
int64_t lnvec = nvec;
accelerator_for(scr,osites_hi,Nsimd,{
#ifdef GRID_SIMT
{
int lane=acceleratorSIMTlane(Nsimd); // buffer lane
#else
for(int lane=0;lane<Nsimd;lane++) {
#endif
Coordinate coor_hi(_ndimension+1);
Coordinate coor_l(_ndimension);
Coordinate coor_c(_ndimension);
Lexicographic::CoorFromIndex(coor_hi,scr,coarse_mrhs_rdimensions);
int v = coor_hi[0];
for(int d=0;d<_ndimension;d++) coor_c[d] = coor_hi[d+1];
Lexicographic::CoorFromIndex(coor_l,lane,csimd);
for(int d=0;d<_ndimension;d++) coor_c[d] += coarse_rdimensions[d]*coor_l[d];
int sc;
Lexicographic::IndexFromCoor(coor_c,sc,coarse_l);
int64_t blas_site = (sc*lnvec + v)*cwords;
coarse_scalar_object * ptr = (coarse_scalar_object *)&blasData_p[blas_site];
coarse_scalar_object data = *ptr;
insertLane(lane,coarseData[scr],data);
#ifdef GRID_SIMT
}
#else
}
#endif
});
}
////////////////////////////////////////////////////////////////////////////
// Reverse directions: BLAS_F -> fine mrhs field, coarse mrhs field -> BLAS_C
////////////////////////////////////////////////////////////////////////////
void ExportFineGridMrhsVectors(Field &vec_mrhs, deviceVector<scalar> &blas)
{
typedef typename Field::vector_object vobj;
GridBase *fine_mrhs_grid = vec_mrhs.Grid();
int _ndimension = coarse_grid->_ndimension;
GRID_ASSERT(fine_mrhs_grid->_ndimension == _ndimension+1);
GRID_ASSERT(fine_mrhs_grid->_simd_layout[0] == 1);
for(int d=0;d<_ndimension;d++){
GRID_ASSERT(fine_mrhs_grid->_rdimensions[d+1] == fine_grid->_rdimensions[d]);
GRID_ASSERT(fine_mrhs_grid->_simd_layout[d+1] == fine_grid->_simd_layout[d]);
}
int nvec = fine_mrhs_grid->_rdimensions[0];
Coordinate fine_mrhs_rdimensions = fine_mrhs_grid->_rdimensions;
Coordinate fine_rdimensions = fine_grid->_rdimensions;
Coordinate coarse_l = coarse_ldimensions;
Coordinate block_l = block_ldimensions;
Coordinate fsimd = fine_simd;
int64_t bv= block_vol;
autoView( fineData , vec_mrhs, AcceleratorWrite);
auto blasData_p = &blas[0];
auto fineData_p = &fineData[0];
int64_t osites = fine_grid->oSites();
int64_t osites_hi = fine_mrhs_grid->oSites();
const int Nsimd = vobj::Nsimd();
uint64_t lwords= words;
int64_t lnvec = nvec;
accelerator_for(sfr,osites_hi,Nsimd,{
#ifdef GRID_SIMT
{
int lane=acceleratorSIMTlane(Nsimd);
#else
for(int lane=0;lane<Nsimd;lane++) {
#endif
Coordinate coor_hi(_ndimension+1);
Coordinate coor_f(_ndimension);
Coordinate coor_l(_ndimension);
Coordinate coor_b(_ndimension);
Coordinate coor_c(_ndimension);
Lexicographic::CoorFromIndex(coor_hi,sfr,fine_mrhs_rdimensions);
int v = coor_hi[0];
for(int d=0;d<_ndimension;d++) coor_f[d] = coor_hi[d+1];
Lexicographic::CoorFromIndex(coor_l,lane,fsimd);
for(int d=0;d<_ndimension;d++) coor_f[d] += fine_rdimensions[d]*coor_l[d];
for(int d=0;d<_ndimension;d++) coor_b[d] = coor_f[d]%block_l[d];
for(int d=0;d<_ndimension;d++) coor_c[d] = coor_f[d]/block_l[d];
int sc,sb;
Lexicographic::IndexFromCoor(coor_c,sc,coarse_l);
Lexicographic::IndexFromCoor(coor_b,sb,block_l);
int64_t site = (sc*lnvec + v)*bv
+ sb;
scalar_object * ptr = (scalar_object *)&blasData_p[site*lwords];
scalar_object data = *ptr;
insertLane(lane,fineData[sfr],data);
#ifdef GRID_SIMT
}
#else
}
#endif
});
}
template<class vobj>
void ImportCoarseGridMrhsVectors(Lattice<vobj> &vec_mrhs, deviceVector<scalar> &blas)
{
typedef typename vobj::scalar_object coarse_scalar_object;
GridBase *coarse_mrhs_grid = vec_mrhs.Grid();
int _ndimension = coarse_grid->_ndimension;
GRID_ASSERT(coarse_mrhs_grid->_ndimension == _ndimension+1);
GRID_ASSERT(coarse_mrhs_grid->_simd_layout[0] == 1);
for(int d=0;d<_ndimension;d++){
GRID_ASSERT(coarse_mrhs_grid->_rdimensions[d+1] == coarse_grid->_rdimensions[d]);
GRID_ASSERT(coarse_mrhs_grid->_simd_layout[d+1] == coarse_grid->_simd_layout[d]);
}
int nvec = coarse_mrhs_grid->_rdimensions[0];
Coordinate coarse_mrhs_rdimensions = coarse_mrhs_grid->_rdimensions;
Coordinate coarse_rdimensions = coarse_grid->_rdimensions;
Coordinate coarse_l = coarse_ldimensions;
Coordinate csimd = coarse_simd;
autoView( coarseData , vec_mrhs, AcceleratorRead);
auto blasData_p = &blas[0];
auto coarseData_p = &coarseData[0];
int64_t osites = coarse_grid->oSites();
int64_t osites_hi = coarse_mrhs_grid->oSites();
const int Nsimd = vobj::Nsimd();
uint64_t cwords=sizeof(typename vobj::scalar_object)/sizeof(scalar);
GRID_ASSERT(cwords==nbasis);
int64_t lnvec = nvec;
accelerator_for(scr,osites_hi,Nsimd,{
#ifdef GRID_SIMT
{
int lane=acceleratorSIMTlane(Nsimd);
#else
for(int lane=0;lane<Nsimd;lane++) {
#endif
Coordinate coor_hi(_ndimension+1);
Coordinate coor_l(_ndimension);
Coordinate coor_c(_ndimension);
Lexicographic::CoorFromIndex(coor_hi,scr,coarse_mrhs_rdimensions);
int v = coor_hi[0];
for(int d=0;d<_ndimension;d++) coor_c[d] = coor_hi[d+1];
Lexicographic::CoorFromIndex(coor_l,lane,csimd);
for(int d=0;d<_ndimension;d++) coor_c[d] += coarse_rdimensions[d]*coor_l[d];
int sc;
Lexicographic::IndexFromCoor(coor_c,sc,coarse_l);
coarse_scalar_object data = extractLane(lane,coarseData[scr]);
int64_t blas_site = (sc*lnvec + v)*cwords;
coarse_scalar_object * ptr = (coarse_scalar_object *)&blasData_p[blas_site];
*ptr = data;
#ifdef GRID_SIMT
}
#else
}
#endif
});
}
void ExportFineGridVectors(std::vector <Field> &vecs, deviceVector<scalar> &blas) void ExportFineGridVectors(std::vector <Field> &vecs, deviceVector<scalar> &blas)
{ {
GRID_TRACE("ExportFineGridVectors");
typedef typename Field::vector_object vobj; typedef typename Field::vector_object vobj;
int nvec = vecs.size(); int nvec = vecs.size();
GRID_ASSERT(vecs[0].Grid()==fine_grid); assert(vecs[0].Grid()==fine_grid);
subdivides(coarse_grid,fine_grid); // require they map
int _ndimension = coarse_grid->_ndimension; int _ndimension = coarse_grid->_ndimension;
assert(block_vol == fine_grid->oSites() / coarse_grid->oSites());
Coordinate block_r (_ndimension);
for(int d=0 ; d<_ndimension;d++){
block_r[d] = fine_grid->_rdimensions[d] / coarse_grid->_rdimensions[d];
}
Coordinate fine_rdimensions = fine_grid->_rdimensions; Coordinate fine_rdimensions = fine_grid->_rdimensions;
Coordinate coarse_l = coarse_ldimensions; Coordinate coarse_rdimensions = coarse_grid->_rdimensions;
Coordinate block_l = block_ldimensions;
Coordinate fsimd = fine_simd;
// std::cout << " export fine Blas norm "<<blasNorm2(blas)<<std::endl; // std::cout << " export fine Blas norm "<<blasNorm2(blas)<<std::endl;
@@ -588,24 +259,23 @@ public:
#endif #endif
// One thread per fine site // One thread per fine site
Coordinate coor_f(_ndimension); Coordinate coor_f(_ndimension);
Coordinate coor_l(_ndimension);
Coordinate coor_b(_ndimension); Coordinate coor_b(_ndimension);
Coordinate coor_c(_ndimension); Coordinate coor_c(_ndimension);
Lexicographic::CoorFromIndex(coor_f,sf,fine_rdimensions); Lexicographic::CoorFromIndex(coor_f,sf,fine_rdimensions);
Lexicographic::CoorFromIndex(coor_l,lane,fsimd);
for(int d=0;d<_ndimension;d++) coor_f[d] += fine_rdimensions[d]*coor_l[d];
for(int d=0;d<_ndimension;d++) coor_b[d] = coor_f[d]%block_l[d];
for(int d=0;d<_ndimension;d++) coor_c[d] = coor_f[d]/block_l[d];
for(int d=0;d<_ndimension;d++) coor_b[d] = coor_f[d]%block_r[d];
for(int d=0;d<_ndimension;d++) coor_c[d] = coor_f[d]/block_r[d];
int sc; int sc;
int sb; int sb;
Lexicographic::IndexFromCoor(coor_c,sc,coarse_l); Lexicographic::IndexFromCoor(coor_c,sc,coarse_rdimensions);
Lexicographic::IndexFromCoor(coor_b,sb,block_l); Lexicographic::IndexFromCoor(coor_b,sb,block_r);
// BLAS_F[coarse_vol][nvec][block_vol][words] // BLAS layout address calculation
int64_t site = (sc*nvec + v)*bv // words * block_vol * nbasis x coarse_vol
int64_t site = (lane*osites + sc*bv)*nvec
+ v*bv
+ sb; + sb;
scalar_object * ptr = (scalar_object *)&blasData_p[site*lwords]; scalar_object * ptr = (scalar_object *)&blasData_p[site*lwords];
@@ -624,22 +294,19 @@ public:
template<class vobj> template<class vobj>
void ImportCoarseGridVectors(std::vector <Lattice<vobj> > &vecs, deviceVector<scalar> &blas) void ImportCoarseGridVectors(std::vector <Lattice<vobj> > &vecs, deviceVector<scalar> &blas)
{ {
GRID_TRACE("ImportCoarseGridVectors");
int nvec = vecs.size(); int nvec = vecs.size();
typedef typename vobj::scalar_object coarse_scalar_object; typedef typename vobj::scalar_object coarse_scalar_object;
// std::cout << " BlockProjector importing "<<nvec<< " coarse grid vectors" <<std::endl; // std::cout << " BlockProjector importing "<<nvec<< " coarse grid vectors" <<std::endl;
GRID_ASSERT(vecs[0].Grid()==coarse_grid); assert(vecs[0].Grid()==coarse_grid);
int _ndimension = coarse_grid->_ndimension; int _ndimension = coarse_grid->_ndimension;
uint64_t sz = blas.size(); uint64_t sz = blas.size();
Coordinate coarse_rdimensions = coarse_grid->_rdimensions; Coordinate coarse_rdimensions = coarse_grid->_rdimensions;
Coordinate coarse_l = coarse_ldimensions;
Coordinate csimd = coarse_simd;
for(int v=0;v<vecs.size();v++){ for(int v=0;v<vecs.size();v++){
// std::cout << " BlockProjector importing coarse vector"<<v<<" "<<norm2(vecs[v])<<std::endl; // std::cout << " BlockProjector importing coarse vector"<<v<<" "<<norm2(vecs[v])<<std::endl;
@@ -653,7 +320,7 @@ public:
// loop over fine sites // loop over fine sites
const int Nsimd = vobj::Nsimd(); const int Nsimd = vobj::Nsimd();
uint64_t cwords=sizeof(typename vobj::scalar_object)/sizeof(scalar); uint64_t cwords=sizeof(typename vobj::scalar_object)/sizeof(scalar);
GRID_ASSERT(cwords==nbasis); assert(cwords==nbasis);
accelerator_for(sc,osites,Nsimd,{ accelerator_for(sc,osites,Nsimd,{
#ifdef GRID_SIMT #ifdef GRID_SIMT
@@ -663,17 +330,8 @@ public:
for(int lane=0;lane<Nsimd;lane++) { for(int lane=0;lane<Nsimd;lane++) {
#endif #endif
// C_br per site // C_br per site
Coordinate coor_c(_ndimension); int64_t blas_site = (lane*osites + sc)*nvec*cwords + v*cwords;
Coordinate coor_l(_ndimension);
Lexicographic::CoorFromIndex(coor_c,sc,coarse_rdimensions);
Lexicographic::CoorFromIndex(coor_l,lane,csimd);
for(int d=0;d<_ndimension;d++) coor_c[d] += coarse_rdimensions[d]*coor_l[d];
int scl;
Lexicographic::IndexFromCoor(coor_c,scl,coarse_l);
int64_t blas_site = (scl*nvec + v)*cwords;
coarse_scalar_object data = extractLane(lane,coarseData[sc]); coarse_scalar_object data = extractLane(lane,coarseData[sc]);
coarse_scalar_object * ptr = (coarse_scalar_object *)&blasData_p[blas_site]; coarse_scalar_object * ptr = (coarse_scalar_object *)&blasData_p[blas_site];
@@ -691,21 +349,18 @@ public:
template<class vobj> template<class vobj>
void ExportCoarseGridVectors(std::vector <Lattice<vobj> > &vecs, deviceVector<scalar> &blas) void ExportCoarseGridVectors(std::vector <Lattice<vobj> > &vecs, deviceVector<scalar> &blas)
{ {
GRID_TRACE("ExportCoarseGridVectors");
int nvec = vecs.size(); int nvec = vecs.size();
typedef typename vobj::scalar_object coarse_scalar_object; typedef typename vobj::scalar_object coarse_scalar_object;
// std::cout << GridLogMessage<<" BlockProjector exporting "<<nvec<< " coarse grid vectors" <<std::endl; // std::cout << GridLogMessage<<" BlockProjector exporting "<<nvec<< " coarse grid vectors" <<std::endl;
GRID_ASSERT(vecs[0].Grid()==coarse_grid); assert(vecs[0].Grid()==coarse_grid);
int _ndimension = coarse_grid->_ndimension; int _ndimension = coarse_grid->_ndimension;
uint64_t sz = blas.size(); uint64_t sz = blas.size();
Coordinate coarse_rdimensions = coarse_grid->_rdimensions; Coordinate coarse_rdimensions = coarse_grid->_rdimensions;
Coordinate coarse_l = coarse_ldimensions;
Coordinate csimd = coarse_simd;
// std::cout << " export coarsee Blas norm "<<blasNorm2(blas)<<std::endl; // std::cout << " export coarsee Blas norm "<<blasNorm2(blas)<<std::endl;
for(int v=0;v<vecs.size();v++){ for(int v=0;v<vecs.size();v++){
@@ -720,7 +375,7 @@ public:
// loop over fine sites // loop over fine sites
const int Nsimd = vobj::Nsimd(); const int Nsimd = vobj::Nsimd();
uint64_t cwords=sizeof(typename vobj::scalar_object)/sizeof(scalar); uint64_t cwords=sizeof(typename vobj::scalar_object)/sizeof(scalar);
GRID_ASSERT(cwords==nbasis); assert(cwords==nbasis);
accelerator_for(sc,osites,Nsimd,{ accelerator_for(sc,osites,Nsimd,{
// Wrap in a macro "FOR_ALL_LANES(lane,{ ... }); // Wrap in a macro "FOR_ALL_LANES(lane,{ ... });
@@ -730,16 +385,7 @@ public:
#else #else
for(int lane=0;lane<Nsimd;lane++) { for(int lane=0;lane<Nsimd;lane++) {
#endif #endif
Coordinate coor_c(_ndimension); int64_t blas_site = (lane*osites + sc)*nvec*cwords + v*cwords;
Coordinate coor_l(_ndimension);
Lexicographic::CoorFromIndex(coor_c,sc,coarse_rdimensions);
Lexicographic::CoorFromIndex(coor_l,lane,csimd);
for(int d=0;d<_ndimension;d++) coor_c[d] += coarse_rdimensions[d]*coor_l[d];
int scl;
Lexicographic::IndexFromCoor(coor_c,scl,coarse_l);
int64_t blas_site = (scl*nvec + v)*cwords;
coarse_scalar_object * ptr = (coarse_scalar_object *)&blasData_p[blas_site]; coarse_scalar_object * ptr = (coarse_scalar_object *)&blasData_p[blas_site];
coarse_scalar_object data = *ptr; coarse_scalar_object data = *ptr;
insertLane(lane,coarseData[sc],data); insertLane(lane,coarseData[sc],data);
@@ -760,11 +406,10 @@ public:
template<class cobj> template<class cobj>
void blockProject(std::vector<Field> &fine,std::vector< Lattice<cobj> > & coarse) void blockProject(std::vector<Field> &fine,std::vector< Lattice<cobj> > & coarse)
{ {
GRID_TRACE("BlockProject");
int nrhs=fine.size(); int nrhs=fine.size();
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar); int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar);
// std::cout << "blockProject nbasis " <<nbasis<<" " << _nbasis<<std::endl; // std::cout << "blockProject nbasis " <<nbasis<<" " << _nbasis<<std::endl;
GRID_ASSERT(nbasis==_nbasis); assert(nbasis==_nbasis);
BLAS_F.resize (fine_vol * words * nrhs ); BLAS_F.resize (fine_vol * words * nrhs );
BLAS_C.resize (coarse_vol * nbasis * nrhs ); BLAS_C.resize (coarse_vol * nbasis * nrhs );
@@ -780,20 +425,17 @@ public:
deviceVector<scalar *> Cd(coarse_vol); deviceVector<scalar *> Cd(coarse_vol);
// std::cout << "BlockProject pointers"<<std::endl; // std::cout << "BlockProject pointers"<<std::endl;
// ONE bulk transfer per table. acceleratorPut is a *synchronous* 8-byte for(int c=0;c<coarse_vol;c++){
// hipMemcpy, so the elementwise form emitted 3*coarse_vol of them per call: // BLAS_V[coarse_vol][nbasis][block_vol][words]
// a traced run showed 272k hipMemcpy calls costing 4.5 s of API time to move // BLAS_F[coarse_vol][nrhs][block_vol][words]
// 0.7 s worth of bytes. Same fix as BatchedBlas.h's staging rewrite. // BLAS_C[coarse_vol][nrhs][nbasis]
if ( coarse_vol ) { scalar * Vh = & BLAS_V[c*nbasis*block_vol*words];
std::vector<scalar *> hVd(coarse_vol), hFd(coarse_vol), hCd(coarse_vol); scalar * Fh = & BLAS_F[c*nrhs*block_vol*words];
for(int c=0;c<coarse_vol;c++){ scalar * Ch = & BLAS_C[c*nrhs*nbasis];
hVd[c] = & BLAS_V[c*nbasis*block_vol*words];
hFd[c] = & BLAS_F[c*nrhs*block_vol*words]; acceleratorPut(Vd[c],Vh);
hCd[c] = & BLAS_C[c*nrhs*nbasis]; acceleratorPut(Fd[c],Fh);
} acceleratorPut(Cd[c],Ch);
acceleratorCopyToDevice(&hVd[0],&Vd[0],coarse_vol*sizeof(scalar *));
acceleratorCopyToDevice(&hFd[0],&Fd[0],coarse_vol*sizeof(scalar *));
acceleratorCopyToDevice(&hCd[0],&Cd[0],coarse_vol*sizeof(scalar *));
} }
GridBLAS BLAS; GridBLAS BLAS;
@@ -820,10 +462,9 @@ public:
template<class cobj> template<class cobj>
void blockPromote(std::vector<Field> &fine,std::vector<Lattice<cobj> > & coarse) void blockPromote(std::vector<Field> &fine,std::vector<Lattice<cobj> > & coarse)
{ {
GRID_TRACE("BlockPromote");
int nrhs=fine.size(); int nrhs=fine.size();
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar); int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar);
GRID_ASSERT(nbasis==_nbasis); assert(nbasis==_nbasis);
BLAS_F.resize (fine_vol * words * nrhs ); BLAS_F.resize (fine_vol * words * nrhs );
BLAS_C.resize (coarse_vol * nbasis * nrhs ); BLAS_C.resize (coarse_vol * nbasis * nrhs );
@@ -836,20 +477,16 @@ public:
deviceVector<scalar *> Fd(coarse_vol); deviceVector<scalar *> Fd(coarse_vol);
deviceVector<scalar *> Cd(coarse_vol); deviceVector<scalar *> Cd(coarse_vol);
// ONE bulk transfer per table. acceleratorPut is a *synchronous* 8-byte for(int c=0;c<coarse_vol;c++){
// hipMemcpy, so the elementwise form emitted 3*coarse_vol of them per call: // BLAS_V[coarse_vol][nbasis][block_vol][words]
// a traced run showed 272k hipMemcpy calls costing 4.5 s of API time to move // BLAS_F[coarse_vol][nrhs][block_vol][words]
// 0.7 s worth of bytes. Same fix as BatchedBlas.h's staging rewrite. // BLAS_C[coarse_vol][nrhs][nbasis]
if ( coarse_vol ) { scalar * Vh = & BLAS_V[c*nbasis*block_vol*words];
std::vector<scalar *> hVd(coarse_vol), hFd(coarse_vol), hCd(coarse_vol); scalar * Fh = & BLAS_F[c*nrhs*block_vol*words];
for(int c=0;c<coarse_vol;c++){ scalar * Ch = & BLAS_C[c*nrhs*nbasis];
hVd[c] = & BLAS_V[c*nbasis*block_vol*words]; acceleratorPut(Vd[c],Vh);
hFd[c] = & BLAS_F[c*nrhs*block_vol*words]; acceleratorPut(Fd[c],Fh);
hCd[c] = & BLAS_C[c*nrhs*nbasis]; acceleratorPut(Cd[c],Ch);
}
acceleratorCopyToDevice(&hVd[0],&Vd[0],coarse_vol*sizeof(scalar *));
acceleratorCopyToDevice(&hFd[0],&Fd[0],coarse_vol*sizeof(scalar *));
acceleratorCopyToDevice(&hCd[0],&Cd[0],coarse_vol*sizeof(scalar *));
} }
///////////////////////////////////////// /////////////////////////////////////////
@@ -871,183 +508,6 @@ public:
ExportFineGridVectors(fine, BLAS_F); ExportFineGridVectors(fine, BLAS_F);
// std::cout << " exported "<<std::endl; // std::cout << " exported "<<std::endl;
} }
////////////////////////////////////////////////////////////////////////////
// multiRHS ordered interfaces. The GEMM is identical; only the import and
// export differ, so those are the whole of the layout question.
////////////////////////////////////////////////////////////////////////////
template<class cobj>
void blockProject(Field &fine_mrhs,Lattice<cobj> &coarse_mrhs)
{
GRID_TRACE("BlockProjectMrhs");
int nrhs = fine_mrhs.Grid()->_rdimensions[0];
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar);
GRID_ASSERT(nbasis==_nbasis);
GRID_ASSERT(coarse_mrhs.Grid()->_rdimensions[0]==nrhs);
BLAS_F.resize (fine_vol * words * nrhs );
BLAS_C.resize (coarse_vol * nbasis * nrhs );
ImportFineGridMrhsVectors(fine_mrhs,BLAS_F);
ProjectBLAS(nrhs);
ExportCoarseGridMrhsVectors(coarse_mrhs,BLAS_C);
}
template<class cobj>
void blockPromote(Field &fine_mrhs,Lattice<cobj> &coarse_mrhs)
{
GRID_TRACE("BlockPromoteMrhs");
int nrhs = fine_mrhs.Grid()->_rdimensions[0];
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar);
GRID_ASSERT(nbasis==_nbasis);
GRID_ASSERT(coarse_mrhs.Grid()->_rdimensions[0]==nrhs);
BLAS_F.resize (fine_vol * words * nrhs );
BLAS_C.resize (coarse_vol * nbasis * nrhs );
ImportCoarseGridMrhsVectors(coarse_mrhs,BLAS_C);
PromoteBLAS(nrhs);
ExportFineGridMrhsVectors(fine_mrhs,BLAS_F);
}
////////////////////////////////////////////////////////////////////////////
// Mixed orderings. A single RHS fine operator produces a vector of fine
// fields with no packing; the coarse side is still wanted in mrhs order.
////////////////////////////////////////////////////////////////////////////
template<class cobj>
void blockProject(std::vector<Field> &fine,Lattice<cobj> &coarse_mrhs)
{
GRID_TRACE("BlockProjectMixed");
int nrhs = fine.size();
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar);
GRID_ASSERT(nbasis==_nbasis);
GRID_ASSERT(coarse_mrhs.Grid()->_rdimensions[0]==nrhs);
BLAS_F.resize (fine_vol * words * nrhs );
BLAS_C.resize (coarse_vol * nbasis * nrhs );
ImportFineGridVectors(fine,BLAS_F);
ProjectBLAS(nrhs);
ExportCoarseGridMrhsVectors(coarse_mrhs,BLAS_C);
}
template<class cobj>
void blockProject(Field &fine_mrhs,std::vector< Lattice<cobj> > &coarse)
{
GRID_TRACE("BlockProjectMixed");
int nrhs = fine_mrhs.Grid()->_rdimensions[0];
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar);
GRID_ASSERT(nbasis==_nbasis);
GRID_ASSERT(coarse.size()==nrhs);
BLAS_F.resize (fine_vol * words * nrhs );
BLAS_C.resize (coarse_vol * nbasis * nrhs );
ImportFineGridMrhsVectors(fine_mrhs,BLAS_F);
ProjectBLAS(nrhs);
ExportCoarseGridVectors(coarse,BLAS_C);
}
template<class cobj>
void blockPromote(std::vector<Field> &fine,Lattice<cobj> &coarse_mrhs)
{
GRID_TRACE("BlockPromoteMixed");
int nrhs = fine.size();
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar);
GRID_ASSERT(nbasis==_nbasis);
GRID_ASSERT(coarse_mrhs.Grid()->_rdimensions[0]==nrhs);
BLAS_F.resize (fine_vol * words * nrhs );
BLAS_C.resize (coarse_vol * nbasis * nrhs );
ImportCoarseGridMrhsVectors(coarse_mrhs,BLAS_C);
PromoteBLAS(nrhs);
ExportFineGridVectors(fine,BLAS_F);
}
template<class cobj>
void blockPromote(Field &fine_mrhs,std::vector< Lattice<cobj> > &coarse)
{
GRID_TRACE("BlockPromoteMixed");
int nrhs = fine_mrhs.Grid()->_rdimensions[0];
int _nbasis = sizeof(typename cobj::scalar_object)/sizeof(scalar);
GRID_ASSERT(nbasis==_nbasis);
GRID_ASSERT(coarse.size()==nrhs);
BLAS_F.resize (fine_vol * words * nrhs );
BLAS_C.resize (coarse_vol * nbasis * nrhs );
ImportCoarseGridVectors(coarse,BLAS_C);
PromoteBLAS(nrhs);
ExportFineGridMrhsVectors(fine_mrhs,BLAS_F);
}
////////////////////////////////////////////////////////////////////////////
// Pointer tables and the batched GEMM, shared by both orderings
////////////////////////////////////////////////////////////////////////////
void BLASPointers(int nrhs,
deviceVector<scalar *> &Vd,
deviceVector<scalar *> &Fd,
deviceVector<scalar *> &Cd)
{
// ONE bulk transfer per table. acceleratorPut is a *synchronous* 8-byte
// hipMemcpy, so the elementwise form emitted 3*coarse_vol of them per call:
// a traced run showed 272k hipMemcpy calls costing 4.5 s of API time to move
// 0.7 s worth of bytes. Same fix as BatchedBlas.h's staging rewrite.
if ( coarse_vol ) {
std::vector<scalar *> hVd(coarse_vol), hFd(coarse_vol), hCd(coarse_vol);
for(int c=0;c<coarse_vol;c++){
hVd[c] = & BLAS_V[c*nbasis*block_vol*words];
hFd[c] = & BLAS_F[c*nrhs*block_vol*words];
hCd[c] = & BLAS_C[c*nrhs*nbasis];
}
acceleratorCopyToDevice(&hVd[0],&Vd[0],coarse_vol*sizeof(scalar *));
acceleratorCopyToDevice(&hFd[0],&Fd[0],coarse_vol*sizeof(scalar *));
acceleratorCopyToDevice(&hCd[0],&Cd[0],coarse_vol*sizeof(scalar *));
}
}
// C_br = V^dag F
void ProjectBLAS(int nrhs)
{
GRID_TRACE("ProjectBLAS");
deviceVector<scalar *> Vd(coarse_vol);
deviceVector<scalar *> Fd(coarse_vol);
deviceVector<scalar *> Cd(coarse_vol);
BLASPointers(nrhs,Vd,Fd,Cd);
GridBLAS BLAS;
int64_t vw = block_vol * words;
BLAS.gemmBatched(GridBLAS_OP_C,GridBLAS_OP_N,
nbasis,nrhs,vw,
scalar(1.0),
Vd,
Fd,
scalar(0.0), // wipe out C
Cd);
BLAS.synchronise();
}
// F_xr = Vxb Cbr
void PromoteBLAS(int nrhs)
{
GRID_TRACE("PromoteBLAS");
deviceVector<scalar *> Vd(coarse_vol);
deviceVector<scalar *> Fd(coarse_vol);
deviceVector<scalar *> Cd(coarse_vol);
BLASPointers(nrhs,Vd,Fd,Cd);
GridBLAS BLAS;
int64_t vw = block_vol * words;
BLAS.gemmBatched(GridBLAS_OP_N,GridBLAS_OP_N,
vw,nrhs,nbasis,
scalar(1.0),
Vd,
Cd,
scalar(0.0), // wipe out F
Fd);
BLAS.synchronise();
}
}; };
NAMESPACE_END(Grid); NAMESPACE_END(Grid);
@@ -98,7 +98,7 @@ public:
void ImportEigenVector(Field &evec,RealD &_eval, int ev) void ImportEigenVector(Field &evec,RealD &_eval, int ev)
{ {
// std::cout << " ev " <<ev<<" eval "<<_eval<< std::endl; // std::cout << " ev " <<ev<<" eval "<<_eval<< std::endl;
GRID_ASSERT(ev<eval.size()); assert(ev<eval.size());
eval[ev] = _eval; eval[ev] = _eval;
int64_t offset = ev*vol*words; int64_t offset = ev*vol*words;
@@ -113,7 +113,7 @@ public:
// Could use to import a batch of eigenvectors // Could use to import a batch of eigenvectors
void ImportEigenBasis(std::vector<Field> &evec,std::vector<RealD> &_eval, int _ev0, int _nev) void ImportEigenBasis(std::vector<Field> &evec,std::vector<RealD> &_eval, int _ev0, int _nev)
{ {
GRID_ASSERT(_ev0+_nev<=evec.size()); assert(_ev0+_nev<=evec.size());
Allocate(_nev,evec[0].Grid()); Allocate(_nev,evec[0].Grid());
@@ -126,8 +126,8 @@ public:
void DeflateSources(std::vector<Field> &source,std::vector<Field> & guess) void DeflateSources(std::vector<Field> &source,std::vector<Field> & guess)
{ {
int nrhs = source.size(); int nrhs = source.size();
GRID_ASSERT(source.size()==guess.size()); assert(source.size()==guess.size());
GRID_ASSERT(grid == guess[0].Grid()); assert(grid == guess[0].Grid());
conformable(guess[0],source[0]); conformable(guess[0],source[0]);
int64_t vw = vol * words; int64_t vw = vol * words;
@@ -189,7 +189,7 @@ public:
Cd); Cd);
BLAS.synchronise(); BLAS.synchronise();
GRID_ASSERT(BLAS_C.size()==nev*nrhs); assert(BLAS_C.size()==nev*nrhs);
std::vector<scalar> HOST_C(BLAS_C.size()); // nrhs . nev -- the coefficients std::vector<scalar> HOST_C(BLAS_C.size()); // nrhs . nev -- the coefficients
acceleratorCopyFromDevice(&BLAS_C[0],&HOST_C[0],BLAS_C.size()*sizeof(scalar)); acceleratorCopyFromDevice(&BLAS_C[0],&HOST_C[0],BLAS_C.size()*sizeof(scalar));
+33 -74
View File
@@ -53,22 +53,7 @@ class TwoLevelCG : public LinearFunction<Field>
// Fine operator, Smoother, CoarseSolver // Fine operator, Smoother, CoarseSolver
LinearOperatorBase<Field> &_FineLinop; LinearOperatorBase<Field> &_FineLinop;
LinearFunction<Field> &_Smoother; LinearFunction<Field> &_Smoother;
GridStopWatch ProjectTimer;
GridStopWatch PromoteTimer;
GridStopWatch CoarseTimer;
GridStopWatch SmoothTimer;
GridStopWatch MatrixTimer;
GridStopWatch M3Timer;
GridStopWatch LinalgTimer;
int64_t M3Calls;
int64_t SmoothCalls;
int64_t MatrixCalls;
int64_t ProjectCalls;
int64_t CoarseCalls;
int64_t PromoteCalls;
// more most opertor functions // more most opertor functions
TwoLevelCG(RealD tol, TwoLevelCG(RealD tol,
Integer maxit, Integer maxit,
@@ -118,20 +103,12 @@ class TwoLevelCG : public LinearFunction<Field>
RealD tn; RealD tn;
GridStopWatch HDCGTimer; GridStopWatch HDCGTimer;
ProjectTimer.Reset();
PromoteTimer.Reset();
CoarseTimer.Reset();
SmoothTimer.Reset();
MatrixTimer.Reset();
M3Timer.Reset();
LinalgTimer.Reset();
M3Calls = SmoothCalls = MatrixCalls = ProjectCalls = CoarseCalls = PromoteCalls = 0;
HDCGTimer.Start(); HDCGTimer.Start();
////////////////////////// //////////////////////////
// x0 = Vstart -- possibly modify guess // x0 = Vstart -- possibly modify guess
////////////////////////// //////////////////////////
Vstart(x,src); Vstart(x,src);
// r0 = b -A x0 // r0 = b -A x0
_FineLinop.HermOp(x,mmp[0]); _FineLinop.HermOp(x,mmp[0]);
axpy (r, -1.0,mmp[0], src); // Recomputes r=src-Ax0 axpy (r, -1.0,mmp[0], src); // Recomputes r=src-Ax0
@@ -168,40 +145,33 @@ class TwoLevelCG : public LinearFunction<Field>
int peri_kp = (k+1) % mmax; int peri_kp = (k+1) % mmax;
rtz=rtzp; rtz=rtzp;
M3Timer.Start();
d= PcgM3(p[peri_k],mmp[peri_k]); d= PcgM3(p[peri_k],mmp[peri_k]);
M3Timer.Stop();
M3Calls++;
a = rtz/d; a = rtz/d;
// Memorise this // Memorise this
pAp[peri_k] = d; pAp[peri_k] = d;
LinalgTimer.Start();
axpy(x,a,p[peri_k],x); axpy(x,a,p[peri_k],x);
RealD rn = axpy_norm(r,-a,mmp[peri_k],r); RealD rn = axpy_norm(r,-a,mmp[peri_k],r);
LinalgTimer.Stop();
// Compute z = M x // Compute z = M x
PcgM1(r,z); PcgM1(r,z);
{ {
RealD n1,n2; RealD n1,n2;
n1=norm2(r); n1=norm2(r);
n2=norm2(z); n2=norm2(z);
std::cout << GridLogMessage<<"HDCG::fPcg iteration "<<k<<" : vector r,z "<<n1<<" "<<n2<<"\n"; std::cout << GridLogMessage<<"HDCG::fPcg iteration "<<k<<" : vector r,z "<<n1<<" "<<n2<<"\n";
} }
LinalgTimer.Start();
rtzp =real(innerProduct(r,z)); rtzp =real(innerProduct(r,z));
LinalgTimer.Stop();
std::cout << GridLogMessage<<"HDCG::fPcg iteration "<<k<<" : inner rtzp "<<rtzp<<"\n"; std::cout << GridLogMessage<<"HDCG::fPcg iteration "<<k<<" : inner rtzp "<<rtzp<<"\n";
// PcgM2(z,p[0]); // PcgM2(z,p[0]);
PcgM2(z,mu); // ADEF-2 this is identity. Axpy possible to eliminate PcgM2(z,mu); // ADEF-2 this is identity. Axpy possible to eliminate
p[peri_kp]=mu; p[peri_kp]=mu;
// Standard search direction p -> z + b p // Standard search direction p -> z + b p
b = (rtzp)/rtz; b = (rtzp)/rtz;
int northog; int northog;
@@ -232,25 +202,8 @@ class TwoLevelCG : public LinearFunction<Field>
if ( rn <= rsq ) { if ( rn <= rsq ) {
HDCGTimer.Stop(); HDCGTimer.Stop();
std::cout<<GridLogMessage<<"HDCG: fPcg converged in "<<k<<" iterations and "<<HDCGTimer.Elapsed()<<std::endl; std::cout<<GridLogMessage<<"HDCG: fPcg converged in "<<k<<" iterations and "<<HDCGTimer.Elapsed()<<std::endl;;
std::cout<<GridLogMessage<<"HDCG: fPcg breakdown"<<std::endl;
auto mspc = [](GridStopWatch &sw, int64_t n) -> double {
return (n > 0) ? sw.useconds() * 1e-3 / n : 0.0;
};
std::cout<<GridLogMessage<<"HDCG: fPcg M3 (fine MVM) "<<M3Timer.Elapsed()
<<" "<<M3Calls<<" calls "<<mspc(M3Timer,M3Calls)<<" ms/call"<<std::endl;
std::cout<<GridLogMessage<<"HDCG: fPcg linalg "<<LinalgTimer.Elapsed()<<std::endl;
std::cout<<GridLogMessage<<"HDCG: fPcg smoother "<<SmoothTimer.Elapsed()
<<" "<<SmoothCalls<<" calls "<<mspc(SmoothTimer,SmoothCalls)<<" ms/call"<<std::endl;
std::cout<<GridLogMessage<<"HDCG: fPcg matrix (in M1) "<<MatrixTimer.Elapsed()
<<" "<<MatrixCalls<<" calls "<<mspc(MatrixTimer,MatrixCalls)<<" ms/call"<<std::endl;
std::cout<<GridLogMessage<<"HDCG: fPcg project "<<ProjectTimer.Elapsed()
<<" "<<ProjectCalls<<" calls "<<mspc(ProjectTimer,ProjectCalls)<<" ms/call"<<std::endl;
std::cout<<GridLogMessage<<"HDCG: fPcg coarse "<<CoarseTimer.Elapsed()
<<" "<<CoarseCalls<<" calls "<<mspc(CoarseTimer,CoarseCalls)<<" ms/call"<<std::endl;
std::cout<<GridLogMessage<<"HDCG: fPcg promote "<<PromoteTimer.Elapsed()
<<" "<<PromoteCalls<<" calls "<<mspc(PromoteTimer,PromoteCalls)<<" ms/call"<<std::endl;
_FineLinop.HermOp(x,mmp[0]); _FineLinop.HermOp(x,mmp[0]);
axpy(tmp,-1.0,src,mmp[0]); axpy(tmp,-1.0,src,mmp[0]);
@@ -317,7 +270,7 @@ class TwoLevelCG : public LinearFunction<Field>
std::vector<RealD> src_nrm(nrhs); std::vector<RealD> src_nrm(nrhs);
for(int rhs=0;rhs<nrhs;rhs++) { for(int rhs=0;rhs<nrhs;rhs++) {
src_nrm[rhs]=norm2(src[rhs]); src_nrm[rhs]=norm2(src[rhs]);
GRID_ASSERT(src_nrm[rhs]!=0.0); assert(src_nrm[rhs]!=0.0);
} }
std::vector<RealD> tn(nrhs); std::vector<RealD> tn(nrhs);
@@ -522,29 +475,35 @@ class TwoLevelADEF2 : public TwoLevelCG<Field>
CoarseField PleftProj(this->coarsegrid); CoarseField PleftProj(this->coarsegrid);
CoarseField PleftMss_proj(this->coarsegrid); CoarseField PleftMss_proj(this->coarsegrid);
this->SmoothTimer.Start(); GridStopWatch SmootherTimer;
GridStopWatch MatrixTimer;
SmootherTimer.Start();
this->_Smoother(in,Min); this->_Smoother(in,Min);
this->SmoothTimer.Stop(); SmootherTimer.Stop();
this->SmoothCalls++;
this->MatrixTimer.Start(); MatrixTimer.Start();
this->_FineLinop.HermOp(Min,out); this->_FineLinop.HermOp(Min,out);
this->MatrixTimer.Stop(); MatrixTimer.Stop();
this->MatrixCalls++;
axpy(tmp,-1.0,out,in); // tmp = in - A Min axpy(tmp,-1.0,out,in); // tmp = in - A Min
this->ProjectTimer.Start(); GridStopWatch ProjTimer;
this->_Aggregates.ProjectToSubspace(PleftProj,tmp); GridStopWatch CoarseTimer;
this->ProjectTimer.Stop(); GridStopWatch PromTimer;
this->ProjectCalls++; ProjTimer.Start();
this->CoarseTimer.Start(); this->_Aggregates.ProjectToSubspace(PleftProj,tmp);
ProjTimer.Stop();
CoarseTimer.Start();
this->_CoarseSolver(PleftProj,PleftMss_proj); // Ass^{-1} [in - A Min]_s this->_CoarseSolver(PleftProj,PleftMss_proj); // Ass^{-1} [in - A Min]_s
this->CoarseTimer.Stop(); CoarseTimer.Stop();
this->CoarseCalls++; PromTimer.Start();
this->PromoteTimer.Start(); this->_Aggregates.PromoteFromSubspace(PleftMss_proj,tmp);// tmp = Q[in - A Min]
this->_Aggregates.PromoteFromSubspace(PleftMss_proj,tmp);// tmp = Q[in - A Min] PromTimer.Stop();
this->PromoteTimer.Stop(); std::cout << GridLogPerformance << "PcgM1 breakdown "<<std::endl;
this->PromoteCalls++; std::cout << GridLogPerformance << "\tSmoother " << SmootherTimer.Elapsed() <<std::endl;
std::cout << GridLogPerformance << "\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
std::cout << GridLogPerformance << "\tProj " << ProjTimer.Elapsed() <<std::endl;
std::cout << GridLogPerformance << "\tCoarse " << CoarseTimer.Elapsed() <<std::endl;
std::cout << GridLogPerformance << "\tProm " << PromTimer.Elapsed() <<std::endl;
axpy(out,1.0,Min,tmp); // Min+tmp axpy(out,1.0,Min,tmp); // Min+tmp
} }
+5 -5
View File
@@ -92,8 +92,8 @@ class TwoLevelCGmrhs
// Vector case // Vector case
virtual void operator() (std::vector<Field> &src, std::vector<Field> &x) virtual void operator() (std::vector<Field> &src, std::vector<Field> &x)
{ {
SolveSingleSystem(src,x); // SolveSingleSystem(src,x);
// SolvePrecBlockCG(src,x); SolvePrecBlockCG(src,x);
} }
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -161,7 +161,7 @@ class TwoLevelCGmrhs
//////////////////////////////////////////// ////////////////////////////////////////////
std::vector<RealD> ssq(nrhs); std::vector<RealD> ssq(nrhs);
for(int rhs=0;rhs<nrhs;rhs++){ for(int rhs=0;rhs<nrhs;rhs++){
ssq[rhs]=norm2(src[rhs]); GRID_ASSERT(ssq[rhs]!=0.0); ssq[rhs]=norm2(src[rhs]); assert(ssq[rhs]!=0.0);
} }
/////////////////////////// ///////////////////////////
@@ -382,7 +382,7 @@ class TwoLevelCGmrhs
} }
HDCGTimer.Stop(); HDCGTimer.Stop();
std::cout<<GridLogMessage<<"HDCG: PrecBlockCGrQ not converged "<<HDCGTimer.Elapsed()<<std::endl; std::cout<<GridLogMessage<<"HDCG: PrecBlockCGrQ not converged "<<HDCGTimer.Elapsed()<<std::endl;
GRID_ASSERT(0); assert(0);
} }
virtual void SolveSingleSystem (std::vector<Field> &src, std::vector<Field> &x) virtual void SolveSingleSystem (std::vector<Field> &src, std::vector<Field> &x)
@@ -415,7 +415,7 @@ class TwoLevelCGmrhs
std::vector<RealD> src_nrm(nrhs); std::vector<RealD> src_nrm(nrhs);
for(int rhs=0;rhs<nrhs;rhs++) { for(int rhs=0;rhs<nrhs;rhs++) {
src_nrm[rhs]=norm2(src[rhs]); src_nrm[rhs]=norm2(src[rhs]);
GRID_ASSERT(src_nrm[rhs]!=0.0); assert(src_nrm[rhs]!=0.0);
} }
std::vector<RealD> tn(nrhs); std::vector<RealD> tn(nrhs);
+4 -4
View File
@@ -47,7 +47,7 @@ class BiCGSTAB : public OperatorFunction<Field>
public: public:
using OperatorFunction<Field>::operator(); using OperatorFunction<Field>::operator();
bool ErrorOnNoConverge; // throw an GRID_ASSERT when the CG fails to converge. bool ErrorOnNoConverge; // throw an assert when the CG fails to converge.
// Defaults true. // Defaults true.
RealD Tolerance; RealD Tolerance;
Integer MaxIterations; Integer MaxIterations;
@@ -77,7 +77,7 @@ class BiCGSTAB : public OperatorFunction<Field>
// Initial residual computation & set up // Initial residual computation & set up
RealD guess = norm2(psi); RealD guess = norm2(psi);
GRID_ASSERT(std::isnan(guess) == 0); assert(std::isnan(guess) == 0);
Linop.Op(psi, v); Linop.Op(psi, v);
b = norm2(v); b = norm2(v);
@@ -214,7 +214,7 @@ class BiCGSTAB : public OperatorFunction<Field>
std::cout << GridLogMessage << "\tAxpyNorm " << AxpyNormTimer.Elapsed() << std::endl; std::cout << GridLogMessage << "\tAxpyNorm " << AxpyNormTimer.Elapsed() << std::endl;
std::cout << GridLogMessage << "\tLinearComb " << LinearCombTimer.Elapsed() << std::endl; std::cout << GridLogMessage << "\tLinearComb " << LinearCombTimer.Elapsed() << std::endl;
if(ErrorOnNoConverge){ GRID_ASSERT(true_residual / Tolerance < 10000.0); } if(ErrorOnNoConverge){ assert(true_residual / Tolerance < 10000.0); }
IterationsToComplete = k; IterationsToComplete = k;
@@ -224,7 +224,7 @@ class BiCGSTAB : public OperatorFunction<Field>
std::cout << GridLogMessage << "BiCGSTAB did NOT converge" << std::endl; std::cout << GridLogMessage << "BiCGSTAB did NOT converge" << std::endl;
if(ErrorOnNoConverge){ GRID_ASSERT(0); } if(ErrorOnNoConverge){ assert(0); }
IterationsToComplete = k; IterationsToComplete = k;
} }
}; };
@@ -98,7 +98,7 @@ class BlockConjugateGradient : public OperatorFunction<Field> {
int Nblock; int Nblock;
BlockCGtype CGtype; BlockCGtype CGtype;
bool ErrorOnNoConverge; // throw an GRID_ASSERT when the CG fails to converge. bool ErrorOnNoConverge; // throw an assert when the CG fails to converge.
// Defaults true. // Defaults true.
RealD Tolerance; RealD Tolerance;
Integer MaxIterations; Integer MaxIterations;
@@ -201,7 +201,7 @@ void operator()(LinearOperatorBase<Field> &Linop, const Field &Src, Field &Psi)
} else if (CGtype == CGmultiRHS ) { } else if (CGtype == CGmultiRHS ) {
CGmultiRHSsolve(Linop,Src,Psi); CGmultiRHSsolve(Linop,Src,Psi);
} else { } else {
GRID_ASSERT(0); assert(0);
} }
} }
virtual void operator()(LinearOperatorBase<Field> &Linop, const std::vector<Field> &Src, std::vector<Field> &Psi) virtual void operator()(LinearOperatorBase<Field> &Linop, const std::vector<Field> &Src, std::vector<Field> &Psi)
@@ -209,7 +209,7 @@ virtual void operator()(LinearOperatorBase<Field> &Linop, const std::vector<Fiel
if ( CGtype == BlockCGrQVec ) { if ( CGtype == BlockCGrQVec ) {
BlockCGrQsolveVec(Linop,Src,Psi); BlockCGrQsolveVec(Linop,Src,Psi);
} else { } else {
GRID_ASSERT(0); assert(0);
} }
} }
@@ -259,10 +259,10 @@ void BlockCGrQsolve(LinearOperatorBase<Field> &Linop, const Field &B, Field &X)
for(int b=0;b<Nblock;b++) std::cout << "src["<<b<<"]" << ssq[b] <<std::endl; for(int b=0;b<Nblock;b++) std::cout << "src["<<b<<"]" << ssq[b] <<std::endl;
sliceNorm(residuals,B,Orthog); sliceNorm(residuals,B,Orthog);
for(int b=0;b<Nblock;b++){ GRID_ASSERT(std::isnan(residuals[b])==0); } for(int b=0;b<Nblock;b++){ assert(std::isnan(residuals[b])==0); }
sliceNorm(residuals,X,Orthog); sliceNorm(residuals,X,Orthog);
for(int b=0;b<Nblock;b++){ GRID_ASSERT(std::isnan(residuals[b])==0); } for(int b=0;b<Nblock;b++){ assert(std::isnan(residuals[b])==0); }
/************************************************************************ /************************************************************************
* Block conjugate gradient rQ (Sebastien Birk Thesis, after Dubrulle 2001) * Block conjugate gradient rQ (Sebastien Birk Thesis, after Dubrulle 2001)
@@ -402,7 +402,7 @@ void BlockCGrQsolve(LinearOperatorBase<Field> &Linop, const Field &B, Field &X)
std::cout << GridLogMessage << "BlockConjugateGradient(rQ) did NOT converge "<<k<<" / "<<MaxIterations std::cout << GridLogMessage << "BlockConjugateGradient(rQ) did NOT converge "<<k<<" / "<<MaxIterations
<<" residual "<< std::sqrt(max_resid)<< std::endl; <<" residual "<< std::sqrt(max_resid)<< std::endl;
if (ErrorOnNoConverge) GRID_ASSERT(0); if (ErrorOnNoConverge) assert(0);
IterationsToComplete = k; IterationsToComplete = k;
} }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@@ -438,10 +438,10 @@ void CGmultiRHSsolve(LinearOperatorBase<Field> &Linop, const Field &Src, Field &
for(int b=0;b<Nblock;b++) sssum+=ssq[b]; for(int b=0;b<Nblock;b++) sssum+=ssq[b];
sliceNorm(residuals,Src,Orthog); sliceNorm(residuals,Src,Orthog);
for(int b=0;b<Nblock;b++){ GRID_ASSERT(std::isnan(residuals[b])==0); } for(int b=0;b<Nblock;b++){ assert(std::isnan(residuals[b])==0); }
sliceNorm(residuals,Psi,Orthog); sliceNorm(residuals,Psi,Orthog);
for(int b=0;b<Nblock;b++){ GRID_ASSERT(std::isnan(residuals[b])==0); } for(int b=0;b<Nblock;b++){ assert(std::isnan(residuals[b])==0); }
// Initial search dir is guess // Initial search dir is guess
Linop.HermOp(Psi, AP); Linop.HermOp(Psi, AP);
@@ -540,7 +540,7 @@ void CGmultiRHSsolve(LinearOperatorBase<Field> &Linop, const Field &Src, Field &
} }
std::cout << GridLogMessage << "MultiRHSConjugateGradient did NOT converge" << std::endl; std::cout << GridLogMessage << "MultiRHSConjugateGradient did NOT converge" << std::endl;
if (ErrorOnNoConverge) GRID_ASSERT(0); if (ErrorOnNoConverge) assert(0);
IterationsToComplete = k; IterationsToComplete = k;
} }
@@ -554,7 +554,7 @@ void CGmultiRHSsolve(LinearOperatorBase<Field> &Linop, const Field &Src, Field &
void BlockCGrQsolveVec(LinearOperatorBase<Field> &Linop, const std::vector<Field> &B, std::vector<Field> &X) void BlockCGrQsolveVec(LinearOperatorBase<Field> &Linop, const std::vector<Field> &B, std::vector<Field> &X)
{ {
Nblock = B.size(); Nblock = B.size();
GRID_ASSERT(Nblock == X.size()); assert(Nblock == X.size());
std::cout<<GridLogMessage<<" Block Conjugate Gradient Vec rQ : Nblock "<<Nblock<<std::endl; std::cout<<GridLogMessage<<" Block Conjugate Gradient Vec rQ : Nblock "<<Nblock<<std::endl;
@@ -594,10 +594,10 @@ void BlockCGrQsolveVec(LinearOperatorBase<Field> &Linop, const std::vector<Field
for(int b=0;b<Nblock;b++) sssum+=ssq[b]; for(int b=0;b<Nblock;b++) sssum+=ssq[b];
for(int b=0;b<Nblock;b++){ residuals[b] = norm2(B[b]);} for(int b=0;b<Nblock;b++){ residuals[b] = norm2(B[b]);}
for(int b=0;b<Nblock;b++){ GRID_ASSERT(std::isnan(residuals[b])==0); } for(int b=0;b<Nblock;b++){ assert(std::isnan(residuals[b])==0); }
for(int b=0;b<Nblock;b++){ residuals[b] = norm2(X[b]);} for(int b=0;b<Nblock;b++){ residuals[b] = norm2(X[b]);}
for(int b=0;b<Nblock;b++){ GRID_ASSERT(std::isnan(residuals[b])==0); } for(int b=0;b<Nblock;b++){ assert(std::isnan(residuals[b])==0); }
/************************************************************************ /************************************************************************
* Block conjugate gradient rQ (Sebastien Birk Thesis, after Dubrulle 2001) * Block conjugate gradient rQ (Sebastien Birk Thesis, after Dubrulle 2001)
@@ -731,7 +731,7 @@ void BlockCGrQsolveVec(LinearOperatorBase<Field> &Linop, const std::vector<Field
} }
std::cout << GridLogMessage << "BlockConjugateGradient(rQ) did NOT converge" << std::endl; std::cout << GridLogMessage << "BlockConjugateGradient(rQ) did NOT converge" << std::endl;
if (ErrorOnNoConverge) GRID_ASSERT(0); if (ErrorOnNoConverge) assert(0);
IterationsToComplete = k; IterationsToComplete = k;
} }
@@ -36,7 +36,7 @@ class CommunicationAvoidingGeneralisedMinimalResidual : public OperatorFunction<
public: public:
using OperatorFunction<Field>::operator(); using OperatorFunction<Field>::operator();
bool ErrorOnNoConverge; // Throw an GRID_ASSERT when CAGMRES fails to converge, bool ErrorOnNoConverge; // Throw an assert when CAGMRES fails to converge,
// defaults to true // defaults to true
RealD Tolerance; RealD Tolerance;
@@ -82,7 +82,7 @@ class CommunicationAvoidingGeneralisedMinimalResidual : public OperatorFunction<
conformable(psi, src); conformable(psi, src);
RealD guess = norm2(psi); RealD guess = norm2(psi);
GRID_ASSERT(std::isnan(guess) == 0); assert(std::isnan(guess) == 0);
RealD cp; RealD cp;
RealD ssq = norm2(src); RealD ssq = norm2(src);
@@ -137,7 +137,7 @@ class CommunicationAvoidingGeneralisedMinimalResidual : public OperatorFunction<
std::cout << GridLogMessage << "CommunicationAvoidingGeneralisedMinimalResidual did NOT converge" << std::endl; std::cout << GridLogMessage << "CommunicationAvoidingGeneralisedMinimalResidual did NOT converge" << std::endl;
if (ErrorOnNoConverge) if (ErrorOnNoConverge)
GRID_ASSERT(0); assert(0);
} }
RealD outerLoopBody(LinearOperatorBase<Field> &LinOp, const Field &src, Field &psi, RealD rsq) { RealD outerLoopBody(LinearOperatorBase<Field> &LinOp, const Field &src, Field &psi, RealD rsq) {
@@ -185,7 +185,7 @@ class CommunicationAvoidingGeneralisedMinimalResidual : public OperatorFunction<
} }
} }
GRID_ASSERT(0); // Never reached assert(0); // Never reached
return cp; return cp;
} }
+11 -15
View File
@@ -45,7 +45,7 @@ public:
using OperatorFunction<Field>::operator(); using OperatorFunction<Field>::operator();
bool ErrorOnNoConverge; // throw an GRID_ASSERT when the CG fails to converge. bool ErrorOnNoConverge; // throw an assert when the CG fails to converge.
// Defaults true. // Defaults true.
RealD Tolerance; RealD Tolerance;
Integer MaxIterations; Integer MaxIterations;
@@ -94,7 +94,7 @@ public:
ssq = norm2(src); ssq = norm2(src);
RealD guess = norm2(psi); RealD guess = norm2(psi);
NormTimer.Stop(); NormTimer.Stop();
GRID_ASSERT(std::isnan(guess) == 0); assert(std::isnan(guess) == 0);
AssignTimer.Start(); AssignTimer.Start();
if ( guess == 0.0 ) { if ( guess == 0.0 ) {
r = src; r = src;
@@ -212,21 +212,17 @@ public:
<< "\tTarget " << Tolerance << std::endl; << "\tTarget " << Tolerance << std::endl;
// std::cout << GridLogMessage << "\tPreamble " << PreambleTimer.Elapsed() <<std::endl; // std::cout << GridLogMessage << "\tPreamble " << PreambleTimer.Elapsed() <<std::endl;
std::cout << GridLogMessage << "\tPreamble " << PreambleTimer.Elapsed() <<std::endl; std::cout << GridLogMessage << "\tSolver Elapsed " << SolverTimer.Elapsed() <<std::endl;
std::cout << GridLogMessage << "\tConstruct " << ConstructTimer.Elapsed() <<std::endl; std::cout << GridLogPerformance << "Time breakdown "<<std::endl;
std::cout << GridLogMessage << "\tNorm " << NormTimer.Elapsed() <<std::endl; std::cout << GridLogPerformance << "\tMatrix " << MatrixTimer.Elapsed() <<std::endl;
std::cout << GridLogMessage << "\tAssign " << AssignTimer.Elapsed() <<std::endl; std::cout << GridLogPerformance << "\tLinalg " << LinalgTimer.Elapsed() <<std::endl;
std::cout << GridLogMessage << "\tSolver " << SolverTimer.Elapsed() <<std::endl; std::cout << GridLogPerformance << "\t\tInner " << InnerTimer.Elapsed() <<std::endl;
std::cout << GridLogMessage << "Solver breakdown "<<std::endl; std::cout << GridLogPerformance << "\t\tAxpyNorm " << AxpyNormTimer.Elapsed() <<std::endl;
std::cout << GridLogMessage << "\tMatrix " << MatrixTimer.Elapsed() <<std::endl; std::cout << GridLogPerformance << "\t\tLinearComb " << LinearCombTimer.Elapsed() <<std::endl;
std::cout << GridLogMessage << "\tLinalg " << LinalgTimer.Elapsed() <<std::endl;
std::cout << GridLogMessage << "\t\tInner " << InnerTimer.Elapsed() <<std::endl;
std::cout << GridLogMessage << "\t\tAxpyNorm " << AxpyNormTimer.Elapsed() <<std::endl;
std::cout << GridLogMessage << "\t\tLinearComb " << LinearCombTimer.Elapsed() <<std::endl;
std::cout << GridLogDebug << "\tMobius flop rate " << DwfFlops/ usecs<< " Gflops " <<std::endl; std::cout << GridLogDebug << "\tMobius flop rate " << DwfFlops/ usecs<< " Gflops " <<std::endl;
if (ErrorOnNoConverge) GRID_ASSERT(true_residual / Tolerance < 10000.0); if (ErrorOnNoConverge) assert(true_residual / Tolerance < 10000.0);
IterationsToComplete = k; IterationsToComplete = k;
TrueResidual = true_residual; TrueResidual = true_residual;
@@ -255,7 +251,7 @@ public:
std::cout << GridLogPerformance << "\t\tAxpyNorm " << AxpyNormTimer.Elapsed() <<std::endl; std::cout << GridLogPerformance << "\t\tAxpyNorm " << AxpyNormTimer.Elapsed() <<std::endl;
std::cout << GridLogPerformance << "\t\tLinearComb " << LinearCombTimer.Elapsed() <<std::endl; std::cout << GridLogPerformance << "\t\tLinearComb " << LinearCombTimer.Elapsed() <<std::endl;
if (ErrorOnNoConverge) GRID_ASSERT(0); if (ErrorOnNoConverge) assert(0);
IterationsToComplete = k; IterationsToComplete = k;
} }
@@ -77,7 +77,7 @@ public:
} }
void operator() (const std::vector<FieldD> &src_d_in, std::vector<FieldD> &sol_d){ void operator() (const std::vector<FieldD> &src_d_in, std::vector<FieldD> &sol_d){
GRID_ASSERT(src_d_in.size() == sol_d.size()); assert(src_d_in.size() == sol_d.size());
int NBatch = src_d_in.size(); int NBatch = src_d_in.size();
std::cout << GridLogMessage << "NBatch = " << NBatch << std::endl; std::cout << GridLogMessage << "NBatch = " << NBatch << std::endl;
@@ -98,9 +98,9 @@ public:
std::vector<RealD> alpha(nshift,1.0); std::vector<RealD> alpha(nshift,1.0);
std::vector<Field> ps(nshift,grid);// Search directions std::vector<Field> ps(nshift,grid);// Search directions
GRID_ASSERT(psi.size()==nshift); assert(psi.size()==nshift);
GRID_ASSERT(mass.size()==nshift); assert(mass.size()==nshift);
GRID_ASSERT(mresidual.size()==nshift); assert(mresidual.size()==nshift);
// remove dynamic sized arrays on stack; 2d is a pain with vector // remove dynamic sized arrays on stack; 2d is a pain with vector
std::vector<RealD> bs(nshift); std::vector<RealD> bs(nshift);
@@ -122,7 +122,7 @@ public:
// Check lightest mass // Check lightest mass
for(int s=0;s<nshift;s++){ for(int s=0;s<nshift;s++){
GRID_ASSERT( mass[s]>= mass[primary] ); assert( mass[s]>= mass[primary] );
converged[s]=0; converged[s]=0;
} }
@@ -338,7 +338,7 @@ public:
} }
// ugly hack // ugly hack
std::cout<<GridLogMessage<<"CG multi shift did not converge"<<std::endl; std::cout<<GridLogMessage<<"CG multi shift did not converge"<<std::endl;
// GRID_ASSERT(0); // assert(0);
} }
}; };
@@ -118,9 +118,9 @@ public:
FieldF r_f(SinglePrecGrid); FieldF r_f(SinglePrecGrid);
FieldD mmp_d(DoublePrecGrid); FieldD mmp_d(DoublePrecGrid);
GRID_ASSERT(psi_d.size()==nshift); assert(psi_d.size()==nshift);
GRID_ASSERT(mass.size()==nshift); assert(mass.size()==nshift);
GRID_ASSERT(mresidual.size()==nshift); assert(mresidual.size()==nshift);
// dynamic sized arrays on stack; 2d is a pain with vector // dynamic sized arrays on stack; 2d is a pain with vector
std::vector<RealD> bs(nshift); std::vector<RealD> bs(nshift);
@@ -141,7 +141,7 @@ public:
// Check lightest mass // Check lightest mass
for(int s=0;s<nshift;s++){ for(int s=0;s<nshift;s++){
GRID_ASSERT( mass[s]>= mass[primary] ); assert( mass[s]>= mass[primary] );
converged[s]=0; converged[s]=0;
} }
@@ -179,7 +179,7 @@ public:
Linop_d.HermOpAndNorm(p_d,mmp_d,d,qq); // mmp = MdagM p d=real(dot(p, mmp)), qq=norm2(mmp) Linop_d.HermOpAndNorm(p_d,mmp_d,d,qq); // mmp = MdagM p d=real(dot(p, mmp)), qq=norm2(mmp)
tmp_d = tmp_d - mmp_d; tmp_d = tmp_d - mmp_d;
std::cout << " Testing operators match "<<norm2(mmp_d)<<" f "<<norm2(mmp_f)<<" diff "<< norm2(tmp_d)<<std::endl; std::cout << " Testing operators match "<<norm2(mmp_d)<<" f "<<norm2(mmp_f)<<" diff "<< norm2(tmp_d)<<std::endl;
// GRID_ASSERT(norm2(tmp_d)< 1.0e-4); // assert(norm2(tmp_d)< 1.0e-4);
axpy(mmp_d,mass[0],p_d,mmp_d); axpy(mmp_d,mass[0],p_d,mmp_d);
RealD rn = norm2(p_d); RealD rn = norm2(p_d);
@@ -365,7 +365,7 @@ public:
} }
std::cout<<GridLogMessage<<"CG multi shift did not converge"<<std::endl; std::cout<<GridLogMessage<<"CG multi shift did not converge"<<std::endl;
GRID_ASSERT(0); assert(0);
} }
}; };
@@ -48,12 +48,12 @@ public:
ShiftedLinop(LinearOperatorBase<Field> &_linop_base, RealD _shift): linop_base(_linop_base), shift(_shift){} ShiftedLinop(LinearOperatorBase<Field> &_linop_base, RealD _shift): linop_base(_linop_base), shift(_shift){}
void OpDiag (const Field &in, Field &out){ GRID_ASSERT(0); } void OpDiag (const Field &in, Field &out){ assert(0); }
void OpDir (const Field &in, Field &out,int dir,int disp){ GRID_ASSERT(0); } void OpDir (const Field &in, Field &out,int dir,int disp){ assert(0); }
void OpDirAll (const Field &in, std::vector<Field> &out){ GRID_ASSERT(0); } void OpDirAll (const Field &in, std::vector<Field> &out){ assert(0); }
void Op (const Field &in, Field &out){ GRID_ASSERT(0); } void Op (const Field &in, Field &out){ assert(0); }
void AdjOp (const Field &in, Field &out){ GRID_ASSERT(0); } void AdjOp (const Field &in, Field &out){ assert(0); }
void HermOp(const Field &in, Field &out){ void HermOp(const Field &in, Field &out){
linop_base.HermOp(in, out); linop_base.HermOp(in, out);
@@ -151,9 +151,9 @@ public:
FieldD r_d(DoublePrecGrid); FieldD r_d(DoublePrecGrid);
FieldD mmp_d(DoublePrecGrid); FieldD mmp_d(DoublePrecGrid);
GRID_ASSERT(psi_d.size()==nshift); assert(psi_d.size()==nshift);
GRID_ASSERT(mass.size()==nshift); assert(mass.size()==nshift);
GRID_ASSERT(mresidual.size()==nshift); assert(mresidual.size()==nshift);
// dynamic sized arrays on stack; 2d is a pain with vector // dynamic sized arrays on stack; 2d is a pain with vector
std::vector<RealD> bs(nshift); std::vector<RealD> bs(nshift);
@@ -174,7 +174,7 @@ public:
// Check lightest mass // Check lightest mass
for(int s=0;s<nshift;s++){ for(int s=0;s<nshift;s++){
GRID_ASSERT( mass[s]>= mass[primary] ); assert( mass[s]>= mass[primary] );
converged[s]=0; converged[s]=0;
} }
@@ -211,7 +211,7 @@ public:
Linop_d.HermOpAndNorm(p_d,mmp_d,d,qq); // mmp = MdagM p d=real(dot(p, mmp)), qq=norm2(mmp) Linop_d.HermOpAndNorm(p_d,mmp_d,d,qq); // mmp = MdagM p d=real(dot(p, mmp)), qq=norm2(mmp)
tmp_d = tmp_d - mmp_d; tmp_d = tmp_d - mmp_d;
std::cout << " Testing operators match "<<norm2(mmp_d)<<" f "<<norm2(mmp_f)<<" diff "<< norm2(tmp_d)<<std::endl; std::cout << " Testing operators match "<<norm2(mmp_d)<<" f "<<norm2(mmp_f)<<" diff "<< norm2(tmp_d)<<std::endl;
GRID_ASSERT(norm2(tmp_d)< 1.0); assert(norm2(tmp_d)< 1.0);
axpy(mmp_d,mass[0],p_d,mmp_d); axpy(mmp_d,mass[0],p_d,mmp_d);
RealD rn = norm2(p_d); RealD rn = norm2(p_d);
@@ -408,7 +408,7 @@ public:
} }
std::cout<<GridLogMessage<<"CG multi shift did not converge"<<std::endl; std::cout<<GridLogMessage<<"CG multi shift did not converge"<<std::endl;
GRID_ASSERT(0); assert(0);
} }
}; };
@@ -35,7 +35,7 @@ template<class FieldD,class FieldF,
typename std::enable_if< getPrecision<FieldF>::value == 1, int>::type = 0> typename std::enable_if< getPrecision<FieldF>::value == 1, int>::type = 0>
class ConjugateGradientReliableUpdate : public LinearFunction<FieldD> { class ConjugateGradientReliableUpdate : public LinearFunction<FieldD> {
public: public:
bool ErrorOnNoConverge; // throw an GRID_ASSERT when the CG fails to converge. bool ErrorOnNoConverge; // throw an assert when the CG fails to converge.
// Defaults true. // Defaults true.
RealD Tolerance; RealD Tolerance;
Integer MaxIterations; Integer MaxIterations;
@@ -66,7 +66,7 @@ public:
DoFinalCleanup(true), DoFinalCleanup(true),
Linop_fallback(NULL) Linop_fallback(NULL)
{ {
GRID_ASSERT(Delta > 0. && Delta < 1. && "Expect 0 < Delta < 1"); assert(Delta > 0. && Delta < 1. && "Expect 0 < Delta < 1");
}; };
void setFallbackLinop(LinearOperatorBase<FieldF> &_Linop_fallback, const RealD _fallback_transition_tol){ void setFallbackLinop(LinearOperatorBase<FieldF> &_Linop_fallback, const RealD _fallback_transition_tol){
@@ -90,7 +90,7 @@ public:
// Initial residual computation & set up // Initial residual computation & set up
RealD guess = norm2(psi); RealD guess = norm2(psi);
GRID_ASSERT(std::isnan(guess) == 0); assert(std::isnan(guess) == 0);
Linop_d.HermOpAndNorm(psi, mmp, d, b); Linop_d.HermOpAndNorm(psi, mmp, d, b);
@@ -217,7 +217,7 @@ public:
CG(Linop_d,src,psi); CG(Linop_d,src,psi);
IterationsToCleanup = CG.IterationsToComplete; IterationsToCleanup = CG.IterationsToComplete;
} }
else if (ErrorOnNoConverge) GRID_ASSERT(true_residual / Tolerance < 10000.0); else if (ErrorOnNoConverge) assert(true_residual / Tolerance < 10000.0);
std::cout << GridLogMessage << "ConjugateGradientReliableUpdate complete.\n"; std::cout << GridLogMessage << "ConjugateGradientReliableUpdate complete.\n";
return; return;
@@ -263,7 +263,7 @@ public:
std::cout << GridLogMessage << "ConjugateGradientReliableUpdate did NOT converge" std::cout << GridLogMessage << "ConjugateGradientReliableUpdate did NOT converge"
<< std::endl; << std::endl;
if (ErrorOnNoConverge) GRID_ASSERT(0); if (ErrorOnNoConverge) assert(0);
IterationsToComplete = k; IterationsToComplete = k;
ReliableUpdatesPerformed = l; ReliableUpdatesPerformed = l;
} }
@@ -106,7 +106,7 @@ public:
} }
std::cout<<GridLogMessage<<"ConjugateResidual did NOT converge"<<std::endl; std::cout<<GridLogMessage<<"ConjugateResidual did NOT converge"<<std::endl;
GRID_ASSERT(0); assert(0);
} }
}; };
NAMESPACE_END(Grid); NAMESPACE_END(Grid);
@@ -36,7 +36,7 @@ class FlexibleCommunicationAvoidingGeneralisedMinimalResidual : public OperatorF
public: public:
using OperatorFunction<Field>::operator(); using OperatorFunction<Field>::operator();
bool ErrorOnNoConverge; // Throw an GRID_ASSERT when FCAGMRES fails to converge, bool ErrorOnNoConverge; // Throw an assert when FCAGMRES fails to converge,
// defaults to true // defaults to true
RealD Tolerance; RealD Tolerance;
@@ -87,7 +87,7 @@ class FlexibleCommunicationAvoidingGeneralisedMinimalResidual : public OperatorF
conformable(psi, src); conformable(psi, src);
RealD guess = norm2(psi); RealD guess = norm2(psi);
GRID_ASSERT(std::isnan(guess) == 0); assert(std::isnan(guess) == 0);
RealD cp; RealD cp;
RealD ssq = norm2(src); RealD ssq = norm2(src);
@@ -144,7 +144,7 @@ class FlexibleCommunicationAvoidingGeneralisedMinimalResidual : public OperatorF
std::cout << GridLogMessage << "FlexibleCommunicationAvoidingGeneralisedMinimalResidual did NOT converge" << std::endl; std::cout << GridLogMessage << "FlexibleCommunicationAvoidingGeneralisedMinimalResidual did NOT converge" << std::endl;
if (ErrorOnNoConverge) if (ErrorOnNoConverge)
GRID_ASSERT(0); assert(0);
} }
RealD outerLoopBody(LinearOperatorBase<Field> &LinOp, const Field &src, Field &psi, RealD rsq) { RealD outerLoopBody(LinearOperatorBase<Field> &LinOp, const Field &src, Field &psi, RealD rsq) {
@@ -191,7 +191,7 @@ class FlexibleCommunicationAvoidingGeneralisedMinimalResidual : public OperatorF
} }
} }
GRID_ASSERT(0); // Never reached assert(0); // Never reached
return cp; return cp;
} }
@@ -36,7 +36,7 @@ class FlexibleGeneralisedMinimalResidual : public OperatorFunction<Field> {
public: public:
using OperatorFunction<Field>::operator(); using OperatorFunction<Field>::operator();
bool ErrorOnNoConverge; // Throw an GRID_ASSERT when FGMRES fails to converge, bool ErrorOnNoConverge; // Throw an assert when FGMRES fails to converge,
// defaults to true // defaults to true
RealD Tolerance; RealD Tolerance;
@@ -85,7 +85,7 @@ class FlexibleGeneralisedMinimalResidual : public OperatorFunction<Field> {
conformable(psi, src); conformable(psi, src);
RealD guess = norm2(psi); RealD guess = norm2(psi);
GRID_ASSERT(std::isnan(guess) == 0); assert(std::isnan(guess) == 0);
RealD cp; RealD cp;
RealD ssq = norm2(src); RealD ssq = norm2(src);
@@ -142,7 +142,7 @@ class FlexibleGeneralisedMinimalResidual : public OperatorFunction<Field> {
std::cout << GridLogMessage << "FlexibleGeneralisedMinimalResidual did NOT converge" << std::endl; std::cout << GridLogMessage << "FlexibleGeneralisedMinimalResidual did NOT converge" << std::endl;
if (ErrorOnNoConverge) if (ErrorOnNoConverge)
GRID_ASSERT(0); assert(0);
} }
RealD outerLoopBody(LinearOperatorBase<Field> &LinOp, const Field &src, Field &psi, RealD rsq) { RealD outerLoopBody(LinearOperatorBase<Field> &LinOp, const Field &src, Field &psi, RealD rsq) {
@@ -189,7 +189,7 @@ class FlexibleGeneralisedMinimalResidual : public OperatorFunction<Field> {
} }
} }
GRID_ASSERT(0); // Never reached assert(0); // Never reached
return cp; return cp;
} }
@@ -1,59 +0,0 @@
/*************************************************************************************
Grid physics library, www.github.com/paboyle/Grid
Source file: ./Grid/algorithms/iterative/GCRCoefficients.h
Copyright (C) 2026
Author: Peter Boyle <pboyle@bnl.gov>
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 */
#pragma once
NAMESPACE_BEGIN(Grid);
//////////////////////////////////////////////////////////////////////////////
// Recorded GCR coefficients: per-step means over calls of the step length
// a_k and the orthogonalisation coefficients b_kj (already scaled and
// signed as applied: p_{k+1} = r + sum_j b_kj p_{k-j}).
//////////////////////////////////////////////////////////////////////////////
struct GCRCoefficients {
int mmax = 0;
std::vector<ComplexD> a_sum; // [k]
std::vector<int> a_n;
std::vector<std::vector<ComplexD> > b_sum; // [k][j]
std::vector<std::vector<int> > b_n;
void RecordA(int k, ComplexD a){
if ( (int)a_sum.size() <= k ) { a_sum.resize(k+1,ComplexD(0.0)); a_n.resize(k+1,0); }
a_sum[k] += a; a_n[k]++;
}
void RecordB(int k, const std::vector<ComplexD> &b){
if ( (int)b_sum.size() <= k ) { b_sum.resize(k+1); b_n.resize(k+1); }
if ( b_sum[k].size() < b.size() ) { b_sum[k].resize(b.size(),ComplexD(0.0)); b_n[k].resize(b.size(),0); }
for(int j=0;j<(int)b.size();j++){ b_sum[k][j] += b[j]; b_n[k][j]++; }
}
int Steps(void) const { return a_sum.size(); }
int Calls(void) const { return a_n.size() ? a_n[0] : 0; }
ComplexD A(int k) const { return a_sum[k]/(double)a_n[k]; }
int NB(int k) const { return (k<(int)b_sum.size()) ? b_sum[k].size() : 0; }
ComplexD B(int k,int j) const { return b_sum[k][j]/(double)b_n[k][j]; }
void Report(const std::string &name) const {
std::cout << GridLogMessage << "GCRCoefficients " << name << ": " << Calls() << " calls, " << Steps() << " steps, mmax " << mmax << std::endl;
for(int k=0;k<Steps();k++){
std::cout << GridLogMessage << " step " << k << " a=(" << real(A(k)) << "," << imag(A(k)) << ")";
for(int j=0;j<NB(k);j++) std::cout << " b[" << j << "]=(" << real(B(k,j)) << "," << imag(B(k,j)) << ")";
std::cout << std::endl;
}
}
};
NAMESPACE_END(Grid);
@@ -36,7 +36,7 @@ class GeneralisedMinimalResidual : public OperatorFunction<Field> {
public: public:
using OperatorFunction<Field>::operator(); using OperatorFunction<Field>::operator();
bool ErrorOnNoConverge; // Throw an GRID_ASSERT when GMRES fails to converge, bool ErrorOnNoConverge; // Throw an assert when GMRES fails to converge,
// defaults to true // defaults to true
RealD Tolerance; RealD Tolerance;
@@ -80,7 +80,7 @@ class GeneralisedMinimalResidual : public OperatorFunction<Field> {
conformable(psi, src); conformable(psi, src);
RealD guess = norm2(psi); RealD guess = norm2(psi);
GRID_ASSERT(std::isnan(guess) == 0); assert(std::isnan(guess) == 0);
RealD cp; RealD cp;
RealD ssq = norm2(src); RealD ssq = norm2(src);
@@ -135,7 +135,7 @@ class GeneralisedMinimalResidual : public OperatorFunction<Field> {
std::cout << GridLogMessage << "GeneralisedMinimalResidual did NOT converge" << std::endl; std::cout << GridLogMessage << "GeneralisedMinimalResidual did NOT converge" << std::endl;
if (ErrorOnNoConverge) if (ErrorOnNoConverge)
GRID_ASSERT(0); assert(0);
} }
RealD outerLoopBody(LinearOperatorBase<Field> &LinOp, const Field &src, Field &psi, RealD rsq) { RealD outerLoopBody(LinearOperatorBase<Field> &LinOp, const Field &src, Field &psi, RealD rsq) {
@@ -181,7 +181,7 @@ class GeneralisedMinimalResidual : public OperatorFunction<Field> {
} }
} }
GRID_ASSERT(0); // Never reached assert(0); // Never reached
return cp; return cp;
} }
@@ -175,7 +175,7 @@ public:
eresid(_eresid), MaxIter(_MaxIter), eresid(_eresid), MaxIter(_MaxIter),
diagonalisation(_diagonalisation),split_test(0), diagonalisation(_diagonalisation),split_test(0),
Nevec_acc(_Nu) Nevec_acc(_Nu)
{ GRID_ASSERT( (Nk%Nu==0) && (Nm%Nu==0) ); }; { assert( (Nk%Nu==0) && (Nm%Nu==0) ); };
//////////////////////////////// ////////////////////////////////
// Helpers // Helpers
@@ -206,7 +206,7 @@ public:
Glog<<"orthogonalize after: "<<j<<" of "<<k<<" "<< ip <<std::endl; Glog<<"orthogonalize after: "<<j<<" of "<<k<<" "<< ip <<std::endl;
} }
} }
GRID_ASSERT(normalize(w,if_print) != 0); assert(normalize(w,if_print) != 0);
} }
void reorthogonalize(Field& w, std::vector<Field>& evec, int k) void reorthogonalize(Field& w, std::vector<Field>& evec, int k)
{ {
@@ -225,7 +225,7 @@ public:
w[i] = w[i] - ip * evec[j]; w[i] = w[i] - ip * evec[j];
}} }}
for(int i=0; i<_Nu; ++i) for(int i=0; i<_Nu; ++i)
GRID_ASSERT(normalize(w[i],if_print) !=0); assert(normalize(w[i],if_print) !=0);
} }
@@ -244,7 +244,7 @@ public:
const uint64_t sites = grid->lSites(); const uint64_t sites = grid->lSites();
int Nbatch = R/Nevec_acc; int Nbatch = R/Nevec_acc;
GRID_ASSERT( R%Nevec_acc == 0 ); assert( R%Nevec_acc == 0 );
// Glog << "nBatch, Nevec_acc, R, Nu = " // Glog << "nBatch, Nevec_acc, R, Nu = "
// << Nbatch << "," << Nevec_acc << "," << R << "," << Nu << std::endl; // << Nbatch << "," << Nevec_acc << "," << R << "," << Nu << std::endl;
@@ -302,7 +302,7 @@ public:
} }
} }
for (int i=0; i<Nu; ++i) { for (int i=0; i<Nu; ++i) {
GRID_ASSERT(normalize(w[i],do_print)!=0); assert(normalize(w[i],do_print)!=0);
} }
Glog << "cuBLAS Zgemm done"<< std::endl; Glog << "cuBLAS Zgemm done"<< std::endl;
@@ -374,8 +374,8 @@ cudaStat = cudaMallocManaged((void **)&evec_acc, Nevec_acc*sites*12*sizeof(CUDA_
{ {
std::string fname = std::string(cname+"::calc_irbl()"); std::string fname = std::string(cname+"::calc_irbl()");
GridBase *grid = evec[0].Grid(); GridBase *grid = evec[0].Grid();
GRID_ASSERT(grid == src[0].Grid()); assert(grid == src[0].Grid());
GRID_ASSERT( Nu = src.size() ); assert( Nu = src.size() );
Glog << std::string(74,'*') << std::endl; Glog << std::string(74,'*') << std::endl;
Glog << fname + " starting iteration 0 / "<< MaxIter<< std::endl; Glog << fname + " starting iteration 0 / "<< MaxIter<< std::endl;
@@ -396,7 +396,7 @@ cudaStat = cudaMallocManaged((void **)&evec_acc, Nevec_acc*sites*12*sizeof(CUDA_
} }
Glog << std::string(74,'*') << std::endl; Glog << std::string(74,'*') << std::endl;
GRID_ASSERT(Nm == evec.size() && Nm == eval.size()); assert(Nm == evec.size() && Nm == eval.size());
std::vector<std::vector<ComplexD>> lmd(Nu,std::vector<ComplexD>(Nm,0.0)); std::vector<std::vector<ComplexD>> lmd(Nu,std::vector<ComplexD>(Nm,0.0));
std::vector<std::vector<ComplexD>> lme(Nu,std::vector<ComplexD>(Nm,0.0)); std::vector<std::vector<ComplexD>> lme(Nu,std::vector<ComplexD>(Nm,0.0));
@@ -579,8 +579,8 @@ cudaStat = cudaMallocManaged((void **)&evec_acc, Nevec_acc*sites*12*sizeof(CUDA_
{ {
std::string fname = std::string(cname+"::calc_rbl()"); std::string fname = std::string(cname+"::calc_rbl()");
GridBase *grid = evec[0].Grid(); GridBase *grid = evec[0].Grid();
GRID_ASSERT(grid == src[0].Grid()); assert(grid == src[0].Grid());
GRID_ASSERT( Nu = src.size() ); assert( Nu = src.size() );
int Np = (Nm-Nk); int Np = (Nm-Nk);
if (Np > 0 && MaxIter > 1) Np /= MaxIter; if (Np > 0 && MaxIter > 1) Np /= MaxIter;
@@ -607,7 +607,7 @@ cudaStat = cudaMallocManaged((void **)&evec_acc, Nevec_acc*sites*12*sizeof(CUDA_
} }
Glog << std::string(74,'*') << std::endl; Glog << std::string(74,'*') << std::endl;
GRID_ASSERT(Nm == evec.size() && Nm == eval.size()); assert(Nm == evec.size() && Nm == eval.size());
std::vector<std::vector<ComplexD>> lmd(Nu,std::vector<ComplexD>(Nm,0.0)); std::vector<std::vector<ComplexD>> lmd(Nu,std::vector<ComplexD>(Nm,0.0));
std::vector<std::vector<ComplexD>> lme(Nu,std::vector<ComplexD>(Nm,0.0)); std::vector<std::vector<ComplexD>> lme(Nu,std::vector<ComplexD>(Nm,0.0));
@@ -785,7 +785,7 @@ private:
int Nu = w.size(); int Nu = w.size();
int Nm = evec.size(); int Nm = evec.size();
GRID_ASSERT( b < Nm/Nu ); assert( b < Nm/Nu );
// GridCartesian *grid = evec[0]._grid; // GridCartesian *grid = evec[0]._grid;
// converts block index to full indicies for an interval [L,R) // converts block index to full indicies for an interval [L,R)
@@ -796,7 +796,7 @@ private:
Glog << "Using split grid"<< std::endl; Glog << "Using split grid"<< std::endl;
// LatticeGaugeField s_Umu(SGrid); // LatticeGaugeField s_Umu(SGrid);
GRID_ASSERT((Nu%mrhs)==0); assert((Nu%mrhs)==0);
std::vector<Field> in(mrhs,f_grid); std::vector<Field> in(mrhs,f_grid);
Field s_in(sf_grid); Field s_in(sf_grid);
@@ -906,7 +906,7 @@ if(split_test){
for (int u=0; u<Nu; ++u) { for (int u=0; u<Nu; ++u) {
// Glog << "norm2(w[" << u << "])= "<< norm2(w[u]) << std::endl; // Glog << "norm2(w[" << u << "])= "<< norm2(w[u]) << std::endl;
GRID_ASSERT (!isnan(norm2(w[u]))); assert (!isnan(norm2(w[u])));
for (int k=L+u; k<R; ++k) { for (int k=L+u; k<R; ++k) {
Glog <<" In block "<< b << "," <<" beta[" << u << "," << k-L << "] = " << lme[u][k] << std::endl; Glog <<" In block "<< b << "," <<" beta[" << u << "," << k-L << "] = " << lme[u][k] << std::endl;
} }
@@ -929,8 +929,8 @@ if(split_test){
Eigen::MatrixXcd & Qt, // Nm x Nm Eigen::MatrixXcd & Qt, // Nm x Nm
GridBase *grid) GridBase *grid)
{ {
GRID_ASSERT( Nk%Nu == 0 && Nm%Nu == 0 ); assert( Nk%Nu == 0 && Nm%Nu == 0 );
GRID_ASSERT( Nk <= Nm ); assert( Nk <= Nm );
Eigen::MatrixXcd BlockTriDiag = Eigen::MatrixXcd::Zero(Nk,Nk); Eigen::MatrixXcd BlockTriDiag = Eigen::MatrixXcd::Zero(Nk,Nk);
for ( int u=0; u<Nu; ++u ) { for ( int u=0; u<Nu; ++u ) {
@@ -970,8 +970,8 @@ if(split_test){
GridBase *grid) GridBase *grid)
{ {
Glog << "diagonalize_lapack: Nu= "<<Nu<<" Nk= "<<Nk<<" Nm= "<<std::endl; Glog << "diagonalize_lapack: Nu= "<<Nu<<" Nk= "<<Nk<<" Nm= "<<std::endl;
GRID_ASSERT( Nk%Nu == 0 && Nm%Nu == 0 ); assert( Nk%Nu == 0 && Nm%Nu == 0 );
GRID_ASSERT( Nk <= Nm ); assert( Nk <= Nm );
Eigen::MatrixXcd BlockTriDiag = Eigen::MatrixXcd::Zero(Nk,Nk); Eigen::MatrixXcd BlockTriDiag = Eigen::MatrixXcd::Zero(Nk,Nk);
for ( int u=0; u<Nu; ++u ) { for ( int u=0; u<Nu; ++u ) {
@@ -1119,7 +1119,7 @@ if (1){
diagonalize_lapack(eval,lmd,lme,Nu,Nk,Nm,Qt,grid); diagonalize_lapack(eval,lmd,lme,Nu,Nk,Nm,Qt,grid);
#endif #endif
} else { } else {
GRID_ASSERT(0); assert(0);
} }
} }
@@ -1131,8 +1131,8 @@ if (1){
Eigen::MatrixXcd& M) Eigen::MatrixXcd& M)
{ {
//Glog << "unpackHermitBlockTriDiagMatToEigen() begin" << '\n'; //Glog << "unpackHermitBlockTriDiagMatToEigen() begin" << '\n';
GRID_ASSERT( Nk%Nu == 0 && Nm%Nu == 0 ); assert( Nk%Nu == 0 && Nm%Nu == 0 );
GRID_ASSERT( Nk <= Nm ); assert( Nk <= Nm );
M = Eigen::MatrixXcd::Zero(Nk,Nk); M = Eigen::MatrixXcd::Zero(Nk,Nk);
// rearrange // rearrange
@@ -1159,8 +1159,8 @@ if (1){
Eigen::MatrixXcd& M) Eigen::MatrixXcd& M)
{ {
//Glog << "packHermitBlockTriDiagMatfromEigen() begin" << '\n'; //Glog << "packHermitBlockTriDiagMatfromEigen() begin" << '\n';
GRID_ASSERT( Nk%Nu == 0 && Nm%Nu == 0 ); assert( Nk%Nu == 0 && Nm%Nu == 0 );
GRID_ASSERT( Nk <= Nm ); assert( Nk <= Nm );
// rearrange // rearrange
for ( int u=0; u<Nu; ++u ) { for ( int u=0; u<Nu; ++u ) {
@@ -121,7 +121,7 @@ public:
eresid(_eresid), MaxIter(_MaxIter), eresid(_eresid), MaxIter(_MaxIter),
diagonalisation(_diagonalisation), diagonalisation(_diagonalisation),
Nevec_acc(_Nu) Nevec_acc(_Nu)
{ GRID_ASSERT( (Nk%Nu==0) && (Nm%Nu==0) ); }; { assert( (Nk%Nu==0) && (Nm%Nu==0) ); };
//////////////////////////////// ////////////////////////////////
// Helpers // Helpers
@@ -151,7 +151,7 @@ public:
Glog<<"orthogonalize after: "<<j<<" of "<<k<<" "<< ip <<std::endl; Glog<<"orthogonalize after: "<<j<<" of "<<k<<" "<< ip <<std::endl;
} }
} }
GRID_ASSERT(normalize(w,if_print) != 0); assert(normalize(w,if_print) != 0);
} }
void reorthogonalize(Field& w, std::vector<Field>& evec, int k) void reorthogonalize(Field& w, std::vector<Field>& evec, int k)
{ {
@@ -169,7 +169,7 @@ public:
w[i] = w[i] - ip * evec[j]; w[i] = w[i] - ip * evec[j];
}} }}
for(int i=0; i<_Nu; ++i) for(int i=0; i<_Nu; ++i)
GRID_ASSERT(normalize(w[i],if_print) !=0); assert(normalize(w[i],if_print) !=0);
} }
void orthogonalize_blockhead(Field& w, std::vector<Field>& evec, int k, int Nu) void orthogonalize_blockhead(Field& w, std::vector<Field>& evec, int k, int Nu)
@@ -205,8 +205,8 @@ public:
{ {
std::string fname = std::string(cname+"::calc_irbl()"); std::string fname = std::string(cname+"::calc_irbl()");
GridBase *grid = evec[0].Grid(); GridBase *grid = evec[0].Grid();
GRID_ASSERT(grid == src[0].Grid()); assert(grid == src[0].Grid());
GRID_ASSERT( Nu = src.size() ); assert( Nu = src.size() );
Glog << std::string(74,'*') << std::endl; Glog << std::string(74,'*') << std::endl;
Glog << fname + " starting iteration 0 / "<< MaxIter<< std::endl; Glog << fname + " starting iteration 0 / "<< MaxIter<< std::endl;
@@ -227,7 +227,7 @@ public:
} }
Glog << std::string(74,'*') << std::endl; Glog << std::string(74,'*') << std::endl;
GRID_ASSERT(Nm == evec.size() && Nm == eval.size()); assert(Nm == evec.size() && Nm == eval.size());
std::vector<std::vector<ComplexD>> lmd(Nu,std::vector<ComplexD>(Nm,0.0)); std::vector<std::vector<ComplexD>> lmd(Nu,std::vector<ComplexD>(Nm,0.0));
std::vector<std::vector<ComplexD>> lme(Nu,std::vector<ComplexD>(Nm,0.0)); std::vector<std::vector<ComplexD>> lme(Nu,std::vector<ComplexD>(Nm,0.0));
@@ -413,8 +413,8 @@ public:
{ {
std::string fname = std::string(cname+"::calc_rbl()"); std::string fname = std::string(cname+"::calc_rbl()");
GridBase *grid = evec[0].Grid(); GridBase *grid = evec[0].Grid();
GRID_ASSERT(grid == src[0].Grid()); assert(grid == src[0].Grid());
GRID_ASSERT( Nu = src.size() ); assert( Nu = src.size() );
int Np = (Nm-Nk); int Np = (Nm-Nk);
if (Np > 0 && MaxIter > 1) Np /= MaxIter; if (Np > 0 && MaxIter > 1) Np /= MaxIter;
@@ -441,7 +441,7 @@ public:
} }
Glog << std::string(74,'*') << std::endl; Glog << std::string(74,'*') << std::endl;
GRID_ASSERT(Nm == evec.size() && Nm == eval.size()); assert(Nm == evec.size() && Nm == eval.size());
std::vector<std::vector<ComplexD>> lmd(Nu,std::vector<ComplexD>(Nm,0.0)); std::vector<std::vector<ComplexD>> lmd(Nu,std::vector<ComplexD>(Nm,0.0));
std::vector<std::vector<ComplexD>> lme(Nu,std::vector<ComplexD>(Nm,0.0)); std::vector<std::vector<ComplexD>> lme(Nu,std::vector<ComplexD>(Nm,0.0));
@@ -622,7 +622,7 @@ private:
int Nu = w.size(); int Nu = w.size();
int Nm = evec.size(); int Nm = evec.size();
GRID_ASSERT( b < Nm/Nu ); assert( b < Nm/Nu );
// converts block index to full indicies for an interval [L,R) // converts block index to full indicies for an interval [L,R)
int L = Nu*b; int L = Nu*b;
@@ -630,7 +630,7 @@ private:
Real beta; Real beta;
GRID_ASSERT((Nu%mrhs)==0); assert((Nu%mrhs)==0);
std::vector<Field> in(mrhs,f_grid); std::vector<Field> in(mrhs,f_grid);
std::vector<Field> out(mrhs,f_grid); std::vector<Field> out(mrhs,f_grid);
@@ -711,7 +711,7 @@ private:
for (int u=0; u<Nu; ++u) { for (int u=0; u<Nu; ++u) {
// Glog << "norm2(w[" << u << "])= "<< norm2(w[u]) << std::endl; // Glog << "norm2(w[" << u << "])= "<< norm2(w[u]) << std::endl;
GRID_ASSERT (!isnan(norm2(w[u]))); assert (!isnan(norm2(w[u])));
for (int k=L+u; k<R; ++k) { for (int k=L+u; k<R; ++k) {
// Glog <<" In block "<< b << "," <<" beta[" << u << "," << k-L << "] = " << lme[u][k] << std::endl; // Glog <<" In block "<< b << "," <<" beta[" << u << "," << k-L << "] = " << lme[u][k] << std::endl;
} }
@@ -734,8 +734,8 @@ private:
Eigen::MatrixXcd & Qt, // Nm x Nm Eigen::MatrixXcd & Qt, // Nm x Nm
GridBase *grid) GridBase *grid)
{ {
GRID_ASSERT( Nk%Nu == 0 && Nm%Nu == 0 ); assert( Nk%Nu == 0 && Nm%Nu == 0 );
GRID_ASSERT( Nk <= Nm ); assert( Nk <= Nm );
Eigen::MatrixXcd BlockTriDiag = Eigen::MatrixXcd::Zero(Nk,Nk); Eigen::MatrixXcd BlockTriDiag = Eigen::MatrixXcd::Zero(Nk,Nk);
for ( int u=0; u<Nu; ++u ) { for ( int u=0; u<Nu; ++u ) {
@@ -775,8 +775,8 @@ private:
GridBase *grid) GridBase *grid)
{ {
Glog << "diagonalize_lapack: Nu= "<<Nu<<" Nk= "<<Nk<<" Nm= "<<std::endl; Glog << "diagonalize_lapack: Nu= "<<Nu<<" Nk= "<<Nk<<" Nm= "<<std::endl;
GRID_ASSERT( Nk%Nu == 0 && Nm%Nu == 0 ); assert( Nk%Nu == 0 && Nm%Nu == 0 );
GRID_ASSERT( Nk <= Nm ); assert( Nk <= Nm );
Eigen::MatrixXcd BlockTriDiag = Eigen::MatrixXcd::Zero(Nk,Nk); Eigen::MatrixXcd BlockTriDiag = Eigen::MatrixXcd::Zero(Nk,Nk);
for ( int u=0; u<Nu; ++u ) { for ( int u=0; u<Nu; ++u ) {
@@ -924,7 +924,7 @@ if (1){
diagonalize_lapack(eval,lmd,lme,Nu,Nk,Nm,Qt,grid); diagonalize_lapack(eval,lmd,lme,Nu,Nk,Nm,Qt,grid);
#endif #endif
} else { } else {
GRID_ASSERT(0); assert(0);
} }
} }
@@ -936,8 +936,8 @@ if (1){
Eigen::MatrixXcd& M) Eigen::MatrixXcd& M)
{ {
// Glog << "unpackHermitBlockTriDiagMatToEigen() begin" << '\n'; // Glog << "unpackHermitBlockTriDiagMatToEigen() begin" << '\n';
GRID_ASSERT( Nk%Nu == 0 && Nm%Nu == 0 ); assert( Nk%Nu == 0 && Nm%Nu == 0 );
GRID_ASSERT( Nk <= Nm ); assert( Nk <= Nm );
M = Eigen::MatrixXcd::Zero(Nk,Nk); M = Eigen::MatrixXcd::Zero(Nk,Nk);
// rearrange // rearrange
@@ -964,8 +964,8 @@ if (1){
Eigen::MatrixXcd& M) Eigen::MatrixXcd& M)
{ {
// Glog << "packHermitBlockTriDiagMatfromEigen() begin" << '\n'; // Glog << "packHermitBlockTriDiagMatfromEigen() begin" << '\n';
GRID_ASSERT( Nk%Nu == 0 && Nm%Nu == 0 ); assert( Nk%Nu == 0 && Nm%Nu == 0 );
GRID_ASSERT( Nk <= Nm ); assert( Nk <= Nm );
// rearrange // rearrange
for ( int u=0; u<Nu; ++u ) { for ( int u=0; u<Nu; ++u ) {
@@ -211,7 +211,7 @@ until convergence
void calc(std::vector<RealD>& eval, std::vector<Field>& evec, const Field& src, int& Nconv, bool reverse=false) void calc(std::vector<RealD>& eval, std::vector<Field>& evec, const Field& src, int& Nconv, bool reverse=false)
{ {
GridBase *grid = src.Grid(); GridBase *grid = src.Grid();
GRID_ASSERT(grid == evec[0].Grid()); assert(grid == evec[0].Grid());
// GridLogIRL.TimingMode(1); // GridLogIRL.TimingMode(1);
std::cout << GridLogIRL <<"**************************************************************************"<< std::endl; std::cout << GridLogIRL <<"**************************************************************************"<< std::endl;
@@ -231,7 +231,7 @@ until convergence
} }
std::cout << GridLogIRL <<"**************************************************************************"<< std::endl; std::cout << GridLogIRL <<"**************************************************************************"<< std::endl;
GRID_ASSERT(Nm <= evec.size() && Nm <= eval.size()); assert(Nm <= evec.size() && Nm <= eval.size());
// quickly get an idea of the largest eigenvalue to more properly normalize the residuum // quickly get an idea of the largest eigenvalue to more properly normalize the residuum
RealD evalMaxApprox = 0.0; RealD evalMaxApprox = 0.0;
@@ -337,7 +337,7 @@ until convergence
} }
std::cout<<GridLogIRL <<"QR decomposed "<<std::endl; std::cout<<GridLogIRL <<"QR decomposed "<<std::endl;
GRID_ASSERT(k2<Nm); GRID_ASSERT(k2<Nm); GRID_ASSERT(k1>0); assert(k2<Nm); assert(k2<Nm); assert(k1>0);
basisRotate(evec,Qt,k1-1,k2+1,0,Nm,Nm); /// big constraint on the basis basisRotate(evec,Qt,k1-1,k2+1,0,Nm,Nm); /// big constraint on the basis
std::cout<<GridLogIRL <<"basisRotated by Qt *"<<k1-1<<","<<k2+1<<")"<<std::endl; std::cout<<GridLogIRL <<"basisRotated by Qt *"<<k1-1<<","<<k2+1<<")"<<std::endl;
@@ -463,7 +463,7 @@ until convergence
{ {
std::cout<<GridLogDebug << "Lanczos step " <<k<<std::endl; std::cout<<GridLogDebug << "Lanczos step " <<k<<std::endl;
const RealD tiny = 1.0e-20; const RealD tiny = 1.0e-20;
GRID_ASSERT( k< Nm ); assert( k< Nm );
GridStopWatch gsw_op,gsw_o; GridStopWatch gsw_op,gsw_o;
@@ -597,7 +597,7 @@ until convergence
} else if ( diagonalisation == IRLdiagonaliseWithEigen ) { } else if ( diagonalisation == IRLdiagonaliseWithEigen ) {
diagonalize_Eigen(lmd,lme,Nk,Nm,Qt,grid); diagonalize_Eigen(lmd,lme,Nk,Nm,Qt,grid);
} else { } else {
GRID_ASSERT(0); assert(0);
} }
} }
@@ -687,7 +687,7 @@ void diagonalize_lapack(std::vector<RealD>& lmd,
} }
} }
#else #else
GRID_ASSERT(0); assert(0);
#endif #endif
} }
@@ -80,7 +80,7 @@ public:
ProjectedHermOp(LinearOperatorBase<FineField>& linop, std::vector<FineField> & _subspace) : ProjectedHermOp(LinearOperatorBase<FineField>& linop, std::vector<FineField> & _subspace) :
_Linop(linop), subspace(_subspace) _Linop(linop), subspace(_subspace)
{ {
GRID_ASSERT(subspace.size() >0); assert(subspace.size() >0);
}; };
void operator()(const CoarseField& in, CoarseField& out) { void operator()(const CoarseField& in, CoarseField& out) {
@@ -346,12 +346,12 @@ public:
void testFine(RealD resid) void testFine(RealD resid)
{ {
GRID_ASSERT(evals_fine.size() == nbasis); assert(evals_fine.size() == nbasis);
GRID_ASSERT(subspace.size() == nbasis); assert(subspace.size() == nbasis);
PlainHermOp<FineField> Op(_FineOp); PlainHermOp<FineField> Op(_FineOp);
ImplicitlyRestartedLanczosHermOpTester<FineField> SimpleTester(Op); ImplicitlyRestartedLanczosHermOpTester<FineField> SimpleTester(Op);
for(int k=0;k<nbasis;k++){ for(int k=0;k<nbasis;k++){
GRID_ASSERT(SimpleTester.ReconstructEval(k,resid,subspace[k],evals_fine[k],1.0)==1); assert(SimpleTester.ReconstructEval(k,resid,subspace[k],evals_fine[k],1.0)==1);
} }
} }
@@ -359,8 +359,8 @@ public:
//hence the smoother can be tuned after running the coarse Lanczos by using a different smoother here //hence the smoother can be tuned after running the coarse Lanczos by using a different smoother here
void testCoarse(RealD resid,ChebyParams cheby_smooth,RealD relax) void testCoarse(RealD resid,ChebyParams cheby_smooth,RealD relax)
{ {
GRID_ASSERT(evals_fine.size() == nbasis); assert(evals_fine.size() == nbasis);
GRID_ASSERT(subspace.size() == nbasis); assert(subspace.size() == nbasis);
////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////
// create a smoother and see if we can get a cheap convergence test and smooth inside the IRL // create a smoother and see if we can get a cheap convergence test and smooth inside the IRL
////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////
@@ -380,7 +380,7 @@ public:
void calcFine(ChebyParams cheby_parms,int Nstop,int Nk,int Nm,RealD resid, void calcFine(ChebyParams cheby_parms,int Nstop,int Nk,int Nm,RealD resid,
RealD MaxIt, RealD betastp, int MinRes) RealD MaxIt, RealD betastp, int MinRes)
{ {
GRID_ASSERT(nbasis<=Nm); assert(nbasis<=Nm);
Chebyshev<FineField> Cheby(cheby_parms); Chebyshev<FineField> Cheby(cheby_parms);
FunctionHermOp<FineField> ChebyOp(Cheby,_FineOp); FunctionHermOp<FineField> ChebyOp(Cheby,_FineOp);
PlainHermOp<FineField> Op(_FineOp); PlainHermOp<FineField> Op(_FineOp);
@@ -400,8 +400,8 @@ public:
IRL.calc(evals_fine,subspace,src,Nconv,false); IRL.calc(evals_fine,subspace,src,Nconv,false);
// Shrink down to number saved // Shrink down to number saved
GRID_ASSERT(Nstop>=nbasis); assert(Nstop>=nbasis);
GRID_ASSERT(Nconv>=nbasis); assert(Nconv>=nbasis);
evals_fine.resize(nbasis); evals_fine.resize(nbasis);
subspace.resize(nbasis,_FineGrid); subspace.resize(nbasis,_FineGrid);
} }
@@ -433,7 +433,7 @@ public:
ImplicitlyRestartedLanczos<CoarseField> IRL(ChebyOp,ChebyOp,ChebySmoothTester,Nstop,Nk,Nm,resid,MaxIt,betastp,MinRes); ImplicitlyRestartedLanczos<CoarseField> IRL(ChebyOp,ChebyOp,ChebySmoothTester,Nstop,Nk,Nm,resid,MaxIt,betastp,MinRes);
int Nconv=0; int Nconv=0;
IRL.calc(evals_coarse,evec_coarse,src,Nconv,false); IRL.calc(evals_coarse,evec_coarse,src,Nconv,false);
GRID_ASSERT(Nconv>=Nstop); assert(Nconv>=Nstop);
evals_coarse.resize(Nstop); evals_coarse.resize(Nstop);
evec_coarse.resize (Nstop,_CoarseGrid); evec_coarse.resize (Nstop,_CoarseGrid);
for (int i=0;i<Nstop;i++){ for (int i=0;i<Nstop;i++){
+4 -4
View File
@@ -35,7 +35,7 @@ template<class Field> class MinimalResidual : public OperatorFunction<Field> {
public: public:
using OperatorFunction<Field>::operator(); using OperatorFunction<Field>::operator();
bool ErrorOnNoConverge; // throw an GRID_ASSERT when the MR fails to converge. bool ErrorOnNoConverge; // throw an assert when the MR fails to converge.
// Defaults true. // Defaults true.
RealD Tolerance; RealD Tolerance;
Integer MaxIterations; Integer MaxIterations;
@@ -59,7 +59,7 @@ template<class Field> class MinimalResidual : public OperatorFunction<Field> {
// Initial residual computation & set up // Initial residual computation & set up
RealD guess = norm2(psi); RealD guess = norm2(psi);
GRID_ASSERT(std::isnan(guess) == 0); assert(std::isnan(guess) == 0);
RealD ssq = norm2(src); RealD ssq = norm2(src);
RealD rsq = Tolerance * Tolerance * ssq; RealD rsq = Tolerance * Tolerance * ssq;
@@ -136,7 +136,7 @@ template<class Field> class MinimalResidual : public OperatorFunction<Field> {
std::cout << GridLogMessage << "MR Time elapsed: Linalg " << LinalgTimer.Elapsed() << std::endl; std::cout << GridLogMessage << "MR Time elapsed: Linalg " << LinalgTimer.Elapsed() << std::endl;
if (ErrorOnNoConverge) if (ErrorOnNoConverge)
GRID_ASSERT(true_residual / Tolerance < 10000.0); assert(true_residual / Tolerance < 10000.0);
IterationsToComplete = k; IterationsToComplete = k;
@@ -148,7 +148,7 @@ template<class Field> class MinimalResidual : public OperatorFunction<Field> {
<< std::endl; << std::endl;
if (ErrorOnNoConverge) if (ErrorOnNoConverge)
GRID_ASSERT(0); assert(0);
IterationsToComplete = k; IterationsToComplete = k;
} }
@@ -37,7 +37,7 @@ class MixedPrecisionFlexibleGeneralisedMinimalResidual : public OperatorFunction
using OperatorFunction<FieldD>::operator(); using OperatorFunction<FieldD>::operator();
bool ErrorOnNoConverge; // Throw an GRID_ASSERT when MPFGMRES fails to converge, bool ErrorOnNoConverge; // Throw an assert when MPFGMRES fails to converge,
// defaults to true // defaults to true
RealD Tolerance; RealD Tolerance;
@@ -91,7 +91,7 @@ class MixedPrecisionFlexibleGeneralisedMinimalResidual : public OperatorFunction
conformable(psi, src); conformable(psi, src);
RealD guess = norm2(psi); RealD guess = norm2(psi);
GRID_ASSERT(std::isnan(guess) == 0); assert(std::isnan(guess) == 0);
RealD cp; RealD cp;
RealD ssq = norm2(src); RealD ssq = norm2(src);
@@ -150,7 +150,7 @@ class MixedPrecisionFlexibleGeneralisedMinimalResidual : public OperatorFunction
std::cout << GridLogMessage << "MPFGMRES did NOT converge" << std::endl; std::cout << GridLogMessage << "MPFGMRES did NOT converge" << std::endl;
if (ErrorOnNoConverge) if (ErrorOnNoConverge)
GRID_ASSERT(0); assert(0);
} }
RealD outerLoopBody(LinearOperatorBase<FieldD> &LinOp, const FieldD &src, FieldD &psi, RealD rsq) { RealD outerLoopBody(LinearOperatorBase<FieldD> &LinOp, const FieldD &src, FieldD &psi, RealD rsq) {
@@ -197,7 +197,7 @@ class MixedPrecisionFlexibleGeneralisedMinimalResidual : public OperatorFunction
} }
} }
GRID_ASSERT(0); // Never reached assert(0); // Never reached
return cp; return cp;
} }
@@ -112,7 +112,7 @@ public:
} }
std::cout<<GridLogMessage<<"PrecConjugateResidual did NOT converge"<<std::endl; std::cout<<GridLogMessage<<"PrecConjugateResidual did NOT converge"<<std::endl;
GRID_ASSERT(0); assert(0);
} }
}; };
NAMESPACE_END(Grid); NAMESPACE_END(Grid);
@@ -118,7 +118,7 @@ public:
} }
GCRLogLevel<<"Variable Preconditioned GCR did not converge"<<std::endl; GCRLogLevel<<"Variable Preconditioned GCR did not converge"<<std::endl;
// GRID_ASSERT(0); // assert(0);
} }
RealD GCRnStep(const Field &src, Field &psi,RealD rsq){ RealD GCRnStep(const Field &src, Field &psi,RealD rsq){
@@ -221,7 +221,7 @@ public:
int northog = ((kp)>(mmax-1))?(mmax-1):(kp); // if more than mmax done, we orthog all mmax history. int northog = ((kp)>(mmax-1))?(mmax-1):(kp); // if more than mmax done, we orthog all mmax history.
for(int back=0;back<northog;back++){ for(int back=0;back<northog;back++){
int peri_back=(k-back)%mmax; GRID_ASSERT((k-back)>=0); int peri_back=(k-back)%mmax; assert((k-back)>=0);
b=-real(innerProduct(q[peri_back],Az))/qq[peri_back]; b=-real(innerProduct(q[peri_back],Az))/qq[peri_back];
p[peri_kp]=p[peri_kp]+b*p[peri_back]; p[peri_kp]=p[peri_kp]+b*p[peri_back];
@@ -231,10 +231,9 @@ public:
qq[peri_kp]=norm2(q[peri_kp]); // could use axpy_norm qq[peri_kp]=norm2(q[peri_kp]); // could use axpy_norm
LinalgTimer.Stop(); LinalgTimer.Stop();
} }
GRID_ASSERT(0); // never reached assert(0); // never reached
return cp; return cp;
} }
}; };
NAMESPACE_END(Grid); NAMESPACE_END(Grid);
#undef GCRLogLevel
#endif #endif
@@ -28,7 +28,6 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
/* END LEGAL */ /* END LEGAL */
#ifndef GRID_PREC_GCR_NON_HERM_H #ifndef GRID_PREC_GCR_NON_HERM_H
#define GRID_PREC_GCR_NON_HERM_H #define GRID_PREC_GCR_NON_HERM_H
#include <Grid/algorithms/iterative/GCRCoefficients.h>
/////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////
//VPGCR Abe and Zhang, 2005. //VPGCR Abe and Zhang, 2005.
@@ -39,14 +38,13 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
/////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////
NAMESPACE_BEGIN(Grid); NAMESPACE_BEGIN(Grid);
#define GCRLogLevel std::cout << GridLogMessage <<std::string(level,'\t')<< name<<" " #define GCRLogLevel std::cout << GridLogMessage <<std::string(level,'\t')<< " Level "<<level<<" "
template<class Field> template<class Field>
class PrecGeneralisedConjugateResidualNonHermitian : public LinearFunction<Field> { class PrecGeneralisedConjugateResidualNonHermitian : public LinearFunction<Field> {
public: public:
using LinearFunction<Field>::operator(); using LinearFunction<Field>::operator();
RealD Tolerance; RealD Tolerance;
RealD SSQ;
Integer MaxIterations; Integer MaxIterations;
int verbose; int verbose;
int mmax; int mmax;
@@ -56,35 +54,11 @@ public:
GridStopWatch PrecTimer; GridStopWatch PrecTimer;
GridStopWatch MatTimer; GridStopWatch MatTimer;
GridStopWatch LinalgTimer; GridStopWatch LinalgTimer;
std::string name;
int ZeroGuess = 0; // caller contract: guess is always zero => first-cycle r0 = src, skip the apply
// persistent GCR history (see GCRnStep)
GridBase *hist_grid = nullptr;
std::vector<Field> q;
std::vector<Field> p;
std::vector<RealD> qq;
int FirstCycle = 0;
LinearFunction<Field> &Preconditioner; LinearFunction<Field> &Preconditioner;
LinearOperatorBase<Field> &Linop; LinearOperatorBase<Field> &Linop;
void Name(std::string _name) { name = _name; }; void Level(int lv) { level=lv; };
void Level(int n) { Name("Level " + std::to_string(n)); level = n; }
void SetZeroGuess(int z) { ZeroGuess = z; };
// Coefficient logging: one line per step with the step length a_k and the
// orthogonalisation coefficients b_{k,j}. These are the data from which a
// FIXED polynomial smoother can be harvested: if they are stable from call
// to call, the adaptive GCR can be replaced by a stationary p(A) with the
// same applies and no reductions. Off by default; boss rank prints.
int LogCoeffs = 0;
void LogCoefficients(int l) { LogCoeffs = l; };
// Optional recorder of the per-step coefficients (means over calls), for
// replay by GCRReplaySmoother (Smoothers.h). Records only; no effect on
// the iteration.
GCRCoefficients *Recorder = nullptr;
void SetCoefficientRecorder(GCRCoefficients *r) { Recorder = r; if(r) r->mmax = mmax; };
PrecGeneralisedConjugateResidualNonHermitian(RealD tol,Integer maxit,LinearOperatorBase<Field> &_Linop,LinearFunction<Field> &Prec,int _mmax,int _nstep) : PrecGeneralisedConjugateResidualNonHermitian(RealD tol,Integer maxit,LinearOperatorBase<Field> &_Linop,LinearFunction<Field> &Prec,int _mmax,int _nstep) :
Tolerance(tol), Tolerance(tol),
@@ -93,8 +67,8 @@ public:
Preconditioner(Prec), Preconditioner(Prec),
mmax(_mmax), mmax(_mmax),
nstep(_nstep) nstep(_nstep)
{ {
Level(1); level=1;
verbose=1; verbose=1;
}; };
@@ -103,7 +77,6 @@ public:
// psi=Zero(); // psi=Zero();
RealD cp, ssq,rsq; RealD cp, ssq,rsq;
ssq=norm2(src); ssq=norm2(src);
SSQ=ssq;
rsq=Tolerance*Tolerance*ssq; rsq=Tolerance*Tolerance*ssq;
Field r(src.Grid()); Field r(src.Grid());
@@ -116,12 +89,11 @@ public:
SolverTimer.Start(); SolverTimer.Start();
steps=0; steps=0;
FirstCycle=1;
for(int k=0;k<MaxIterations;k++){ for(int k=0;k<MaxIterations;k++){
cp=GCRnStep(src,psi,rsq); cp=GCRnStep(src,psi,rsq);
GCRLogLevel <<"PGCR("<<mmax<<","<<nstep<<") "<< steps <<" steps cp = "<<sqrt(cp/ssq)<<" target "<<sqrt(rsq/ssq) <<std::endl; GCRLogLevel <<"PGCR("<<mmax<<","<<nstep<<") "<< steps <<" steps cp = "<<cp<<" target "<<rsq <<std::endl;
if(cp<rsq) { if(cp<rsq) {
@@ -141,85 +113,77 @@ public:
} }
GCRLogLevel<<"Variable Preconditioned GCR did not converge"<<std::endl; GCRLogLevel<<"Variable Preconditioned GCR did not converge"<<std::endl;
// GRID_ASSERT(0); // assert(0);
} }
RealD GCRnStep(const Field &src, Field &psi,RealD rsq){ RealD GCRnStep(const Field &src, Field &psi,RealD rsq){
RealD cp; RealD cp;
ComplexD a, b; ComplexD a, b;
// ComplexD zAz;
RealD zAAz;
ComplexD rq; ComplexD rq;
GridBase *grid = src.Grid(); GridBase *grid = src.Grid();
// Only r and one scratch for the restart residual; the new p/q directions
// are produced directly in their persistent history slots.
Field r(grid); Field r(grid);
Field z(grid);
Field tmp(grid);
Field ttmp(grid);
Field Az(grid); Field Az(grid);
//////////////////////////////// ////////////////////////////////
// history for flexible orthog // history for flexible orthog
//////////////////////////////// ////////////////////////////////
// History arrays are PERSISTENT across calls (allocated once per grid, std::vector<Field> q(mmax,grid);
// re-made only if the grid or mmax changes). The per-call form std::vector<Field> p(mmax,grid);
// std::vector<Field>(mmax,grid) built a temporary and copy-constructed it std::vector<RealD> qq(mmax);
// mmax times on every restart cycle -- measured ~5 ms per fine-smoother
// call. Safe: every entry is written before it is read within a cycle
// (q[kp],p[kp] assigned before the northog loop can reach them), so no
// stale content is ever consumed.
if ( hist_grid != grid || (int)q.size() != mmax ) {
q.clear(); p.clear();
q.reserve(mmax); p.reserve(mmax);
for(int i=0;i<mmax;i++){ q.emplace_back(grid); p.emplace_back(grid); }
qq.assign(mmax, 0.0);
hist_grid = grid;
}
GCRLogLevel<< "PGCR nStep("<<nstep<<")"<<std::endl; GCRLogLevel<< "PGCR nStep("<<nstep<<")"<<std::endl;
////////////////////////////////// //////////////////////////////////
// r0 = src - A x0. ZeroGuess: on the first cycle x0==0 by caller // initial guess x0 is taken as nonzero.
// contract (enforced here), so r0 = src exactly; skip the apply. // r0=src-A x0 = src
// Restart cycles (psi!=0) always do the full computation.
////////////////////////////////// //////////////////////////////////
if (ZeroGuess && FirstCycle) { MatTimer.Start();
psi = Zero(); Linop.Op(psi,Az);
LinalgTimer.Start(); // zAz = innerProduct(Az,psi);
r = src; zAAz= norm2(Az);
LinalgTimer.Stop(); MatTimer.Stop();
} else {
MatTimer.Start();
Linop.Op(psi,Az);
MatTimer.Stop();
LinalgTimer.Start();
r=src-Az;
LinalgTimer.Stop();
}
FirstCycle=0;
LinalgTimer.Start();
r=src-Az;
LinalgTimer.Stop();
GCRLogLevel<< "PGCR true residual r = src - A psi "<<norm2(r) <<std::endl;
///////////////////// /////////////////////
// p = Prec(r) // p = Prec(r)
///////////////////// /////////////////////
// p[0] = Prec(r), q[0] = A p[0], written straight into the history slots
PrecTimer.Start(); PrecTimer.Start();
Preconditioner(r,p[0]); Preconditioner(r,z);
PrecTimer.Stop(); PrecTimer.Stop();
MatTimer.Start(); MatTimer.Start();
Linop.Op(p[0],q[0]); Linop.Op(z,Az);
MatTimer.Stop(); MatTimer.Stop();
LinalgTimer.Start(); LinalgTimer.Start();
qq[0]= norm2(q[0]); // zAz = innerProduct(Az,psi);
zAAz= norm2(Az);
//p[0],q[0],qq[0]
p[0]= z;
q[0]= Az;
qq[0]= zAAz;
cp =norm2(r); cp =norm2(r);
LinalgTimer.Stop(); LinalgTimer.Stop();
GCRLogLevel<< "PGCR true residual "<< sqrt(cp/SSQ) <<std::endl;
for(int k=0;k<nstep;k++){ for(int k=0;k<nstep;k++){
GRID_TRACE("PGCR_step");
steps++; steps++;
int kp = k+1; int kp = k+1;
@@ -229,76 +193,50 @@ public:
LinalgTimer.Start(); LinalgTimer.Start();
rq= innerProduct(q[peri_k],r); // what if rAr not real? rq= innerProduct(q[peri_k],r); // what if rAr not real?
a = rq/qq[peri_k]; a = rq/qq[peri_k];
if ( Recorder ) Recorder->RecordA(k,a);
axpy(psi,a,p[peri_k],psi); axpy(psi,a,p[peri_k],psi);
cp = axpy_norm(r,-a,q[peri_k],r); cp = axpy_norm(r,-a,q[peri_k],r);
LinalgTimer.Stop(); LinalgTimer.Stop();
if ( LogCoeffs ) {
GCRLogLevel<<"coeff["<<k<<"] a=("<<real(a)<<","<<imag(a)<<")"
<<" |r|/|r0|="<<sqrt(cp/SSQ)<<std::endl;
}
GCRLogLevel<< "PGCR step["<<steps<<"] resid " << sqrt(cp/SSQ)<<std::endl; GCRLogLevel<< "PGCR step["<<steps<<"] resid " << cp << " target " <<rsq<<std::endl;
if((k==nstep-1)||(cp<rsq)){ if((k==nstep-1)||(cp<rsq)){
return cp; return cp;
} }
// New direction written straight into its history slot: p = Prec(r), q = A p.
PrecTimer.Start(); PrecTimer.Start();
Preconditioner(r,p[peri_kp]); Preconditioner(r,z);// solve Az = r
PrecTimer.Stop(); PrecTimer.Stop();
MatTimer.Start(); MatTimer.Start();
Linop.Op(p[peri_kp],q[peri_kp]); Linop.Op(z,Az);
MatTimer.Stop(); MatTimer.Stop();
// zAz = innerProduct(Az,psi);
zAAz= norm2(Az);
LinalgTimer.Start(); LinalgTimer.Start();
int northog = ((kp)>(mmax-1))?(mmax-1):(kp); // if more than mmax done, we orthog all mmax history. q[peri_kp]=Az;
std::ostringstream bs; p[peri_kp]=z;
// Classical Gram-Schmidt: every coefficient is taken against the int northog = ((kp)>(mmax-1))?(mmax-1):(kp); // if more than mmax done, we orthog all mmax history.
// UN-updated new q (so all northog inner products are independent and
// batchable), then the window is applied. The coefficient is complex:
// for a non-Hermitian operator <q_j,Aq> is complex and keeping only the
// real part left the q's non-orthogonal.
// Batched: one fused kernel + one reduction for all coefficients,
// one fused pass per update (independent of mmax).
std::vector<const Field*> qwin(northog), pwin(northog);
for(int back=0;back<northog;back++){ for(int back=0;back<northog;back++){
int peri_back=(k-back)%mmax; GRID_ASSERT((k-back)>=0);
GRID_ASSERT(peri_back!=peri_kp); int peri_back=(k-back)%mmax; assert((k-back)>=0);
qwin[back] = &q[peri_back];
pwin[back] = &p[peri_back]; b=-real(innerProduct(q[peri_back],Az))/qq[peri_back];
} p[peri_kp]=p[peri_kp]+b*p[peri_back];
std::vector<ComplexD> bcoef; q[peri_kp]=q[peri_kp]+b*q[peri_back];
innerProductMulti(bcoef,qwin,q[peri_kp]);
for(int back=0;back<northog;back++){
int peri_back=(k-back)%mmax;
bcoef[back] = -bcoef[back]/qq[peri_back];
if ( LogCoeffs ) bs<<" b["<<back<<"]="<<bcoef[back];
}
if ( Recorder && northog ) Recorder->RecordB(k,bcoef);
if ( northog ) {
axpyMulti(p[peri_kp],bcoef,pwin);
qq[peri_kp]=axpyMultiNorm(q[peri_kp],bcoef,qwin);
} else {
qq[peri_kp]=norm2(q[peri_kp]);
}
if ( LogCoeffs && northog ) {
GCRLogLevel<<"coeff["<<k<<"]"<<bs.str()<<std::endl;
} }
qq[peri_kp]=norm2(q[peri_kp]); // could use axpy_norm qq[peri_kp]=norm2(q[peri_kp]); // could use axpy_norm
LinalgTimer.Stop(); LinalgTimer.Stop();
} }
GRID_ASSERT(0); // never reached assert(0); // never reached
return cp; return cp;
} }
}; };
NAMESPACE_END(Grid); NAMESPACE_END(Grid);
#undef GCRLogLevel
#endif #endif
@@ -79,7 +79,7 @@ class QuasiMinimalResidual : public OperatorFunction<Field> {
LinOp.Op(x,r); r = b - r; LinOp.Op(x,r); r = b - r;
GRID_ASSERT(normb> 0.0); assert(normb> 0.0);
resid = norm2(r)/normb; resid = norm2(r)/normb;
if (resid <= Tolerance) { if (resid <= Tolerance) {
@@ -105,8 +105,8 @@ class QuasiMinimalResidual : public OperatorFunction<Field> {
for (int i = 1; i <= MaxIterations; i++) { for (int i = 1; i <= MaxIterations; i++) {
// Breakdown tests // Breakdown tests
GRID_ASSERT( rho != 0.0); assert( rho != 0.0);
GRID_ASSERT( xi != 0.0); assert( xi != 0.0);
v = (1. / rho) * v_tld; v = (1. / rho) * v_tld;
y = (1. / rho) * y; y = (1. / rho) * y;
@@ -134,10 +134,10 @@ class QuasiMinimalResidual : public OperatorFunction<Field> {
ep=Zep.real(); ep=Zep.real();
std::cout << "Zep "<<Zep <<std::endl; std::cout << "Zep "<<Zep <<std::endl;
// Complex Audit // Complex Audit
GRID_ASSERT(abs(ep)>0); assert(abs(ep)>0);
beta = ep / delta; beta = ep / delta;
GRID_ASSERT(abs(beta)>0); assert(abs(beta)>0);
v_tld = p_tld - beta * v; v_tld = p_tld - beta * v;
y = v_tld; y = v_tld;
@@ -158,7 +158,7 @@ class QuasiMinimalResidual : public OperatorFunction<Field> {
std::cout << "theta "<<theta<<std::endl; std::cout << "theta "<<theta<<std::endl;
std::cout << "gamma "<<gamma<<std::endl; std::cout << "gamma "<<gamma<<std::endl;
GRID_ASSERT(abs(gamma)> 0.0); assert(abs(gamma)> 0.0);
eta = -eta * rho_1 * gamma* gamma / (beta * gamma_1 * gamma_1); eta = -eta * rho_1 * gamma* gamma / (beta * gamma_1 * gamma_1);
@@ -178,7 +178,7 @@ class QuasiMinimalResidual : public OperatorFunction<Field> {
} }
std::cout << "Iteration "<<i<<" resid " << resid<<std::endl; std::cout << "Iteration "<<i<<" resid " << resid<<std::endl;
} }
GRID_ASSERT(0); assert(0);
return; // no convergence return; // no convergence
} }
#else #else
+54 -54
View File
@@ -327,9 +327,9 @@ namespace Grid {
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
// src_o = (source_o - Moe MeeInv source_e) // src_o = (source_o - Moe MeeInv source_e)
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
_Matrix.MooeeInv(src_e,tmp); GRID_ASSERT( tmp.Checkerboard() ==Even); _Matrix.MooeeInv(src_e,tmp); assert( tmp.Checkerboard() ==Even);
_Matrix.Meooe (tmp,Mtmp); GRID_ASSERT( Mtmp.Checkerboard() ==Odd); _Matrix.Meooe (tmp,Mtmp); assert( Mtmp.Checkerboard() ==Odd);
tmp=src_o-Mtmp; GRID_ASSERT( tmp.Checkerboard() ==Odd); tmp=src_o-Mtmp; assert( tmp.Checkerboard() ==Odd);
_Matrix.Mooee(tmp,src_o); // Extra factor of "m" in source from dumb choice of matrix norm. _Matrix.Mooee(tmp,src_o); // Extra factor of "m" in source from dumb choice of matrix norm.
} }
@@ -347,17 +347,17 @@ namespace Grid {
/////////////////////////////////////////////////// ///////////////////////////////////////////////////
// sol_e = M_ee^-1 * ( src_e - Meo sol_o )... // sol_e = M_ee^-1 * ( src_e - Meo sol_o )...
/////////////////////////////////////////////////// ///////////////////////////////////////////////////
_Matrix.Meooe(sol_o,tmp); GRID_ASSERT( tmp.Checkerboard() ==Even); _Matrix.Meooe(sol_o,tmp); assert( tmp.Checkerboard() ==Even);
src_e = src_e-tmp; GRID_ASSERT( src_e.Checkerboard() ==Even); src_e = src_e-tmp; assert( src_e.Checkerboard() ==Even);
_Matrix.MooeeInv(src_e,sol_e); GRID_ASSERT( sol_e.Checkerboard() ==Even); _Matrix.MooeeInv(src_e,sol_e); assert( sol_e.Checkerboard() ==Even);
setCheckerboard(sol,sol_e); GRID_ASSERT( sol_e.Checkerboard() ==Even); setCheckerboard(sol,sol_e); assert( sol_e.Checkerboard() ==Even);
setCheckerboard(sol,sol_o); GRID_ASSERT( sol_o.Checkerboard() ==Odd ); setCheckerboard(sol,sol_o); assert( sol_o.Checkerboard() ==Odd );
} }
virtual void RedBlackSolve (Matrix & _Matrix,const Field &src_o, Field &sol_o) virtual void RedBlackSolve (Matrix & _Matrix,const Field &src_o, Field &sol_o)
{ {
SchurStaggeredOperator<Matrix,Field> _HermOpEO(_Matrix); SchurStaggeredOperator<Matrix,Field> _HermOpEO(_Matrix);
this->_HermitianRBSolver(_HermOpEO,src_o,sol_o); GRID_ASSERT(sol_o.Checkerboard()==Odd); this->_HermitianRBSolver(_HermOpEO,src_o,sol_o); assert(sol_o.Checkerboard()==Odd);
}; };
virtual void RedBlackSolve (Matrix & _Matrix,const std::vector<Field> &src_o, std::vector<Field> &sol_o) virtual void RedBlackSolve (Matrix & _Matrix,const std::vector<Field> &src_o, std::vector<Field> &sol_o)
{ {
@@ -396,13 +396,13 @@ namespace Grid {
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
// src_o = Mdag * (source_o - Moe MeeInv source_e) // src_o = Mdag * (source_o - Moe MeeInv source_e)
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
_Matrix.MooeeInv(src_e,tmp); GRID_ASSERT( tmp.Checkerboard() ==Even); _Matrix.MooeeInv(src_e,tmp); assert( tmp.Checkerboard() ==Even);
_Matrix.Meooe (tmp,Mtmp); GRID_ASSERT( Mtmp.Checkerboard() ==Odd); _Matrix.Meooe (tmp,Mtmp); assert( Mtmp.Checkerboard() ==Odd);
tmp=src_o-Mtmp; GRID_ASSERT( tmp.Checkerboard() ==Odd); tmp=src_o-Mtmp; assert( tmp.Checkerboard() ==Odd);
// get the right MpcDag // get the right MpcDag
SchurDiagMooeeOperator<Matrix,Field> _HermOpEO(_Matrix); SchurDiagMooeeOperator<Matrix,Field> _HermOpEO(_Matrix);
_HermOpEO.MpcDag(tmp,src_o); GRID_ASSERT(src_o.Checkerboard() ==Odd); _HermOpEO.MpcDag(tmp,src_o); assert(src_o.Checkerboard() ==Odd);
} }
virtual void RedBlackSolution(Matrix & _Matrix,const Field &sol_o, const Field &src_e,Field &sol) virtual void RedBlackSolution(Matrix & _Matrix,const Field &sol_o, const Field &src_e,Field &sol)
@@ -416,17 +416,17 @@ namespace Grid {
/////////////////////////////////////////////////// ///////////////////////////////////////////////////
// sol_e = M_ee^-1 * ( src_e - Meo sol_o )... // sol_e = M_ee^-1 * ( src_e - Meo sol_o )...
/////////////////////////////////////////////////// ///////////////////////////////////////////////////
_Matrix.Meooe(sol_o,tmp); GRID_ASSERT( tmp.Checkerboard() ==Even); _Matrix.Meooe(sol_o,tmp); assert( tmp.Checkerboard() ==Even);
src_e_i = src_e-tmp; GRID_ASSERT( src_e_i.Checkerboard() ==Even); src_e_i = src_e-tmp; assert( src_e_i.Checkerboard() ==Even);
_Matrix.MooeeInv(src_e_i,sol_e); GRID_ASSERT( sol_e.Checkerboard() ==Even); _Matrix.MooeeInv(src_e_i,sol_e); assert( sol_e.Checkerboard() ==Even);
setCheckerboard(sol,sol_e); GRID_ASSERT( sol_e.Checkerboard() ==Even); setCheckerboard(sol,sol_e); assert( sol_e.Checkerboard() ==Even);
setCheckerboard(sol,sol_o); GRID_ASSERT( sol_o.Checkerboard() ==Odd ); setCheckerboard(sol,sol_o); assert( sol_o.Checkerboard() ==Odd );
} }
virtual void RedBlackSolve (Matrix & _Matrix,const Field &src_o, Field &sol_o) virtual void RedBlackSolve (Matrix & _Matrix,const Field &src_o, Field &sol_o)
{ {
SchurDiagMooeeOperator<Matrix,Field> _HermOpEO(_Matrix); SchurDiagMooeeOperator<Matrix,Field> _HermOpEO(_Matrix);
this->_HermitianRBSolver(_HermOpEO,src_o,sol_o); GRID_ASSERT(sol_o.Checkerboard()==Odd); this->_HermitianRBSolver(_HermOpEO,src_o,sol_o); assert(sol_o.Checkerboard()==Odd);
}; };
virtual void RedBlackSolve (Matrix & _Matrix,const std::vector<Field> &src_o, std::vector<Field> &sol_o) virtual void RedBlackSolve (Matrix & _Matrix,const std::vector<Field> &src_o, std::vector<Field> &sol_o)
{ {
@@ -461,9 +461,9 @@ namespace Grid {
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
// src_o = Mdag * (source_o - Moe MeeInv source_e) // src_o = Mdag * (source_o - Moe MeeInv source_e)
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
_Matrix.MooeeInv(src_e, tmp); GRID_ASSERT( tmp.Checkerboard() == Even ); _Matrix.MooeeInv(src_e, tmp); assert( tmp.Checkerboard() == Even );
_Matrix.Meooe (tmp, Mtmp); GRID_ASSERT( Mtmp.Checkerboard() == Odd ); _Matrix.Meooe (tmp, Mtmp); assert( Mtmp.Checkerboard() == Odd );
src_o -= Mtmp; GRID_ASSERT( src_o.Checkerboard() == Odd ); src_o -= Mtmp; assert( src_o.Checkerboard() == Odd );
} }
virtual void RedBlackSolution(Matrix& _Matrix, const Field& sol_o, const Field& src_e, Field& sol) virtual void RedBlackSolution(Matrix& _Matrix, const Field& sol_o, const Field& src_e, Field& sol)
@@ -478,18 +478,18 @@ namespace Grid {
/////////////////////////////////////////////////// ///////////////////////////////////////////////////
// sol_e = M_ee^-1 * ( src_e - Meo sol_o )... // sol_e = M_ee^-1 * ( src_e - Meo sol_o )...
/////////////////////////////////////////////////// ///////////////////////////////////////////////////
_Matrix.Meooe(sol_o, tmp); GRID_ASSERT( tmp.Checkerboard() == Even ); _Matrix.Meooe(sol_o, tmp); assert( tmp.Checkerboard() == Even );
src_e_i = src_e - tmp; GRID_ASSERT( src_e_i.Checkerboard() == Even ); src_e_i = src_e - tmp; assert( src_e_i.Checkerboard() == Even );
_Matrix.MooeeInv(src_e_i, sol_e); GRID_ASSERT( sol_e.Checkerboard() == Even ); _Matrix.MooeeInv(src_e_i, sol_e); assert( sol_e.Checkerboard() == Even );
setCheckerboard(sol, sol_e); GRID_ASSERT( sol_e.Checkerboard() == Even ); setCheckerboard(sol, sol_e); assert( sol_e.Checkerboard() == Even );
setCheckerboard(sol, sol_o); GRID_ASSERT( sol_o.Checkerboard() == Odd ); setCheckerboard(sol, sol_o); assert( sol_o.Checkerboard() == Odd );
} }
virtual void RedBlackSolve(Matrix& _Matrix, const Field& src_o, Field& sol_o) virtual void RedBlackSolve(Matrix& _Matrix, const Field& src_o, Field& sol_o)
{ {
NonHermitianSchurDiagMooeeOperator<Matrix,Field> _OpEO(_Matrix); NonHermitianSchurDiagMooeeOperator<Matrix,Field> _OpEO(_Matrix);
this->_HermitianRBSolver(_OpEO, src_o, sol_o); GRID_ASSERT(sol_o.Checkerboard() == Odd); this->_HermitianRBSolver(_OpEO, src_o, sol_o); assert(sol_o.Checkerboard() == Odd);
} }
virtual void RedBlackSolve(Matrix& _Matrix, const std::vector<Field>& src_o, std::vector<Field>& sol_o) virtual void RedBlackSolve(Matrix& _Matrix, const std::vector<Field>& src_o, std::vector<Field>& sol_o)
@@ -539,13 +539,13 @@ namespace Grid {
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
// src_o = Mpcdag *MooeeInv * (source_o - Moe MeeInv source_e) // src_o = Mpcdag *MooeeInv * (source_o - Moe MeeInv source_e)
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
_Matrix.MooeeInv(src_e,tmp); GRID_ASSERT( tmp.Checkerboard() ==Even); _Matrix.MooeeInv(src_e,tmp); assert( tmp.Checkerboard() ==Even);
_Matrix.Meooe (tmp,Mtmp); GRID_ASSERT( Mtmp.Checkerboard() ==Odd); _Matrix.Meooe (tmp,Mtmp); assert( Mtmp.Checkerboard() ==Odd);
Mtmp=src_o-Mtmp; Mtmp=src_o-Mtmp;
_Matrix.MooeeInv(Mtmp,tmp); GRID_ASSERT( tmp.Checkerboard() ==Odd); _Matrix.MooeeInv(Mtmp,tmp); assert( tmp.Checkerboard() ==Odd);
// get the right MpcDag // get the right MpcDag
_HermOpEO.MpcDag(tmp,src_o); GRID_ASSERT(src_o.Checkerboard() ==Odd); _HermOpEO.MpcDag(tmp,src_o); assert(src_o.Checkerboard() ==Odd);
} }
virtual void RedBlackSolution(Matrix & _Matrix,const Field &sol_o, const Field &src_e,Field &sol) virtual void RedBlackSolution(Matrix & _Matrix,const Field &sol_o, const Field &src_e,Field &sol)
@@ -560,12 +560,12 @@ namespace Grid {
/////////////////////////////////////////////////// ///////////////////////////////////////////////////
// sol_e = M_ee^-1 * ( src_e - Meo sol_o )... // sol_e = M_ee^-1 * ( src_e - Meo sol_o )...
/////////////////////////////////////////////////// ///////////////////////////////////////////////////
_Matrix.Meooe(sol_o,tmp); GRID_ASSERT( tmp.Checkerboard() ==Even); _Matrix.Meooe(sol_o,tmp); assert( tmp.Checkerboard() ==Even);
tmp = src_e-tmp; GRID_ASSERT( src_e.Checkerboard() ==Even); tmp = src_e-tmp; assert( src_e.Checkerboard() ==Even);
_Matrix.MooeeInv(tmp,sol_e); GRID_ASSERT( sol_e.Checkerboard() ==Even); _Matrix.MooeeInv(tmp,sol_e); assert( sol_e.Checkerboard() ==Even);
setCheckerboard(sol,sol_e); GRID_ASSERT( sol_e.Checkerboard() ==Even); setCheckerboard(sol,sol_e); assert( sol_e.Checkerboard() ==Even);
setCheckerboard(sol,sol_o); GRID_ASSERT( sol_o.Checkerboard() ==Odd ); setCheckerboard(sol,sol_o); assert( sol_o.Checkerboard() ==Odd );
}; };
virtual void RedBlackSolve (Matrix & _Matrix,const Field &src_o, Field &sol_o) virtual void RedBlackSolve (Matrix & _Matrix,const Field &src_o, Field &sol_o)
@@ -612,12 +612,12 @@ namespace Grid {
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
// src_o = Mdag * (source_o - Moe MeeInv source_e) // src_o = Mdag * (source_o - Moe MeeInv source_e)
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
_Matrix.MooeeInv(src_e,tmp); GRID_ASSERT( tmp.Checkerboard() ==Even); _Matrix.MooeeInv(src_e,tmp); assert( tmp.Checkerboard() ==Even);
_Matrix.Meooe (tmp,Mtmp); GRID_ASSERT( Mtmp.Checkerboard() ==Odd); _Matrix.Meooe (tmp,Mtmp); assert( Mtmp.Checkerboard() ==Odd);
tmp=src_o-Mtmp; GRID_ASSERT( tmp.Checkerboard() ==Odd); tmp=src_o-Mtmp; assert( tmp.Checkerboard() ==Odd);
// get the right MpcDag // get the right MpcDag
_HermOpEO.MpcDag(tmp,src_o); GRID_ASSERT(src_o.Checkerboard() ==Odd); _HermOpEO.MpcDag(tmp,src_o); assert(src_o.Checkerboard() ==Odd);
} }
virtual void RedBlackSolution(Matrix & _Matrix,const Field &sol_o, const Field &src_e,Field &sol) virtual void RedBlackSolution(Matrix & _Matrix,const Field &sol_o, const Field &src_e,Field &sol)
@@ -638,12 +638,12 @@ namespace Grid {
/////////////////////////////////////////////////// ///////////////////////////////////////////////////
// sol_e = M_ee^-1 * ( src_e - Meo sol_o )... // sol_e = M_ee^-1 * ( src_e - Meo sol_o )...
/////////////////////////////////////////////////// ///////////////////////////////////////////////////
_Matrix.Meooe(sol_o_i,tmp); GRID_ASSERT( tmp.Checkerboard() ==Even); _Matrix.Meooe(sol_o_i,tmp); assert( tmp.Checkerboard() ==Even);
tmp = src_e-tmp; GRID_ASSERT( src_e.Checkerboard() ==Even); tmp = src_e-tmp; assert( src_e.Checkerboard() ==Even);
_Matrix.MooeeInv(tmp,sol_e); GRID_ASSERT( sol_e.Checkerboard() ==Even); _Matrix.MooeeInv(tmp,sol_e); assert( sol_e.Checkerboard() ==Even);
setCheckerboard(sol,sol_e); GRID_ASSERT( sol_e.Checkerboard() ==Even); setCheckerboard(sol,sol_e); assert( sol_e.Checkerboard() ==Even);
setCheckerboard(sol,sol_o_i); GRID_ASSERT( sol_o_i.Checkerboard() ==Odd ); setCheckerboard(sol,sol_o_i); assert( sol_o_i.Checkerboard() ==Odd );
}; };
virtual void RedBlackSolve (Matrix & _Matrix,const Field &src_o, Field &sol_o) virtual void RedBlackSolve (Matrix & _Matrix,const Field &src_o, Field &sol_o)
@@ -684,9 +684,9 @@ namespace Grid {
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
// src_o = Mdag * (source_o - Moe MeeInv source_e) // src_o = Mdag * (source_o - Moe MeeInv source_e)
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
_Matrix.MooeeInv(src_e, tmp); GRID_ASSERT( tmp.Checkerboard() == Even ); _Matrix.MooeeInv(src_e, tmp); assert( tmp.Checkerboard() == Even );
_Matrix.Meooe (tmp, Mtmp); GRID_ASSERT( Mtmp.Checkerboard() == Odd ); _Matrix.Meooe (tmp, Mtmp); assert( Mtmp.Checkerboard() == Odd );
src_o -= Mtmp; GRID_ASSERT( src_o.Checkerboard() == Odd ); src_o -= Mtmp; assert( src_o.Checkerboard() == Odd );
} }
virtual void RedBlackSolution(Matrix& _Matrix, const Field& sol_o, const Field& src_e, Field& sol) virtual void RedBlackSolution(Matrix& _Matrix, const Field& sol_o, const Field& src_e, Field& sol)
@@ -707,12 +707,12 @@ namespace Grid {
/////////////////////////////////////////////////// ///////////////////////////////////////////////////
// sol_e = M_ee^-1 * ( src_e - Meo sol_o )... // sol_e = M_ee^-1 * ( src_e - Meo sol_o )...
/////////////////////////////////////////////////// ///////////////////////////////////////////////////
_Matrix.Meooe(sol_o_i, tmp); GRID_ASSERT( tmp.Checkerboard() == Even ); _Matrix.Meooe(sol_o_i, tmp); assert( tmp.Checkerboard() == Even );
tmp = src_e - tmp; GRID_ASSERT( src_e.Checkerboard() == Even ); tmp = src_e - tmp; assert( src_e.Checkerboard() == Even );
_Matrix.MooeeInv(tmp, sol_e); GRID_ASSERT( sol_e.Checkerboard() == Even ); _Matrix.MooeeInv(tmp, sol_e); assert( sol_e.Checkerboard() == Even );
setCheckerboard(sol, sol_e); GRID_ASSERT( sol_e.Checkerboard() == Even ); setCheckerboard(sol, sol_e); assert( sol_e.Checkerboard() == Even );
setCheckerboard(sol, sol_o_i); GRID_ASSERT( sol_o_i.Checkerboard() == Odd ); setCheckerboard(sol, sol_o_i); assert( sol_o_i.Checkerboard() == Odd );
}; };
virtual void RedBlackSolve(Matrix& _Matrix, const Field& src_o, Field& sol_o) virtual void RedBlackSolve(Matrix& _Matrix, const Field& src_o, Field& sol_o)
+931
View File
@@ -0,0 +1,931 @@
/*************************************************************************************
Grid physics library, www.github.com/paboyle/Grid
Source file: ./lib/algorithms/iterative/ImplicitlyRestartedLanczos.h
Copyright (C) 2015
Author: Chulwoo Jung <chulwoo@bnl.gov>
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 */
#ifndef GRID_LANC_H
#define GRID_LANC_H
#include <string.h> //memset
#ifdef USE_LAPACK
#ifdef USE_MKL
#include<mkl_lapack.h>
#else
void LAPACK_dstegr (char *jobz, char *range, int *n, double *d, double *e,
double *vl, double *vu, int *il, int *iu, double *abstol,
int *m, double *w, double *z, int *ldz, int *isuppz,
double *work, int *lwork, int *iwork, int *liwork,
int *info);
//#include <lapacke/lapacke.h>
#endif
#endif
//#include <Grid/algorithms/densematrix/DenseMatrix.h>
// eliminate temorary vector in calc()
#define MEM_SAVE
namespace Grid
{
struct Bisection
{
#if 0
static void get_eig2 (int row_num, std::vector < RealD > &ALPHA,
std::vector < RealD > &BETA,
std::vector < RealD > &eig)
{
int i, j;
std::vector < RealD > evec1 (row_num + 3);
std::vector < RealD > evec2 (row_num + 3);
RealD eps2;
ALPHA[1] = 0.;
BETHA[1] = 0.;
for (i = 0; i < row_num - 1; i++)
{
ALPHA[i + 1] = A[i * (row_num + 1)].real ();
BETHA[i + 2] = A[i * (row_num + 1) + 1].real ();
}
ALPHA[row_num] = A[(row_num - 1) * (row_num + 1)].real ();
bisec (ALPHA, BETHA, row_num, 1, row_num, 1e-10, 1e-10, evec1, eps2);
bisec (ALPHA, BETHA, row_num, 1, row_num, 1e-16, 1e-16, evec2, eps2);
// Do we really need to sort here?
int begin = 1;
int end = row_num;
int swapped = 1;
while (swapped)
{
swapped = 0;
for (i = begin; i < end; i++)
{
if (mag (evec2[i]) > mag (evec2[i + 1]))
{
swap (evec2 + i, evec2 + i + 1);
swapped = 1;
}
}
end--;
for (i = end - 1; i >= begin; i--)
{
if (mag (evec2[i]) > mag (evec2[i + 1]))
{
swap (evec2 + i, evec2 + i + 1);
swapped = 1;
}
}
begin++;
}
for (i = 0; i < row_num; i++)
{
for (j = 0; j < row_num; j++)
{
if (i == j)
H[i * row_num + j] = evec2[i + 1];
else
H[i * row_num + j] = 0.;
}
}
}
#endif
static void bisec (std::vector < RealD > &c,
std::vector < RealD > &b,
int n,
int m1,
int m2,
RealD eps1,
RealD relfeh, std::vector < RealD > &x, RealD & eps2)
{
std::vector < RealD > wu (n + 2);
RealD h, q, x1, xu, x0, xmin, xmax;
int i, a, k;
b[1] = 0.0;
xmin = c[n] - fabs (b[n]);
xmax = c[n] + fabs (b[n]);
for (i = 1; i < n; i++)
{
h = fabs (b[i]) + fabs (b[i + 1]);
if (c[i] + h > xmax)
xmax = c[i] + h;
if (c[i] - h < xmin)
xmin = c[i] - h;
}
xmax *= 2.;
eps2 = relfeh * ((xmin + xmax) > 0.0 ? xmax : -xmin);
if (eps1 <= 0.0)
eps1 = eps2;
eps2 = 0.5 * eps1 + 7.0 * (eps2);
x0 = xmax;
for (i = m1; i <= m2; i++)
{
x[i] = xmax;
wu[i] = xmin;
}
for (k = m2; k >= m1; k--)
{
xu = xmin;
i = k;
do
{
if (xu < wu[i])
{
xu = wu[i];
i = m1 - 1;
}
i--;
}
while (i >= m1);
if (x0 > x[k])
x0 = x[k];
while ((x0 - xu) > 2 * relfeh * (fabs (xu) + fabs (x0)) + eps1)
{
x1 = (xu + x0) / 2;
a = 0;
q = 1.0;
for (i = 1; i <= n; i++)
{
q =
c[i] - x1 -
((q != 0.0) ? b[i] * b[i] / q : fabs (b[i]) / relfeh);
if (q < 0)
a++;
}
// printf("x1=%0.14e a=%d\n",x1,a);
if (a < k)
{
if (a < m1)
{
xu = x1;
wu[m1] = x1;
}
else
{
xu = x1;
wu[a + 1] = x1;
if (x[a] > x1)
x[a] = x1;
}
}
else
x0 = x1;
}
printf ("x0=%0.14e xu=%0.14e k=%d\n", x0, xu, k);
x[k] = (x0 + xu) / 2;
}
}
};
/////////////////////////////////////////////////////////////
// Implicitly restarted lanczos
/////////////////////////////////////////////////////////////
template < class Field > class SimpleLanczos
{
const RealD small = 1.0e-16;
public:
int lock;
int get;
int Niter;
int converged;
int Nstop; // Number of evecs checked for convergence
int Nk; // Number of converged sought
int Np; // Np -- Number of spare vecs in kryloc space
int Nm; // Nm -- total number of vectors
RealD OrthoTime;
RealD eresid;
// SortEigen < Field > _sort;
LinearFunction < Field > &_Linop;
// OperatorFunction < Field > &_poly;
/////////////////////////
// Constructor
/////////////////////////
void init (void)
{
};
// void Abort (int ff, std::vector < RealD > &evals, DenseVector < Denstd::vector < RealD > >&evecs);
SimpleLanczos (LinearFunction < Field > &Linop, // op
// OperatorFunction < Field > &poly, // polynmial
int _Nstop, // sought vecs
int _Nk, // sought vecs
int _Nm, // spare vecs
RealD _eresid, // resid in lmdue deficit
int _Niter): // Max iterations
_Linop (Linop),
// _poly (poly),
Nstop (_Nstop), Nk (_Nk), Nm (_Nm), eresid (_eresid), Niter (_Niter)
{
Np = Nm - Nk;
assert (Np > 0);
};
/////////////////////////
// Sanity checked this routine (step) against Saad.
/////////////////////////
void RitzMatrix (std::vector < Field > &evec, int k)
{
if (1)
return;
GridBase *grid = evec[0].Grid();
Field w (grid);
std::cout << GridLogMessage << "RitzMatrix " << std::endl;
for (int i = 0; i < k; i++)
{
_Linop(evec[i], w);
// _poly(_Linop,evec[i],w);
std::cout << GridLogMessage << "[" << i << "] ";
for (int j = 0; j < k; j++)
{
ComplexD in = innerProduct (evec[j], w);
if (fabs ((double) i - j) > 1)
{
if (abs (in) > 1.0e-9)
{
std::cout << GridLogMessage << "oops" << std::endl;
abort ();
}
else
std::cout << GridLogMessage << " 0 ";
}
else
{
std::cout << GridLogMessage << " " << in << " ";
}
}
std::cout << GridLogMessage << std::endl;
}
}
void step (std::vector < RealD > &lmd,
std::vector < RealD > &lme,
Field & last, Field & current, Field & next, uint64_t k)
{
if (lmd.size () <= k)
lmd.resize (k + Nm);
if (lme.size () <= k)
lme.resize (k + Nm);
// _poly(_Linop,current,next ); // 3. wk:=Avk−βkv_{k−1}
_Linop(current, next); // 3. wk:=Avk−βkv_{k−1}
if (k > 0)
{
next -= lme[k - 1] * last;
}
// std::cout<<GridLogMessage << "<last|next>" << innerProduct(last,next) <<std::endl;
ComplexD zalph = innerProduct (current, next); // 4. αk:=(wk,vk)
RealD alph = real (zalph);
next = next - alph * current; // 5. wk:=wk−αkvk
// std::cout<<GridLogMessage << "<current|next>" << innerProduct(current,next) <<std::endl;
RealD beta = normalise (next); // 6. βk+1 := ∥wk∥2. If βk+1 = 0 then Stop
// 7. vk+1 := wk/βk+1
// norm=beta;
int interval = Nm / 100 + 1;
if ((k % interval) == 0)
std::
cout << GridLogMessage << k << " : alpha = " << zalph << " beta " <<
beta << std::endl;
const RealD tiny = 1.0e-20;
if (beta < tiny)
{
std::cout << GridLogMessage << " beta is tiny " << beta << std::
endl;
}
lmd[k] = alph;
lme[k] = beta;
}
void qr_decomp (std::vector < RealD > &lmd,
std::vector < RealD > &lme,
int Nk,
int Nm,
std::vector < RealD > &Qt, RealD Dsh, int kmin, int kmax)
{
int k = kmin - 1;
RealD x;
RealD Fden = 1.0 / hypot (lmd[k] - Dsh, lme[k]);
RealD c = (lmd[k] - Dsh) * Fden;
RealD s = -lme[k] * Fden;
RealD tmpa1 = lmd[k];
RealD tmpa2 = lmd[k + 1];
RealD tmpb = lme[k];
lmd[k] = c * c * tmpa1 + s * s * tmpa2 - 2.0 * c * s * tmpb;
lmd[k + 1] = s * s * tmpa1 + c * c * tmpa2 + 2.0 * c * s * tmpb;
lme[k] = c * s * (tmpa1 - tmpa2) + (c * c - s * s) * tmpb;
x = -s * lme[k + 1];
lme[k + 1] = c * lme[k + 1];
for (int i = 0; i < Nk; ++i)
{
RealD Qtmp1 = Qt[i + Nm * k];
RealD Qtmp2 = Qt[i + Nm * (k + 1)];
Qt[i + Nm * k] = c * Qtmp1 - s * Qtmp2;
Qt[i + Nm * (k + 1)] = s * Qtmp1 + c * Qtmp2;
}
// Givens transformations
for (int k = kmin; k < kmax - 1; ++k)
{
RealD Fden = 1.0 / hypot (x, lme[k - 1]);
RealD c = lme[k - 1] * Fden;
RealD s = -x * Fden;
RealD tmpa1 = lmd[k];
RealD tmpa2 = lmd[k + 1];
RealD tmpb = lme[k];
lmd[k] = c * c * tmpa1 + s * s * tmpa2 - 2.0 * c * s * tmpb;
lmd[k + 1] = s * s * tmpa1 + c * c * tmpa2 + 2.0 * c * s * tmpb;
lme[k] = c * s * (tmpa1 - tmpa2) + (c * c - s * s) * tmpb;
lme[k - 1] = c * lme[k - 1] - s * x;
if (k != kmax - 2)
{
x = -s * lme[k + 1];
lme[k + 1] = c * lme[k + 1];
}
for (int i = 0; i < Nk; ++i)
{
RealD Qtmp1 = Qt[i + Nm * k];
RealD Qtmp2 = Qt[i + Nm * (k + 1)];
Qt[i + Nm * k] = c * Qtmp1 - s * Qtmp2;
Qt[i + Nm * (k + 1)] = s * Qtmp1 + c * Qtmp2;
}
}
}
#if 0
#ifdef USE_LAPACK
#ifdef USE_MKL
#define LAPACK_INT MKL_INT
#else
#define LAPACK_INT long long
#endif
void diagonalize_lapack (std::vector < RealD > &lmd, std::vector < RealD > &lme, int N1, // all
int N2, // get
GridBase * grid)
{
const int size = Nm;
LAPACK_INT NN = N1;
double evals_tmp[NN];
double DD[NN];
double EE[NN];
for (int i = 0; i < NN; i++)
for (int j = i - 1; j <= i + 1; j++)
if (j < NN && j >= 0)
{
if (i == j)
DD[i] = lmd[i];
if (i == j)
evals_tmp[i] = lmd[i];
if (j == (i - 1))
EE[j] = lme[j];
}
LAPACK_INT evals_found;
LAPACK_INT lwork =
((18 * NN) >
(1 + 4 * NN + NN * NN) ? (18 * NN) : (1 + 4 * NN + NN * NN));
LAPACK_INT liwork = 3 + NN * 10;
LAPACK_INT iwork[liwork];
double work[lwork];
LAPACK_INT isuppz[2 * NN];
char jobz = 'N'; // calculate evals only
char range = 'I'; // calculate il-th to iu-th evals
// char range = 'A'; // calculate all evals
char uplo = 'U'; // refer to upper half of original matrix
char compz = 'I'; // Compute eigenvectors of tridiagonal matrix
int ifail[NN];
LAPACK_INT info;
// int total = QMP_get_number_of_nodes();
// int node = QMP_get_node_number();
// GridBase *grid = evec[0]._grid;
int total = grid->_Nprocessors;
int node = grid->_processor;
int interval = (NN / total) + 1;
double vl = 0.0, vu = 0.0;
LAPACK_INT il = interval * node + 1, iu = interval * (node + 1);
if (iu > NN)
iu = NN;
double tol = 0.0;
if (1)
{
memset (evals_tmp, 0, sizeof (double) * NN);
if (il <= NN)
{
printf ("total=%d node=%d il=%d iu=%d\n", total, node, il, iu);
#ifdef USE_MKL
dstegr (&jobz, &range, &NN,
#else
LAPACK_dstegr (&jobz, &range, &NN,
#endif
(double *) DD, (double *) EE, &vl, &vu, &il, &iu, // these four are ignored if second parameteris 'A'
&tol, // tolerance
&evals_found, evals_tmp, (double *) NULL, &NN,
isuppz, work, &lwork, iwork, &liwork, &info);
for (int i = iu - 1; i >= il - 1; i--)
{
printf ("node=%d evals_found=%d evals_tmp[%d] = %g\n", node,
evals_found, i - (il - 1), evals_tmp[i - (il - 1)]);
evals_tmp[i] = evals_tmp[i - (il - 1)];
if (il > 1)
evals_tmp[i - (il - 1)] = 0.;
}
}
{
grid->GlobalSumVector (evals_tmp, NN);
}
}
// cheating a bit. It is better to sort instead of just reversing it, but the document of the routine says evals are sorted in increasing order. qr gives evals in decreasing order.
}
#undef LAPACK_INT
#endif
void diagonalize (std::vector < RealD > &lmd,
std::vector < RealD > &lme,
int N2, int N1, GridBase * grid)
{
#ifdef USE_LAPACK
const int check_lapack = 0; // just use lapack if 0, check against lapack if 1
if (!check_lapack)
return diagonalize_lapack (lmd, lme, N2, N1, grid);
// diagonalize_lapack(lmd2,lme2,Nm2,Nm,Qt,grid);
#endif
}
#endif
static RealD normalise (Field & v)
{
RealD nn = norm2 (v);
nn = sqrt (nn);
v = v * (1.0 / nn);
return nn;
}
void orthogonalize (Field & w, std::vector < Field > &evec, int k)
{
double t0 = -usecond () / 1e6;
typedef typename Field::scalar_type MyComplex;
MyComplex ip;
if (0)
{
for (int j = 0; j < k; ++j)
{
normalise (evec[j]);
for (int i = 0; i < j; i++)
{
ip = innerProduct (evec[i], evec[j]); // are the evecs normalised? ; this assumes so.
evec[j] = evec[j] - ip * evec[i];
}
}
}
for (int j = 0; j < k; ++j)
{
ip = innerProduct (evec[j], w); // are the evecs normalised? ; this assumes so.
w = w - ip * evec[j];
}
normalise (w);
t0 += usecond () / 1e6;
OrthoTime += t0;
}
void setUnit_Qt (int Nm, std::vector < RealD > &Qt)
{
for (int i = 0; i < Qt.size (); ++i)
Qt[i] = 0.0;
for (int k = 0; k < Nm; ++k)
Qt[k + k * Nm] = 1.0;
}
void calc (std::vector < RealD > &eval, const Field & src, int &Nconv)
{
GridBase *grid = src.Grid();
// assert(grid == src._grid);
std::
cout << GridLogMessage << " -- Nk = " << Nk << " Np = " << Np << std::
endl;
std::cout << GridLogMessage << " -- Nm = " << Nm << std::endl;
std::cout << GridLogMessage << " -- size of eval = " << eval.
size () << std::endl;
// assert(c.size() && Nm == eval.size());
std::vector < RealD > lme (Nm);
std::vector < RealD > lmd (Nm);
Field current (grid);
Field last (grid);
Field next (grid);
Nconv = 0;
RealD beta_k;
// Set initial vector
// (uniform vector) Why not src??
// evec[0] = 1.0;
current = src;
std::cout << GridLogMessage << "norm2(src)= " << norm2 (src) << std::
endl;
normalise (current);
std::
cout << GridLogMessage << "norm2(evec[0])= " << norm2 (current) <<
std::endl;
// Initial Nk steps
OrthoTime = 0.;
double t0 = usecond () / 1e6;
RealD norm; // sqrt norm of last vector
uint64_t iter = 0;
bool initted = false;
std::vector < RealD > low (Nstop * 10);
std::vector < RealD > high (Nstop * 10);
RealD cont = 0.;
while (1) {
cont = 0.;
std::vector < RealD > lme2 (Nm);
std::vector < RealD > lmd2 (Nm);
for (uint64_t k = 0; k < Nm; ++k, iter++) {
step (lmd, lme, last, current, next, iter);
last = current;
current = next;
}
double t1 = usecond () / 1e6;
std::cout << GridLogMessage << "IRL::Initial steps: " << t1 -
t0 << "seconds" << std::endl;
t0 = t1;
std::
cout << GridLogMessage << "IRL::Initial steps:OrthoTime " <<
OrthoTime << "seconds" << std::endl;
// getting eigenvalues
lmd2.resize (iter + 2);
lme2.resize (iter + 2);
for (uint64_t k = 0; k < iter; ++k) {
lmd2[k + 1] = lmd[k];
lme2[k + 2] = lme[k];
}
t1 = usecond () / 1e6;
std::cout << GridLogMessage << "IRL:: copy: " << t1 -
t0 << "seconds" << std::endl;
t0 = t1;
{
int total = grid->_Nprocessors;
int node = grid->_processor;
int interval = (Nstop / total) + 1;
int iu = (iter + 1) - (interval * node + 1);
int il = (iter + 1) - (interval * (node + 1));
std::vector < RealD > eval2 (iter + 3);
RealD eps2;
Bisection::bisec (lmd2, lme2, iter, il, iu, 1e-16, 1e-10, eval2,
eps2);
// diagonalize(eval2,lme2,iter,Nk,grid);
RealD diff = 0.;
for (int i = il; i <= iu; i++) {
if (initted)
diff =
fabs (eval2[i] - high[iu-i]) / (fabs (eval2[i]) +
fabs (high[iu-i]));
if (initted && (diff > eresid))
cont = 1.;
if (initted)
printf ("eval[%d]=%0.14e %0.14e, %0.14e\n", i, eval2[i],
high[iu-i], diff);
high[iu-i] = eval2[i];
}
il = (interval * node + 1);
iu = (interval * (node + 1));
Bisection::bisec (lmd2, lme2, iter, il, iu, 1e-16, 1e-10, eval2,
eps2);
for (int i = il; i <= iu; i++) {
if (initted)
diff =
fabs (eval2[i] - low[i]) / (fabs (eval2[i]) +
fabs (low[i]));
if (initted && (diff > eresid))
cont = 1.;
if (initted)
printf ("eval[%d]=%0.14e %0.14e, %0.14e\n", i, eval2[i],
low[i], diff);
low[i] = eval2[i];
}
t1 = usecond () / 1e6;
std::cout << GridLogMessage << "IRL:: diagonalize: " << t1 -
t0 << "seconds" << std::endl;
t0 = t1;
}
for (uint64_t k = 0; k < Nk; ++k) {
// eval[k] = eval2[k];
}
if (initted)
{
grid->GlobalSumVector (&cont, 1);
if (cont < 1.) return;
}
initted = true;
}
}
#if 0
/**
There is some matrix Q such that for any vector y
Q.e_1 = y and Q is unitary.
**/
template < class T >
static T orthQ (DenseMatrix < T > &Q, std::vector < T > y)
{
int N = y.size (); //Matrix Size
Fill (Q, 0.0);
T tau;
for (int i = 0; i < N; i++)
{
Q[i][0] = y[i];
}
T sig = conj (y[0]) * y[0];
T tau0 = fabs (sqrt (sig));
for (int j = 1; j < N; j++)
{
sig += conj (y[j]) * y[j];
tau = abs (sqrt (sig));
if (abs (tau0) > 0.0)
{
T gam = conj ((y[j] / tau) / tau0);
for (int k = 0; k <= j - 1; k++)
{
Q[k][j] = -gam * y[k];
}
Q[j][j] = tau0 / tau;
}
else
{
Q[j - 1][j] = 1.0;
}
tau0 = tau;
}
return tau;
}
/**
There is some matrix Q such that for any vector y
Q.e_k = y and Q is unitary.
**/
template < class T >
static T orthU (DenseMatrix < T > &Q, std::vector < T > y)
{
T tau = orthQ (Q, y);
SL (Q);
return tau;
}
/**
Wind up with a matrix with the first con rows untouched
say con = 2
Q is such that Qdag H Q has {x, x, val, 0, 0, 0, 0, ...} as 1st colum
and the matrix is upper hessenberg
and with f and Q appropriately modidied with Q is the arnoldi factorization
**/
template < class T > static void Lock (DenseMatrix < T > &H, ///Hess mtx
DenseMatrix < T > &Q, ///Lock Transform
T val, ///value to be locked
int con, ///number already locked
RealD small, int dfg, bool herm)
{
//ForceTridiagonal(H);
int M = H.dim;
DenseVector < T > vec;
Resize (vec, M - con);
DenseMatrix < T > AH;
Resize (AH, M - con, M - con);
AH = GetSubMtx (H, con, M, con, M);
DenseMatrix < T > QQ;
Resize (QQ, M - con, M - con);
Unity (Q);
Unity (QQ);
DenseVector < T > evals;
Resize (evals, M - con);
DenseMatrix < T > evecs;
Resize (evecs, M - con, M - con);
Wilkinson < T > (AH, evals, evecs, small);
int k = 0;
RealD cold = abs (val - evals[k]);
for (int i = 1; i < M - con; i++)
{
RealD cnew = abs (val - evals[i]);
if (cnew < cold)
{
k = i;
cold = cnew;
}
}
vec = evecs[k];
ComplexD tau;
orthQ (QQ, vec);
//orthQM(QQ,AH,vec);
AH = Hermitian (QQ) * AH;
AH = AH * QQ;
for (int i = con; i < M; i++)
{
for (int j = con; j < M; j++)
{
Q[i][j] = QQ[i - con][j - con];
H[i][j] = AH[i - con][j - con];
}
}
for (int j = M - 1; j > con + 2; j--)
{
DenseMatrix < T > U;
Resize (U, j - 1 - con, j - 1 - con);
DenseVector < T > z;
Resize (z, j - 1 - con);
T nm = norm (z);
for (int k = con + 0; k < j - 1; k++)
{
z[k - con] = conj (H (j, k + 1));
}
normalise (z);
RealD tmp = 0;
for (int i = 0; i < z.size () - 1; i++)
{
tmp = tmp + abs (z[i]);
}
if (tmp < small / ((RealD) z.size () - 1.0))
{
continue;
}
tau = orthU (U, z);
DenseMatrix < T > Hb;
Resize (Hb, j - 1 - con, M);
for (int a = 0; a < M; a++)
{
for (int b = 0; b < j - 1 - con; b++)
{
T sum = 0;
for (int c = 0; c < j - 1 - con; c++)
{
sum += H[a][con + 1 + c] * U[c][b];
} //sum += H(a,con+1+c)*U(c,b);}
Hb[b][a] = sum;
}
}
for (int k = con + 1; k < j; k++)
{
for (int l = 0; l < M; l++)
{
H[l][k] = Hb[k - 1 - con][l];
}
} //H(Hb[k-1-con][l] , l,k);}}
DenseMatrix < T > Qb;
Resize (Qb, M, M);
for (int a = 0; a < M; a++)
{
for (int b = 0; b < j - 1 - con; b++)
{
T sum = 0;
for (int c = 0; c < j - 1 - con; c++)
{
sum += Q[a][con + 1 + c] * U[c][b];
} //sum += Q(a,con+1+c)*U(c,b);}
Qb[b][a] = sum;
}
}
for (int k = con + 1; k < j; k++)
{
for (int l = 0; l < M; l++)
{
Q[l][k] = Qb[k - 1 - con][l];
}
} //Q(Qb[k-1-con][l] , l,k);}}
DenseMatrix < T > Hc;
Resize (Hc, M, M);
for (int a = 0; a < j - 1 - con; a++)
{
for (int b = 0; b < M; b++)
{
T sum = 0;
for (int c = 0; c < j - 1 - con; c++)
{
sum += conj (U[c][a]) * H[con + 1 + c][b];
} //sum += conj( U(c,a) )*H(con+1+c,b);}
Hc[b][a] = sum;
}
}
for (int k = 0; k < M; k++)
{
for (int l = con + 1; l < j; l++)
{
H[l][k] = Hc[k][l - 1 - con];
}
} //H(Hc[k][l-1-con] , l,k);}}
}
}
#endif
};
}
#endif
+10 -33
View File
@@ -66,21 +66,7 @@ public:
{ {
}; };
void GlobalOrthonormalise(void)
{
// Normalise all vectors
for(int i=0;i<nbasis; i++){
RealD scale = std::pow(norm2(subspace[i]),-0.5);
subspace[i] = subspace[i]*scale;
}
for(int i=0;i<nbasis; i++){
for(int j=0;j<i; j++){
basisOrthogonalize(subspace,subspace[i],j);
}
RealD scale = std::pow(norm2(subspace[i]),-0.5);
subspace[i] = subspace[i]*scale;
}
}
void Orthogonalise(void){ void Orthogonalise(void){
CoarseScalar InnerProd(CoarseGrid); CoarseScalar InnerProd(CoarseGrid);
// std::cout << GridLogMessage <<" Block Gramm-Schmidt pass 1"<<std::endl; // std::cout << GridLogMessage <<" Block Gramm-Schmidt pass 1"<<std::endl;
@@ -111,7 +97,7 @@ public:
RealD scale; RealD scale;
ConjugateGradient<FineField> CG(1.0e-4,2000,false); ConjugateGradient<FineField> CG(1.0e-3,400,false);
FineField noise(FineGrid); FineField noise(FineGrid);
FineField Mn(FineGrid); FineField Mn(FineGrid);
@@ -124,16 +110,14 @@ public:
hermop.Op(noise,Mn); std::cout<<GridLogMessage << "noise ["<<b<<"] <n|MdagM|n> "<<norm2(Mn)<<std::endl; hermop.Op(noise,Mn); std::cout<<GridLogMessage << "noise ["<<b<<"] <n|MdagM|n> "<<norm2(Mn)<<std::endl;
for(int i=0;i<2;i++){ for(int i=0;i<4;i++){
CG(hermop,noise,subspace[b]); CG(hermop,noise,subspace[b]);
noise = subspace[b]; noise = subspace[b];
scale = std::pow(norm2(noise),-0.5); scale = std::pow(norm2(noise),-0.5);
noise=noise*scale; noise=noise*scale;
hermop.Op(noise,Mn); std::cout<<GridLogMessage << "intermediate["<<i<<"] <i|MdagM|i> "<<norm2(Mn)<<std::endl;
} }
hermop.Op(noise,Mn); std::cout<<GridLogMessage << "filtered["<<b<<"] <f|MdagM|f> "<<norm2(Mn)<<std::endl; hermop.Op(noise,Mn); std::cout<<GridLogMessage << "filtered["<<b<<"] <f|MdagM|f> "<<norm2(Mn)<<std::endl;
@@ -147,11 +131,7 @@ public:
RealD scale; RealD scale;
TrivialPrecon<FineField> simple_fine; TrivialPrecon<FineField> simple_fine;
// PrecGeneralisedConjugateResidualNonHermitian<FineField> GCR(0.001,10,DiracOp,simple_fine,30,30); PrecGeneralisedConjugateResidualNonHermitian<FineField> GCR(0.001,30,DiracOp,simple_fine,12,12);
// PrecGeneralisedConjugateResidualNonHermitian<FineField> GCR(0.001,10,DiracOp,simple_fine,12,12);
// PrecGeneralisedConjugateResidualNonHermitian<FineField> GCR(0.001,30,DiracOp,simple_fine,12,12);
// PrecGeneralisedConjugateResidualNonHermitian<FineField> GCR(0.0005,30,DiracOp,simple_fine,20,20);
PrecGeneralisedConjugateResidualNonHermitian<FineField> GCR(0.0005,30,DiracOp,simple_fine,10,10);
FineField noise(FineGrid); FineField noise(FineGrid);
FineField src(FineGrid); FineField src(FineGrid);
FineField guess(FineGrid); FineField guess(FineGrid);
@@ -166,16 +146,16 @@ public:
DiracOp.Op(noise,Mn); std::cout<<GridLogMessage << "noise ["<<b<<"] <n|Op|n> "<<innerProduct(noise,Mn)<<std::endl; DiracOp.Op(noise,Mn); std::cout<<GridLogMessage << "noise ["<<b<<"] <n|Op|n> "<<innerProduct(noise,Mn)<<std::endl;
for(int i=0;i<3;i++){ for(int i=0;i<2;i++){
// void operator() (const Field &src, Field &psi){ // void operator() (const Field &src, Field &psi){
#if 1 #if 1
if (i==0)std::cout << GridLogMessage << " inverting on noise "<<std::endl; std::cout << GridLogMessage << " inverting on noise "<<std::endl;
src = noise; src = noise;
guess=Zero(); guess=Zero();
GCR(src,guess); GCR(src,guess);
subspace[b] = guess; subspace[b] = guess;
#else #else
if (i==0)std::cout << GridLogMessage << " inverting on zero "<<std::endl; std::cout << GridLogMessage << " inverting on zero "<<std::endl;
src=Zero(); src=Zero();
guess = noise; guess = noise;
GCR(src,guess); GCR(src,guess);
@@ -184,16 +164,13 @@ public:
noise = subspace[b]; noise = subspace[b];
scale = std::pow(norm2(noise),-0.5); scale = std::pow(norm2(noise),-0.5);
noise=noise*scale; noise=noise*scale;
DiracOp.Op(noise,Mn); std::cout<<GridLogMessage << "intermediate["<<i<<"] <f|Op|f> "<<innerProduct(noise,Mn)<<" <f|OpDagOp|f>"<<norm2(Mn)<<std::endl;
} }
DiracOp.Op(noise,Mn); std::cout<<GridLogMessage << "filtered["<<b<<"] <f|Op|f> "<<innerProduct(noise,Mn)<<" <f|OpDagOp|f>"<<norm2(Mn)<<std::endl; DiracOp.Op(noise,Mn); std::cout<<GridLogMessage << "filtered["<<b<<"] <f|Op|f> "<<innerProduct(noise,Mn)<<std::endl;
subspace[b] = noise; subspace[b] = noise;
} }
GlobalOrthonormalise();
} }
//////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////
@@ -315,7 +292,7 @@ public:
} }
} }
GRID_ASSERT(b==nn); assert(b==nn);
} }
-215
View File
@@ -1,215 +0,0 @@
/*************************************************************************************
Grid physics library, www.github.com/paboyle/Grid
Source file: ./Grid/algorithms/multigrid/BlockCyclic.h
Copyright (C) 2026
Author: Peter Boyle <pboyle@bnl.gov>
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 */
#pragma once
NAMESPACE_BEGIN(Grid);
///////////////////////////////////////////////////////////////////////////////
// BlockCyclicLayout: the index arithmetic of a 2D block-cyclic distribution
// of an N x N matrix over a Pr x Pc logical process grid with block size nb.
//
// This is stage 1 of the 2D distributed dense inverse
// (documentation/DistributedDenseInverse2D.tex). It is deliberately
// COMMUNICATOR-FREE: every mapping is a static pure function of
// (N, nb, Pr, Pc), so the whole layout is exhaustively unit-testable on one
// rank with no MPI in the loop (Test_blockcyclic). A thin instance layer
// binds a world rank to a grid coordinate and caches local extents.
//
// Conventions (fixed here, relied on by every later stage):
//
// * Global block b of a dimension with Pg processes is owned by process
// coordinate b % Pg (ScaLAPACK csrc=0), and is that process's local
// block b / Pg.
// * Rank <-> grid coordinate is ROW MAJOR over the process grid:
// rank = p*Pc + q , p = rank/Pc , q = rank%Pc .
// The eventual ring transport must construct its neighbour tables with
// the same convention.
// * Local storage is COLUMN MAJOR with ld = mloc, matching BlockRows:
// local element (i,j) lives at data[i + j*mloc].
// * The trailing partial block (N % nb != 0) belongs to the owner of the
// last full-size block position; only that one block is short.
//
// Element (gi,gj) therefore lives on grid coordinate
// ( (gi/nb) % Pr , (gj/nb) % Pc )
// at local coordinate
// ( ((gi/nb)/Pr)*nb + gi%nb , ((gj/nb)/Pc)*nb + gj%nb ).
//
// Everything here is host-side integer arithmetic; nothing allocates.
///////////////////////////////////////////////////////////////////////////////
class BlockCyclicLayout
{
public:
///////////////////////////////////////////////////////////////////////////
// Closest-to-square factorisation Pr*Pc == P with Pr <= Pc.
// For fixed P the per-rank SUMMA volume N^2 (1/Pr + 1/Pc) is minimised
// at the most square grid. P=288 -> 16 x 18.
///////////////////////////////////////////////////////////////////////////
static void ChooseProcessGrid(int P, int &Pr, int &Pc)
{
GRID_ASSERT(P >= 1);
Pr = 1;
for(int r=1; (int64_t)r*r <= (int64_t)P; r++)
if ( P % r == 0 ) Pr = r;
Pc = P / Pr;
}
///////////////////////////////////////////////////////////////////////////
// Number of rows (or columns) of a dimension of global extent N, block nb,
// owned by process coordinate p of Pg. ScaLAPACK "numroc", csrc=0.
///////////////////////////////////////////////////////////////////////////
static int64_t NumLocal(int64_t N, int64_t nb, int p, int Pg)
{
GRID_ASSERT(N >= 0);
GRID_ASSERT(nb >= 1);
GRID_ASSERT(p >= 0);
GRID_ASSERT(p < Pg);
int64_t nblocks = N / nb; // full blocks
int64_t extra = N % nb; // trailing partial block
int64_t full = nblocks / Pg; // full blocks everyone owns
int64_t rem = nblocks % Pg; // coords [0,rem) own one more
int64_t n = full*nb;
if ( p < (int)rem ) n += nb; // an extra full block
if ( p == (int)rem ) n += extra; // the partial block, if any
return n;
}
///////////////////////////////////////////////////////////////////////////
// Global index -> (owner coordinate, local index) in one dimension.
///////////////////////////////////////////////////////////////////////////
static void GlobalToLocal(int64_t g, int64_t nb, int Pg,
int &owner, int64_t &loc)
{
GRID_ASSERT(g >= 0);
int64_t b = g / nb; // global block
owner = (int)(b % Pg);
loc = (b / Pg)*nb + (g % nb);
}
///////////////////////////////////////////////////////////////////////////
// (process coordinate, local index) -> global index in one dimension.
// Inverse of GlobalToLocal on the owned set.
///////////////////////////////////////////////////////////////////////////
static int64_t LocalToGlobal(int64_t l, int64_t nb, int p, int Pg)
{
GRID_ASSERT(l >= 0);
int64_t lb = l / nb; // local block
int64_t b = lb*Pg + p; // global block
return b*nb + (l % nb);
}
///////////////////////////////////////////////////////////////////////////
// Instance layer: bind a rank of a Pr x Pc grid.
///////////////////////////////////////////////////////////////////////////
int64_t N; // global matrix dimension (square)
int64_t nb; // block size
int Pr, Pc; // process grid
int me; // world rank within the grid, row major
int prow, pcol;// my grid coordinate
int64_t mloc,nloc; // my local extents; storage column major, ld = mloc
BlockCyclicLayout(int64_t N_, int64_t nb_, int Pr_, int Pc_, int me_)
{
N = N_;
nb = nb_;
Pr = Pr_;
Pc = Pc_;
me = me_;
GRID_ASSERT( N >= 0 );
GRID_ASSERT( nb >= 1 );
GRID_ASSERT( Pr >= 1 );
GRID_ASSERT( Pc >= 1 );
GRID_ASSERT( me >= 0 );
GRID_ASSERT( me < Pr*Pc );
prow = me / Pc; // ROW MAJOR rank convention
pcol = me % Pc;
mloc = NumLocal(N, nb, prow, Pr);
nloc = NumLocal(N, nb, pcol, Pc);
}
// Owning rank of global element (gi,gj), row-major rank convention.
int OwnerRank(int64_t gi, int64_t gj) const
{
int pr,pc; int64_t li,lj;
GlobalToLocal(gi, nb, Pr, pr, li);
GlobalToLocal(gj, nb, Pc, pc, lj);
return pr*Pc + pc;
}
// My local storage offset of global element (gi,gj).
// The caller must know I own it; asserted, not assumed.
int64_t LocalOffset(int64_t gi, int64_t gj) const
{
int pr,pc; int64_t li,lj;
GlobalToLocal(gi, nb, Pr, pr, li);
GlobalToLocal(gj, nb, Pc, pc, lj);
GRID_ASSERT( pr == prow );
GRID_ASSERT( pc == pcol );
return li + lj*mloc; // column major, ld = mloc
}
// Do I own global element (gi,gj)?
int Owns(int64_t gi, int64_t gj) const
{
return OwnerRank(gi,gj) == me;
}
///////////////////////////////////////////////////////////////////////////
// Block-aligned global range [g0,g1) -> my contiguous local range [l0,l1).
//
// For fixed owner p the local index is monotone in the global index, so a
// coordinate's owned elements of ANY global range are contiguous in local
// storage; and for a BLOCK-ALIGNED range the bounds are exactly
// NumLocal(g0) and NumLocal(g1), because NumLocal(g,...) counts the owned
// elements below g. This is what lets a windowed product view the local
// sub-matrix of a global window as &data[l0 + c0*mloc] with the SAME ld --
// no gather, no copy. Verified exhaustively in Test_blockcyclic T7.
//
// g0 must be a block multiple; g1 a block multiple or N itself.
///////////////////////////////////////////////////////////////////////////
static void RangeToLocal(int64_t g0, int64_t g1,
int64_t N, int64_t nb, int p, int Pg,
int64_t &l0, int64_t &l1)
{
GRID_ASSERT( 0 <= g0 );
GRID_ASSERT( g0 <= g1 );
GRID_ASSERT( g1 <= N );
GRID_ASSERT( g0 % nb == 0 );
GRID_ASSERT( (g1 % nb == 0) || (g1 == N) );
l0 = NumLocal(g0, nb, p, Pg);
l1 = NumLocal(g1, nb, p, Pg);
}
// Instance forms, rows and columns of my own coordinate.
void RowRange(int64_t g0, int64_t g1, int64_t &l0, int64_t &l1) const
{ RangeToLocal(g0,g1,N,nb,prow,Pr,l0,l1); }
void ColRange(int64_t g0, int64_t g1, int64_t &l0, int64_t &l1) const
{ RangeToLocal(g0,g1,N,nb,pcol,Pc,l0,l1); }
// Size of global block b (the trailing block may be short).
int64_t BlockSize(int64_t b) const
{
int64_t lo = b*nb;
GRID_ASSERT( lo < N );
return std::min(N, lo+nb) - lo;
}
};
NAMESPACE_END(Grid);
@@ -1,247 +0,0 @@
/*************************************************************************************
Grid physics library, www.github.com/paboyle/Grid
Source file: ./Grid/algorithms/multigrid/BlockCyclicRedistribute.h
Copyright (C) 2026
Author: Peter Boyle <pboyle@bnl.gov>
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 */
#pragma once
#include <Grid/algorithms/multigrid/BlockCyclicSumma.h>
NAMESPACE_BEGIN(Grid);
///////////////////////////////////////////////////////////////////////////////
// Stage 4 of the 2D distributed dense inverse: redistribution between the
// 1D rank-major row layout (BlockRows: rank r owns contiguous global rows
// [rowStart[r], rowStart[r+1]) of an N x N matrix, stored rows x N column
// major with ld = rows) and the 2D block-cyclic layout.
//
// This is what lets the EXISTING stencil->dense import, its certificate,
// the fp32 slab conversion and the apply path all remain byte-for-byte
// untouched: the 2D inverse slots between them as
//
// RowsToCyclic -> BlockCyclicSchurInverse::Invert -> CyclicToRows
//
// Volume is one matrix pass each way -- N^2/P elements per rank (~1 GB at
// production), trivial against the inversion itself.
//
// Transport: PURE POINT-TO-POINT, like everything else in this stack.
// Ranks exchange in a round-robin TOURNAMENT (the circle method, on an odd
// modulus M so it covers every pair exactly once for any P, with byes):
// at round r, ranks x and y are partners iff x+y == r (mod M). Each
// meeting handles both directed edges of the pair in ONE SendToRecvFrom,
// padded to the larger of the two edge sizes -- SendToRecvFrom carries a
// single byte count for both directions, and both endpoints compute the
// same max from the shared descriptors, so there is no asymmetric-size
// case and no zero-count shape. Pairs with nothing to exchange skip the
// round, decided identically at both ends.
//
// Element enumeration within an edge is canonical -- ascending global
// column outer, ascending global row inner -- and each endpoint builds its
// OWN local offset tables from the shared descriptors, so no index data is
// ever transmitted. The round trip is BITWISE exact (pure data movement,
// no arithmetic): Test_schur2d_redist proves it.
///////////////////////////////////////////////////////////////////////////////
class BlockCyclicRedistribute
{
public:
/////////////////////////////////////////////////////////////////////////
// The directed edge (1D rank r1, 2D rank r2): global rows of r1's range
// whose row-block coordinate is r2's prow; ALL global columns whose
// column-block coordinate is r2's pcol. Every rank can enumerate any
// edge from (rowStart, layout) alone.
/////////////////////////////////////////////////////////////////////////
static void EdgeRows(const std::vector<int64_t> &rowStart, int r1,
const BlockCyclicLayout &L, int r2,
std::vector<int64_t> &rows)
{
rows.clear();
int p = r2 / L.Pc; // row-major rank convention
for(int64_t i=rowStart[r1]; i<rowStart[r1+1]; i++)
if ( (int)((i/L.nb) % L.Pr) == p ) rows.push_back(i);
}
static void EdgeCols(const BlockCyclicLayout &L, int r2,
std::vector<int64_t> &cols)
{
cols.clear();
int q = r2 % L.Pc;
for(int64_t b=0; b*L.nb<L.N; b++){
if ( (int)(b % L.Pc) != q ) continue;
int64_t g0=b*L.nb, g1=std::min(L.N,(b+1)*L.nb);
for(int64_t j=g0;j<g1;j++) cols.push_back(j);
}
}
/////////////////////////////////////////////////////////////////////////
// Gather/scatter one edge between a matrix (device, column major, ld)
// and a dense edge buffer, through device offset tables.
// buffer(a,b) = elem(rows[a], cols[b]), a fastest.
/////////////////////////////////////////////////////////////////////////
static void MoveEdge(int toBuffer,
ComplexD *mat, int64_t ld,
const std::vector<int64_t> &roff, // per-row offset in mat
const std::vector<int64_t> &coff, // per-col offset in mat
ComplexD *buf)
{
int64_t nr = roff.size();
int64_t nc = coff.size();
if ( !(nr && nc) ) return;
deviceVector<int64_t> dro(nr), dco(nc);
acceleratorCopyToDevice((void *)&roff[0], (void *)&dro[0], nr*sizeof(int64_t));
acceleratorCopyToDevice((void *)&coff[0], (void *)&dco[0], nc*sizeof(int64_t));
int64_t *ro = &dro[0];
int64_t *co = &dco[0];
if ( toBuffer ) {
accelerator_for(idx, (uint64_t)(nr*nc), 1, {
int64_t b = idx / nr;
int64_t a = idx - b*nr;
buf[a + b*nr] = mat[ ro[a] + co[b]*ld ];
});
} else {
accelerator_for(idx, (uint64_t)(nr*nc), 1, {
int64_t b = idx / nr;
int64_t a = idx - b*nr;
mat[ ro[a] + co[b]*ld ] = buf[a + b*nr];
});
}
}
/////////////////////////////////////////////////////////////////////////
// My offset tables for an edge, on whichever side I am.
/////////////////////////////////////////////////////////////////////////
static void Offsets1D(const std::vector<int64_t> &rows,
const std::vector<int64_t> &cols,
int64_t row0,
std::vector<int64_t> &roff, std::vector<int64_t> &coff)
{
roff.resize(rows.size()); coff.resize(cols.size());
for(uint64_t a=0;a<rows.size();a++) roff[a] = rows[a]-row0; // local row
for(uint64_t b=0;b<cols.size();b++) coff[b] = cols[b]; // global col
}
static void Offsets2D(const BlockCyclicLayout &L,
const std::vector<int64_t> &rows,
const std::vector<int64_t> &cols,
std::vector<int64_t> &roff, std::vector<int64_t> &coff)
{
roff.resize(rows.size()); coff.resize(cols.size());
for(uint64_t a=0;a<rows.size();a++){
int p; int64_t l;
BlockCyclicLayout::GlobalToLocal(rows[a], L.nb, L.Pr, p, l);
GRID_ASSERT( p == L.prow );
roff[a] = l;
}
for(uint64_t b=0;b<cols.size();b++){
int q; int64_t l;
BlockCyclicLayout::GlobalToLocal(cols[b], L.nb, L.Pc, q, l);
GRID_ASSERT( q == L.pcol );
coff[b] = l;
}
}
/////////////////////////////////////////////////////////////////////////
// The worker. dir=+1 : 1D rows -> block cyclic ; dir=-1 : back.
/////////////////////////////////////////////////////////////////////////
static void Redistribute(int dir, GridBase *grid,
const std::vector<int64_t> &rowStart,
ComplexD *rows1d, int64_t myrows,
BlockCyclicMatrix &A)
{
BlockCyclicLayout &L = A.layout;
int P = grid->ProcessorCount();
int me = grid->ThisRank();
GRID_ASSERT( (int)rowStart.size() == P+1 );
GRID_ASSERT( rowStart[P] == L.N );
GRID_ASSERT( rowStart[me+1]-rowStart[me] == myrows );
int64_t row0 = rowStart[me];
int64_t ld1 = myrows ? myrows : 1;
std::vector<int64_t> rows, cols, roff, coff;
deviceVector<ComplexD> sbuf(1), rbuf(1);
///////////////////////////////////////////////////////////////////////
// Self edge: purely local, via a bounce buffer (shares all the code).
///////////////////////////////////////////////////////////////////////
EdgeRows(rowStart, me, L, me, rows);
EdgeCols(L, me, cols);
if ( rows.size() && cols.size() ){
uint64_t ne = rows.size()*cols.size();
if ( sbuf.size() < ne ) sbuf.resize(ne);
std::vector<int64_t> roff2, coff2;
Offsets1D(rows, cols, row0, roff, coff);
Offsets2D(L, rows, cols, roff2, coff2);
if ( dir > 0 ) {
MoveEdge(1, rows1d, ld1, roff, coff, &sbuf[0]);
MoveEdge(0, &A.data[0], L.mloc, roff2, coff2, &sbuf[0]);
} else {
MoveEdge(1, &A.data[0], L.mloc, roff2, coff2, &sbuf[0]);
MoveEdge(0, rows1d, ld1, roff, coff, &sbuf[0]);
}
}
///////////////////////////////////////////////////////////////////////
// Tournament over all pairs: odd modulus M, partner = (r - me) mod M.
// Every unordered pair meets exactly once; partner==me or >=P is a bye.
///////////////////////////////////////////////////////////////////////
int M = (P%2) ? P : P+1;
for(int r=0;r<M;r++){
int partner = (int)(((int64_t)r - me + 2L*M) % M);
if ( partner == me || partner >= P ) continue;
// outbound edge: my (dir>0 ? 1D rows : 2D data) -> partner
// inbound edge: partner -> my (dir>0 ? 2D data : 1D rows)
std::vector<int64_t> orow, ocol, irow, icol;
if ( dir > 0 ) { EdgeRows(rowStart, me, L, partner, orow); EdgeCols(L, partner, ocol);
EdgeRows(rowStart, partner, L, me, irow); EdgeCols(L, me, icol); }
else { EdgeRows(rowStart, partner, L, me, orow); EdgeCols(L, me, ocol);
EdgeRows(rowStart, me, L, partner, irow); EdgeCols(L, partner, icol); }
uint64_t nout = orow.size()*ocol.size();
uint64_t nin = irow.size()*icol.size();
if ( !(nout || nin) ) continue; // both ends compute this identically
uint64_t nmax = std::max(nout,nin); // symmetric padded transfer
if ( sbuf.size() < nmax ) sbuf.resize(nmax);
if ( rbuf.size() < nmax ) rbuf.resize(nmax);
if ( nout ){
if ( dir > 0 ) { Offsets1D(orow, ocol, row0, roff, coff);
MoveEdge(1, rows1d, ld1, roff, coff, &sbuf[0]); }
else { Offsets2D(L, orow, ocol, roff, coff);
MoveEdge(1, &A.data[0], L.mloc, roff, coff, &sbuf[0]); }
}
grid->SendToRecvFrom((void *)&sbuf[0], partner,
(void *)&rbuf[0], partner,
nmax*sizeof(ComplexD));
if ( nin ){
if ( dir > 0 ) { Offsets2D(L, irow, icol, roff, coff);
MoveEdge(0, &A.data[0], L.mloc, roff, coff, &rbuf[0]); }
else { Offsets1D(irow, icol, row0, roff, coff);
MoveEdge(0, rows1d, ld1, roff, coff, &rbuf[0]); }
}
}
}
static void RowsToCyclic(GridBase *grid, const std::vector<int64_t> &rowStart,
ComplexD *rows1d, int64_t myrows, BlockCyclicMatrix &A)
{ Redistribute(+1, grid, rowStart, rows1d, myrows, A); }
static void CyclicToRows(GridBase *grid, const std::vector<int64_t> &rowStart,
BlockCyclicMatrix &A, ComplexD *rows1d, int64_t myrows)
{ Redistribute(-1, grid, rowStart, rows1d, myrows, A); }
};
NAMESPACE_END(Grid);
@@ -1,311 +0,0 @@
/*************************************************************************************
Grid physics library, www.github.com/paboyle/Grid
Source file: ./Grid/algorithms/multigrid/BlockCyclicSchurInverse.h
Copyright (C) 2026
Author: Peter Boyle <pboyle@bnl.gov>
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 */
#pragma once
#include <Grid/algorithms/blas/BatchedInverse.h>
#include <Grid/algorithms/multigrid/BlockCyclicSumma.h>
NAMESPACE_BEGIN(Grid);
///////////////////////////////////////////////////////////////////////////////
// Stage 3 of the 2D distributed dense inverse: the recursive Schur
// complement on a block-cyclic matrix, in place.
//
// The nine-step algebra is IDENTICAL to RecursiveSchurInverse (1D); what
// changes is the decomposition. The recursion splits the GLOBAL INDEX
// RANGE at the block boundary nearest the midpoint -- not the rank range --
// so every rank owns part of every sub-block at every depth, and the
// ownership gating (inI/inJ, dummy operands, zero-width rank ranges) of the
// 1D scheme has no analogue here: it is simply gone.
//
// I = [c0,m) J = [m,c1) (block-aligned, m the mid block boundary)
// 1. recurse I : A11 -> A11inv (in place)
// 2. Bt = A11inv . A12 (scratch, I x J)
// 3. Ct = A21 . A11inv (scratch, J x I)
// 4. A22 -= A21 . Bt == S (in place)
// 5. recurse J : S -> Sinv (in place)
// 6. Tt = Sinv . Ct (scratch, J x I)
// 7. Ut = Bt . Sinv (scratch, I x J)
// 8. A11 += Ut . Ct == X11 (in place)
// 9. A12 = -Ut , A21 = -Tt (window copies)
//
// SCRATCH SHARING. Four full-size block-cyclic scratch matrices (Bt, Ct,
// Tt, Ut) serve the ENTIRE tree, used through windows. This is safe at
// every depth because of a window-disjointness invariant:
//
// * every temporary of a node has its row range in one half of the
// node's window and its column range in the other (I x J or J x I);
// * everything any DESCENDANT touches -- its A windows and its own
// temporaries -- has BOTH ranges inside a single half (I x I during
// step 1, J x J during step 5).
//
// Hence a descendant window and a live ancestor temporary always differ in
// at least one dimension by disjoint ranges. Only Bt and Ct are live
// across the step-5 recursion (Tt, Ut are written after it), and both are
// covered by the invariant.
//
// LEAF. A leaf is a single diagonal block, and block (b,b) of a
// block-cyclic layout lives ENTIRELY on rank (b%Pr, b%Pc). The leaf
// inversion is therefore purely local -- pack the strided block dense,
// GridBLASInverse, unpack -- with NO communication and no assembly. The
// leaf-assembly transport question of the 1D scheme does not arise.
// Successive leaves cycle over ranks, so leaf work is naturally spread.
//
// COMMUNICATION. Every transfer in the whole inversion is a
// SendToRecvFrom inside BlockCyclicSumma's rings: pure point-to-point, no
// collectives on the critical path, deterministic summation order (so
// repeated inversions are bitwise identical). ReportTelemetry() is the
// one optional exception: it performs reductions, and is only ever called
// explicitly by a caller who wants the numbers.
//
// NUMERICS. No pivoting, exactly as the 1D scheme: every A11 and every
// Schur complement met on the way down must be non-singular. The growth
// telemetry stands in for pivoting; note the recursion splits differently
// from the 1D rank-range tree, so DIFFERENT sub-blocks are inverted and
// telemetry values are NOT comparable with the 1D implementation's --
// re-baseline, do not compare.
///////////////////////////////////////////////////////////////////////////////
class BlockCyclicSchurInverse
{
public:
BlockCyclicSumma SUMMA;
GridBLASInverse INV;
// Telemetry: accumulated LOCALLY, no comms unless ReportTelemetry().
double telLeafMaxInv;
uint64_t nLeaf;
uint64_t nNode;
double tLeaf;
double tGemm; // wall in Multiply calls (comms+gemm)
double tCopy;
BlockCyclicSchurInverse()
{
telLeafMaxInv = 0.0;
nLeaf = nNode = 0;
tLeaf = tGemm = tCopy = 0.0;
}
///////////////////////////////////////////////////////////////////////////
// Window copy-scale: Dst[i0:i1, j0:j1] = alpha * Src[same window].
// Both share one layout, so the local bands coincide; pure local kernel.
///////////////////////////////////////////////////////////////////////////
void WindowCopyScale(ComplexD alpha,
BlockCyclicMatrix &Src, BlockCyclicMatrix &Dst,
int64_t i0, int64_t i1, int64_t j0, int64_t j1)
{
BlockCyclicLayout &L = Dst.layout;
GRID_ASSERT( Src.layout.N==L.N && Src.layout.nb==L.nb );
GRID_ASSERT( Src.layout.Pr==L.Pr && Src.layout.Pc==L.Pc );
int64_t li0,li1, lj0,lj1;
L.RowRange(i0,i1, li0,li1);
L.ColRange(j0,j1, lj0,lj1);
int64_t m = li1-li0, n = lj1-lj0;
if ( !(m && n) ) return;
ComplexD *src = Src.LocalWindow(li0,lj0);
ComplexD *dst = Dst.LocalWindow(li0,lj0);
int64_t ldS = Src.layout.mloc;
int64_t ldD = L.mloc;
tCopy -= usecond();
accelerator_for(idx, (uint64_t)(m*n), 1, {
int64_t jj = idx / m;
int64_t ii = idx - jj*m;
dst[ii + jj*ldD] = alpha*src[ii + jj*ldS];
});
tCopy += usecond();
}
///////////////////////////////////////////////////////////////////////////
// Leaf: single diagonal block (b,b), entirely on rank (b%Pr, b%Pc).
// Local pack -> dense inverse -> unpack; every other rank does nothing
// and needs no synchronisation: the next SUMMA's rings pair them up.
///////////////////////////////////////////////////////////////////////////
void Leaf(BlockCyclicMatrix &A, int64_t b)
{
GRID_TRACE("SchurLeaf");
BlockCyclicLayout &L = A.layout;
nLeaf++;
if ( (int)(b % L.Pr) != L.prow ) return;
if ( (int)(b % L.Pc) != L.pcol ) return;
tLeaf -= usecond();
int64_t g0 = b*L.nb;
int64_t g1 = std::min(L.N, g0+L.nb);
int64_t w = g1-g0;
int64_t lr0,lr1, lc0,lc1;
L.RowRange(g0,g1, lr0,lr1);
L.ColRange(g0,g1, lc0,lc1);
GRID_ASSERT( lr1-lr0 == w );
GRID_ASSERT( lc1-lc0 == w );
// Pack the strided block dense (inverseBatched assumes lda == w).
deviceVector<ComplexD> dense((uint64_t)w*w);
{
ComplexD *src = A.LocalWindow(lr0,lc0);
ComplexD *dst = &dense[0];
int64_t ld = L.mloc;
accelerator_for(idx, (uint64_t)(w*w), 1, {
int64_t jj = idx / w;
int64_t ii = idx - jj*w;
dst[ii + jj*w] = src[ii + jj*ld];
});
}
{
deviceVector<ComplexD*> bp(1);
std::vector<ComplexD*> ptr(1);
ptr[0] = &dense[0];
acceleratorCopyToDevice(&ptr[0], &bp[0], sizeof(ComplexD*));
INV.inverseBatched(w, bp);
}
{
ComplexD *src = &dense[0];
ComplexD *dst = A.LocalWindow(lr0,lc0);
int64_t ld = L.mloc;
accelerator_for(idx, (uint64_t)(w*w), 1, {
int64_t jj = idx / w;
int64_t ii = idx - jj*w;
dst[ii + jj*ld] = src[ii + jj*w];
});
}
// Growth telemetry, local only.
{
std::vector<ComplexD> h((uint64_t)w*w);
acceleratorCopyFromDevice(&dense[0], &h[0], h.size()*sizeof(ComplexD));
double mx = 0.0;
for(auto &z : h){
double re=z.real(), im=z.imag();
mx = std::max(mx, re*re+im*im);
}
telLeafMaxInv = std::max(telLeafMaxInv, std::sqrt(mx));
}
tLeaf += usecond();
}
///////////////////////////////////////////////////////////////////////////
// The recursion, on global BLOCK range [b0,b1). SPMD: every rank calls
// with identical arguments; there is no ownership gating to get wrong.
///////////////////////////////////////////////////////////////////////////
void SchurNode(BlockCyclicMatrix &A,
BlockCyclicMatrix &Bt, BlockCyclicMatrix &Ct,
BlockCyclicMatrix &Tt, BlockCyclicMatrix &Ut,
int64_t b0, int64_t b1)
{
BlockCyclicLayout &L = A.layout;
int64_t span = b1-b0;
GRID_ASSERT( span >= 1 );
if ( span == 1 ) { Leaf(A, b0); return; }
GRID_TRACE("SchurNode");
nNode++;
int64_t bm = b0 + span/2;
int64_t c0 = b0*L.nb;
int64_t m = bm*L.nb;
int64_t c1 = std::min(L.N, b1*L.nb);
ComplexD one (1.0,0.0), mone(-1.0,0.0), zero(0.0,0.0);
// 1. A11 -> A11inv
SchurNode(A,Bt,Ct,Tt,Ut, b0,bm);
tGemm -= usecond();
// 2. Bt[I,J] = A11inv . A12
SUMMA.Multiply(one, A, A, zero, Bt, c0,m, m,c1, c0,m );
// 3. Ct[J,I] = A21 . A11inv
SUMMA.Multiply(one, A, A, zero, Ct, m,c1, c0,m, c0,m );
// 4. A22 -= A21 . Bt (the Schur complement, in place)
SUMMA.Multiply(mone, A, Bt, one, A, m,c1, m,c1, c0,m );
tGemm += usecond();
// 5. S -> Sinv (Bt, Ct live across this call: see invariant)
SchurNode(A,Bt,Ct,Tt,Ut, bm,b1);
tGemm -= usecond();
// 6. Tt[J,I] = Sinv . Ct
SUMMA.Multiply(one, A, Ct, zero, Tt, m,c1, c0,m, m,c1 );
// 7. Ut[I,J] = Bt . Sinv
SUMMA.Multiply(one, Bt, A, zero, Ut, c0,m, m,c1, m,c1 );
// 8. A11 += Ut . Ct
SUMMA.Multiply(one, Ut, Ct, one, A, c0,m, c0,m, m,c1 );
tGemm += usecond();
// 9. Off-diagonal signs
{ GRID_TRACE("SchurCopy");
WindowCopyScale(mone, Ut, A, c0,m, m,c1);
WindowCopyScale(mone, Tt, A, m,c1, c0,m);
}
}
///////////////////////////////////////////////////////////////////////////
// PUBLIC ENTRY. In-place inverse of the whole matrix. Scratch (4x the
// matrix footprint) is allocated here and released on return.
///////////////////////////////////////////////////////////////////////////
void Invert(BlockCyclicMatrix &A)
{
BlockCyclicLayout &L = A.layout;
GRID_ASSERT( L.N >= 1 );
int64_t nblocks = (L.N + L.nb - 1)/L.nb;
BlockCyclicMatrix Bt(A.grid, L.N, L.nb, L.Pr, L.Pc);
BlockCyclicMatrix Ct(A.grid, L.N, L.nb, L.Pr, L.Pc);
BlockCyclicMatrix Tt(A.grid, L.N, L.nb, L.Pr, L.Pc);
BlockCyclicMatrix Ut(A.grid, L.N, L.nb, L.Pr, L.Pc);
telLeafMaxInv = 0.0;
nLeaf = nNode = 0;
tLeaf = tGemm = tCopy = 0.0;
SchurNode(A, Bt,Ct,Tt,Ut, 0, nblocks);
}
///////////////////////////////////////////////////////////////////////////
// Optional, and the ONLY place any reduction happens: call it if you
// want the numbers, never from Invert.
///////////////////////////////////////////////////////////////////////////
void ReportTelemetry(GridBase *grid)
{
RealD mx = telLeafMaxInv;
grid->GlobalMax(mx);
std::cout << GridLogMessage << "BlockCyclicSchurInverse:"
<< " nodes " << nNode << " leaves " << nLeaf
<< " max|leafinv| " << mx
<< " (boss secs: gemm+comms " << tGemm/1.0e6
<< " leaf " << tLeaf/1.0e6
<< " copy " << tCopy/1.0e6 << ")"
<< std::endl;
// SUMMA breakdown: boss-rank seconds, plus the ring/gemm time spread over
// ranks (min/max) -- skew shows as max >> min.
RealD ring = (SUMMA.tRingA+SUMMA.tRingB)/1.0e6;
RealD rmin = -ring, rmax = ring; grid->GlobalMax(rmin); grid->GlobalMax(rmax); rmin = -rmin; // no GlobalMin: max of negation
RealD gmin = -SUMMA.tGemm/1.0e6, gmax = SUMMA.tGemm/1.0e6; grid->GlobalMax(gmin); grid->GlobalMax(gmax); gmin = -gmin;
double gb = SUMMA.bytesRing/1.0e9;
std::cout << GridLogMessage << "BlockCyclicSumma:"
<< " multiplies " << SUMMA.nMultiply << " gemms " << SUMMA.nGemm << " ring msgs " << SUMMA.nRingMsg
<< " | boss secs: alloc " << SUMMA.tAlloc/1.0e6
<< " pack " << SUMMA.tPack/1.0e6
<< " ringA " << SUMMA.tRingA/1.0e6 << " ringB " << SUMMA.tRingB/1.0e6
<< " gemm " << SUMMA.tGemm/1.0e6
<< " | ring min/max over ranks " << rmin << "/" << rmax
<< " gemm min/max " << gmin << "/" << gmax
<< " | ring bytes/rank " << gb << " GB -> " << (ring>0 ? gb/ring : 0.0) << " GB/s/rank (boss)"
<< std::endl;
}
};
NAMESPACE_END(Grid);
@@ -1,328 +0,0 @@
/*************************************************************************************
Grid physics library, www.github.com/paboyle/Grid
Source file: ./Grid/algorithms/multigrid/BlockCyclicSumma.h
Copyright (C) 2026
Author: Peter Boyle <pboyle@bnl.gov>
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 */
#pragma once
#include <Grid/algorithms/multigrid/BlockCyclic.h>
NAMESPACE_BEGIN(Grid);
///////////////////////////////////////////////////////////////////////////////
// Stage 2 of the 2D distributed dense inverse: the windowed SUMMA product
//
// C[i0:i1, j0:j1] <- beta C[i0:i1, j0:j1]
// + alpha A[i0:i1, k0:k1] . B[k0:k1, j0:j1]
//
// on block-cyclic matrices sharing one BlockCyclicLayout. All ranges are
// BLOCK ALIGNED (multiples of nb, or N itself at the top end): stage 3's
// recursion splits on block boundaries, so nothing else is ever needed, and
// alignment makes every local window a contiguous band of local storage
// (BlockCyclic.h RangeToLocal, Test_blockcyclic T7).
//
// Transport is PURE POINT-TO-POINT: SendToRecvFrom on explicit world ranks
// computed from the row-major rank convention. No collectives of any kind
// -- no sub-communicators, no broadcast, no allgather -- by design: the
// collective pathologies measured on this machine (MPI_Allgatherv at
// ~0.18 MB/s with skewed counts, mpir_request.h:508 aborts, allreduce at
// 39% of the P2P rate) motivated this implementation. SendToRecvFrom is
// the most exercised device-buffer path in Grid and the only one never
// implicated.
//
// Algorithm: round-based ring allgather SUMMA. The k range is processed in
// rounds of Pc consecutive global blocks. Within a round
//
// * process column c owns at most one A panel (blocks s with s%Pc == c);
// the Pc panels circulate around each process-ROW ring in Pc-1 steps;
// * process row r owns up to ceil(Pc/Pr) B panels (blocks s%Pr == r);
// they circulate around each process-COLUMN ring in Pr-1 steps;
// * every rank then accumulates Cloc += alpha * Apanel_s . Bpanel_s
// for each block s of the round, in ascending s: a fixed summation
// order, so REPEATED RUNS ARE BITWISE IDENTICAL (no reduction, no
// order ambiguity -- the property the P2P doctrine buys).
//
// Ring chunks are PADDED to a fixed size (full nb panels, fixed
// panels-per-origin): SendToRecvFrom carries one byte count for both
// directions, so symmetric transfers eliminate every variable-size edge
// case at a worst-case ~1/Pc extra volume on ragged rounds. Padding is
// never read: GEMMs address only the leading nb_s x width of each slot.
//
// Per-rank received volume: (k-extent) * (mloc_i + nloc_j) elements --
// the N^2 (1/Pr + 1/Pc) SUMMA optimum, ~sqrt(P)/2 below the 1D scheme.
///////////////////////////////////////////////////////////////////////////////
class BlockCyclicMatrix
{
public:
GridBase *grid; // borrowed, never owned
BlockCyclicLayout layout;
deviceVector<ComplexD> data; // column major, ld = layout.mloc
BlockCyclicMatrix(GridBase *g, int64_t N, int64_t nb, int Pr, int Pc)
: grid(g),
layout(N, nb, Pr, Pc, g->ThisRank())
{
GRID_ASSERT( Pr*Pc == g->ProcessorCount() );
uint64_t sz = (uint64_t)layout.mloc*layout.nloc;
data.resize( sz ? sz : 1 );
}
ComplexD *LocalWindow(int64_t li, int64_t lj)
{
return &data[0] + li + lj*layout.mloc;
}
/////////////////////////////////////////////////////////////////////////
// TEST-SCALE import/export of a replicated global matrix (host, O(N^2)
// loops, one collective in Export). For unit tests and the stage-3
// oracle only; production data enters through the direct block-cyclic
// import, never through these.
/////////////////////////////////////////////////////////////////////////
void ImportGlobal(const std::vector<ComplexD> &G)
{
int64_t N = layout.N;
GRID_ASSERT( (int64_t)G.size() == N*N );
std::vector<ComplexD> h((uint64_t)layout.mloc*layout.nloc, ComplexD(0.0,0.0));
for(int64_t j=0;j<N;j++){
for(int64_t i=0;i<N;i++){
if ( layout.Owns(i,j) ) h[layout.LocalOffset(i,j)] = G[i + j*N];
}
}
if ( h.size() )
acceleratorCopyToDevice(&h[0], &data[0], h.size()*sizeof(ComplexD));
}
void ExportGlobal(std::vector<ComplexD> &G)
{
int64_t N = layout.N;
G.assign((uint64_t)N*N, ComplexD(0.0,0.0));
std::vector<ComplexD> h((uint64_t)layout.mloc*layout.nloc);
if ( h.size() )
acceleratorCopyFromDevice(&data[0], &h[0], h.size()*sizeof(ComplexD));
for(int64_t j=0;j<N;j++){
for(int64_t i=0;i<N;i++){
if ( layout.Owns(i,j) ) G[i + j*N] = h[layout.LocalOffset(i,j)];
}
}
if ( N ) grid->GlobalSumVector((ComplexD *)&G[0], (int)(N*N)); // zero-fill: exact
}
};
class BlockCyclicSumma
{
public:
GridBLAS BLAS;
// Per-rank telemetry (boss-rank seconds when printed; no reductions here).
// Every Multiply is: buffer alloc, pack panels, ring A along the process
// row, ring B along the process column, then the local GEMMs. The rings
// are synchronous SendToRecvFrom, so tRing is time the GPU is idle unless
// a future version overlaps them with the GEMMs.
double tAlloc=0, tPack=0, tRingA=0, tRingB=0, tGemm=0;
uint64_t bytesRing=0, nRingMsg=0, nMultiply=0, nGemm=0;
void ResetTelemetry(void){ tAlloc=tPack=tRingA=tRingB=tGemm=0; bytesRing=nRingMsg=nMultiply=nGemm=0; }
static int Overlap(int64_t a0,int64_t a1,int64_t b0,int64_t b1)
{ return (a0 < b1) && (b0 < a1); }
void Multiply(ComplexD alpha,
BlockCyclicMatrix &A,
BlockCyclicMatrix &B,
ComplexD beta,
BlockCyclicMatrix &C,
int64_t i0, int64_t i1,
int64_t j0, int64_t j1,
int64_t k0, int64_t k1)
{
BlockCyclicLayout &L = C.layout;
GridBase *grid = C.grid;
const int64_t N = L.N;
const int64_t nb = L.nb;
const int Pr = L.Pr, Pc = L.Pc;
const int prow = L.prow, pcol = L.pcol;
///////////////////////////////////////////////////////////////////////
// Conformability: one layout, one communicator, aligned ranges.
///////////////////////////////////////////////////////////////////////
auto same = [&](BlockCyclicLayout &X){
GRID_ASSERT( X.N==N ); GRID_ASSERT( X.nb==nb );
GRID_ASSERT( X.Pr==Pr ); GRID_ASSERT( X.Pc==Pc );
GRID_ASSERT( X.me==L.me );
};
same(A.layout); same(B.layout);
GRID_ASSERT( A.grid==grid ); GRID_ASSERT( B.grid==grid );
auto aligned = [&](int64_t g0,int64_t g1){
GRID_ASSERT( 0<=g0 ); GRID_ASSERT( g0<=g1 ); GRID_ASSERT( g1<=N );
GRID_ASSERT( g0%nb==0 ); GRID_ASSERT( (g1%nb==0)||(g1==N) );
};
aligned(i0,i1); aligned(j0,j1); aligned(k0,k1);
GRID_ASSERT( k1 > k0 ); // pure scaling not supported here
// In-place windows are legal only if the written window is disjoint
// from anything read (stage 3 uses this; make violation loud).
if ( &C==&A ) GRID_ASSERT( !Overlap(j0,j1,k0,k1) );
if ( &C==&B ) GRID_ASSERT( !Overlap(i0,i1,k0,k1) );
///////////////////////////////////////////////////////////////////////
// My local bands of the three windows (contiguous: T7).
///////////////////////////////////////////////////////////////////////
int64_t li0,li1, lj0,lj1;
L.RowRange(i0,i1, li0,li1);
L.ColRange(j0,j1, lj0,lj1);
const int64_t mloc_i = li1-li0; // my rows of the i window
const int64_t nloc_j = lj1-lj0; // my cols of the j window
const int64_t kb0 = k0/nb;
const int64_t kb1 = (k1+nb-1)/nb; // block-aligned or ==N: exact
const int64_t S = (Pc + Pr - 1)/Pr; // max B panels per origin row
///////////////////////////////////////////////////////////////////////
// Round buffers, padded to fixed slot sizes (see header comment).
// A: Pc slots of mloc_i x nb (slot c = panel of the block owned by c)
// B: Pr slots of S x (nb x nloc_j)
///////////////////////////////////////////////////////////////////////
const uint64_t slotA = (uint64_t)mloc_i*nb;
const uint64_t slotB1 = (uint64_t)nb*nloc_j; // one panel
const uint64_t slotB = (uint64_t)S*slotB1;
nMultiply++;
tAlloc -= usecond();
deviceVector<ComplexD> Abuf( slotA*Pc ? slotA*Pc : 1 );
deviceVector<ComplexD> Bbuf( slotB*Pr ? slotB*Pr : 1 );
tAlloc += usecond();
deviceVector<ComplexD *> ap(1), bp(1), cp(1);
std::vector<ComplexD *> ptr(1);
int firstblock = 1;
for(int64_t r0=kb0; r0<kb1; r0+=Pc){ // rounds of Pc k-blocks
int64_t r1 = std::min(kb1, r0+Pc);
/////////////////////////////////////////////////////////////////////
// Pack MY panels of this round into my origin slots.
/////////////////////////////////////////////////////////////////////
tPack -= usecond();
{ GRID_TRACE("SummaPack");
for(int64_t s=r0; s<r1; s++){
int64_t nb_s = L.BlockSize(s);
if ( (int)(s%Pc) == pcol && mloc_i ){ // my A panel: block-column s
int64_t lc0, lc1;
L.ColRange(s*nb, std::min(N,(s+1)*nb), lc0, lc1);
GRID_ASSERT( lc1-lc0 == nb_s );
ComplexD *src = A.LocalWindow(li0, lc0);
ComplexD *dst = &Abuf[0] + slotA*pcol;
int64_t ld = A.layout.mloc;
int64_t m = mloc_i;
accelerator_for(idx, (uint64_t)(m*nb_s), 1, {
int64_t jj = idx / m;
int64_t ii = idx - jj*m;
dst[ii + jj*m] = src[ii + jj*ld];
});
}
if ( (int)(s%Pr) == prow && nloc_j ){ // my B panel: block-row s
int64_t lr0, lr1;
L.RowRange(s*nb, std::min(N,(s+1)*nb), lr0, lr1);
GRID_ASSERT( lr1-lr0 == nb_s );
int64_t idxs = (s - r0 - ((prow - r0%Pr + Pr) % Pr)) / Pr; // my panel # in round
GRID_ASSERT( idxs >= 0 ); GRID_ASSERT( idxs < S );
ComplexD *src = B.LocalWindow(lr0, lj0);
ComplexD *dst = &Bbuf[0] + slotB*prow + slotB1*idxs;
int64_t ld = B.layout.mloc;
int64_t nn = nloc_j;
accelerator_for(idx, (uint64_t)(nb_s*nn), 1, {
int64_t jj = idx / nb_s;
int64_t ii = idx - jj*nb_s;
dst[ii + jj*nb_s] = src[ii + jj*ld];
});
}
}
accelerator_barrier();
}
tPack += usecond();
/////////////////////////////////////////////////////////////////////
// Ring allgather along my process ROW: Pc-1 symmetric steps. At
// step t send the slot of origin (pcol-t+1), receive origin (pcol-t).
/////////////////////////////////////////////////////////////////////
if ( Pc > 1 && slotA ){
GRID_TRACE("SummaRingA");
tRingA -= usecond();
int dest = prow*Pc + (pcol+1)%Pc;
int src = prow*Pc + (pcol-1+Pc)%Pc;
for(int t=1;t<Pc;t++){
int cs = (pcol - t + 1 + Pc*Pc) % Pc;
int cr = (pcol - t + Pc*Pc) % Pc;
grid->SendToRecvFrom((void *)(&Abuf[0]+slotA*cs), dest,
(void *)(&Abuf[0]+slotA*cr), src,
slotA*sizeof(ComplexD));
bytesRing += slotA*sizeof(ComplexD); nRingMsg++;
}
tRingA += usecond();
}
/////////////////////////////////////////////////////////////////////
// Ring allgather along my process COLUMN: Pr-1 symmetric steps.
/////////////////////////////////////////////////////////////////////
if ( Pr > 1 && slotB ){
GRID_TRACE("SummaRingB");
tRingB -= usecond();
int dest = ((prow+1)%Pr)*Pc + pcol;
int src = ((prow-1+Pr)%Pr)*Pc + pcol;
for(int t=1;t<Pr;t++){
int rs = (prow - t + 1 + Pr*Pr) % Pr;
int rr = (prow - t + Pr*Pr) % Pr;
grid->SendToRecvFrom((void *)(&Bbuf[0]+slotB*rs), dest,
(void *)(&Bbuf[0]+slotB*rr), src,
slotB*sizeof(ComplexD));
bytesRing += slotB*sizeof(ComplexD); nRingMsg++;
}
tRingB += usecond();
}
/////////////////////////////////////////////////////////////////////
// Local update, ascending s: fixed order, bitwise-reproducible.
/////////////////////////////////////////////////////////////////////
tGemm -= usecond();
{ GRID_TRACE("SummaGEMM");
for(int64_t s=r0; s<r1; s++){
int64_t nb_s = L.BlockSize(s);
if ( !(mloc_i && nloc_j && nb_s) ) { firstblock = 0; continue; }
int cA = (int)(s%Pc);
int rB = (int)(s%Pr);
int64_t idxs = (s - r0 - ((rB - r0%Pr + Pr) % Pr)) / Pr;
ComplexD beta_use = firstblock ? beta : ComplexD(1.0,0.0);
firstblock = 0;
ptr[0] = &Abuf[0] + slotA*cA;
acceleratorCopyToDevice(&ptr[0], &ap[0], sizeof(ComplexD *));
ptr[0] = &Bbuf[0] + slotB*rB + slotB1*idxs;
acceleratorCopyToDevice(&ptr[0], &bp[0], sizeof(ComplexD *));
ptr[0] = C.LocalWindow(li0, lj0);
acceleratorCopyToDevice(&ptr[0], &cp[0], sizeof(ComplexD *));
BLAS.gemmBatched(GridBLAS_OP_N, GridBLAS_OP_N,
(int)mloc_i, (int)nloc_j, (int)nb_s,
alpha, ap, (int)mloc_i,
bp, (int)nb_s,
beta_use, cp, (int)C.layout.mloc);
BLAS.synchronise();
nGemm++;
}
}
tGemm += usecond();
}
}
};
NAMESPACE_END(Grid);
+6 -6
View File
@@ -309,7 +309,7 @@ public:
if ((out.size()!=ndir)&&(out.size()!=ndir+1)) { if ((out.size()!=ndir)&&(out.size()!=ndir+1)) {
std::cout <<"MdirAll out size "<< out.size()<<std::endl; std::cout <<"MdirAll out size "<< out.size()<<std::endl;
std::cout <<"MdirAll ndir "<< ndir<<std::endl; std::cout <<"MdirAll ndir "<< ndir<<std::endl;
GRID_ASSERT(0); assert(0);
} }
for(int p=0;p<ndir;p++){ for(int p=0;p<ndir;p++){
MdirCalc(in,out[p],p); MdirCalc(in,out[p],p);
@@ -373,7 +373,7 @@ public:
conformable(in.Grid(), _cbgrid); // verifies half grid conformable(in.Grid(), _cbgrid); // verifies half grid
conformable(in.Grid(), out.Grid()); // drops the cb check conformable(in.Grid(), out.Grid()); // drops the cb check
GRID_ASSERT(in.Checkerboard() == Even); assert(in.Checkerboard() == Even);
out.Checkerboard() = Odd; out.Checkerboard() = Odd;
DhopInternal(StencilEven, Aodd, in, out, dag); DhopInternal(StencilEven, Aodd, in, out, dag);
@@ -383,7 +383,7 @@ public:
conformable(in.Grid(), _cbgrid); // verifies half grid conformable(in.Grid(), _cbgrid); // verifies half grid
conformable(in.Grid(), out.Grid()); // drops the cb check conformable(in.Grid(), out.Grid()); // drops the cb check
GRID_ASSERT(in.Checkerboard() == Odd); assert(in.Checkerboard() == Odd);
out.Checkerboard() = Even; out.Checkerboard() = Even;
DhopInternal(StencilOdd, Aeven, in, out, dag); DhopInternal(StencilOdd, Aeven, in, out, dag);
@@ -391,7 +391,7 @@ public:
void MooeeInternal(const CoarseVector &in, CoarseVector &out, int dag, int inv) { void MooeeInternal(const CoarseVector &in, CoarseVector &out, int dag, int inv) {
out.Checkerboard() = in.Checkerboard(); out.Checkerboard() = in.Checkerboard();
GRID_ASSERT(in.Checkerboard() == Odd || in.Checkerboard() == Even); assert(in.Checkerboard() == Odd || in.Checkerboard() == Even);
CoarseMatrix *Aself = nullptr; CoarseMatrix *Aself = nullptr;
if(in.Grid()->_isCheckerBoarded) { if(in.Grid()->_isCheckerBoarded) {
@@ -406,7 +406,7 @@ public:
Aself = (inv) ? &AselfInv : &A[geom.npoint-1]; Aself = (inv) ? &AselfInv : &A[geom.npoint-1];
DselfInternal(Stencil, *Aself, in, out, dag); DselfInternal(Stencil, *Aself, in, out, dag);
} }
GRID_ASSERT(Aself != nullptr); assert(Aself != nullptr);
} }
void DselfInternal(CartesianStencil<siteVector,siteVector,DefaultImplParams> &st, CoarseMatrix &a, void DselfInternal(CartesianStencil<siteVector,siteVector,DefaultImplParams> &st, CoarseMatrix &a,
@@ -697,7 +697,7 @@ public:
evenmask = where(mod(bcb,2)==(Integer)0,one,zero); evenmask = where(mod(bcb,2)==(Integer)0,one,zero);
oddmask = one-evenmask; oddmask = one-evenmask;
GRID_ASSERT(self_stencil!=-1); assert(self_stencil!=-1);
for(int i=0;i<nbasis;i++){ for(int i=0;i<nbasis;i++){
File diff suppressed because it is too large. Load diff
@@ -31,7 +31,6 @@ Author: Peter Boyle <pboyle@bnl.gov>
#include <Grid/lattice/PaddedCell.h> #include <Grid/lattice/PaddedCell.h>
#include <Grid/stencil/GeneralLocalStencil.h> #include <Grid/stencil/GeneralLocalStencil.h>
#include <Grid/algorithms/deflation/MultiRHSBlockProject.h>
NAMESPACE_BEGIN(Grid); NAMESPACE_BEGIN(Grid);
@@ -67,24 +66,9 @@ public:
std::vector<CoarseMatrix> _Adag; std::vector<CoarseMatrix> _Adag;
std::vector<CoarseVector> MultTemporaries; std::vector<CoarseVector> MultTemporaries;
int64_t MultCalls;
double MultFlopsAccum;
double MultUsecAccum;
/////////////////////// ///////////////////////
// Interface // Interface
/////////////////////// ///////////////////////
//////////////////////////////////////////////////////////////////////////
// Bilingual accessors: everything a consumer needs to read the operator
// without knowing which of the three coarse classes it holds. The D
// dimensional grid the elements live on, the geometry they are indexed by,
// and one unpadded point at a time (a whole npoint vector is too much
// memory at production nbasis).
//////////////////////////////////////////////////////////////////////////
GridCartesian * CoarseGridD(void) { return _CoarseGrid; };
NonLocalStencilGeometry & Geometry(void) { return geom; };
void ExtractMatrix(int p,CoarseMatrix &A) { A = Cell.Extract(_A[p]); };
GridBase * Grid(void) { return _CoarseGrid; }; // this is all the linalg routines need to know GridBase * Grid(void) { return _CoarseGrid; }; // this is all the linalg routines need to know
GridBase * FineGrid(void) { return _FineGrid; }; // this is all the linalg routines need to know GridBase * FineGrid(void) { return _FineGrid; }; // this is all the linalg routines need to know
GridCartesian * CoarseGrid(void) { return _CoarseGrid; }; // this is all the linalg routines need to know GridCartesian * CoarseGrid(void) { return _CoarseGrid; }; // this is all the linalg routines need to know
@@ -115,25 +99,24 @@ public:
} }
} }
} }
GRID_ASSERT(nfound==geom.npoint); assert(nfound==geom.npoint);
ExchangeCoarseLinks(); ExchangeCoarseLinks();
} }
*/ */
GeneralCoarsenedMatrix(NonLocalStencilGeometry &_geom,GridBase *FineGrid, GridCartesian * CoarseGrid,int _herm=1) GeneralCoarsenedMatrix(NonLocalStencilGeometry &_geom,GridBase *FineGrid, GridCartesian * CoarseGrid)
: geom(_geom), : geom(_geom),
_FineGrid(FineGrid), _FineGrid(FineGrid),
_CoarseGrid(CoarseGrid), _CoarseGrid(CoarseGrid),
hermitian(_herm), hermitian(1),
Cell(_geom.Depth(),_CoarseGrid), Cell(_geom.Depth(),_CoarseGrid),
Stencil(Cell.grids.back(),geom.shifts), Stencil(Cell.grids.back(),geom.shifts)
MultCalls(0), MultFlopsAccum(0.0), MultUsecAccum(0.0)
{ {
{ {
int npoint = _geom.npoint; int npoint = _geom.npoint;
} }
_A.resize(geom.npoint,CoarseGrid); _A.resize(geom.npoint,CoarseGrid);
if ( !hermitian ) _Adag.resize(geom.npoint,CoarseGrid); // _Adag.resize(geom.npoint,CoarseGrid);
} }
void M (const CoarseVector &in, CoarseVector &out) void M (const CoarseVector &in, CoarseVector &out)
{ {
@@ -141,10 +124,10 @@ public:
} }
void Mdag (const CoarseVector &in, CoarseVector &out) void Mdag (const CoarseVector &in, CoarseVector &out)
{ {
if(hermitian) assert(hermitian);
Mult(_A,in,out); Mult(_A,in,out);
else // if ( hermitian ) M(in,out);
Mult(_Adag,in,out); // else Mult(_Adag,in,out);
} }
void Mult (std::vector<CoarseMatrix> &A,const CoarseVector &in, CoarseVector &out) void Mult (std::vector<CoarseMatrix> &A,const CoarseVector &in, CoarseVector &out)
{ {
@@ -244,28 +227,29 @@ public:
text+=usecond(); text+=usecond();
ttot+=usecond(); ttot+=usecond();
MultCalls++; std::cout << GridLogPerformance<<"Coarse 1rhs Mult Aviews "<<tviews<<" us"<<std::endl;
MultFlopsAccum += flops; std::cout << GridLogPerformance<<"Coarse Mult exch "<<texch<<" us"<<std::endl;
MultUsecAccum += ttot; std::cout << GridLogPerformance<<"Coarse Mult mult "<<tmult<<" us"<<std::endl;
std::cout << GridLogPerformance std::cout << GridLogPerformance<<" of which mult2 "<<tmult2<<" us"<<std::endl;
<< "Coarse Mult call " << MultCalls std::cout << GridLogPerformance<<"Coarse Mult ext "<<text<<" us"<<std::endl;
<< " tot " << ttot << " us" std::cout << GridLogPerformance<<"Coarse Mult temps "<<ttemps<<" us"<<std::endl;
<< " kernel " << tmult << " us" std::cout << GridLogPerformance<<"Coarse Mult copy "<<tcopy<<" us"<<std::endl;
<< " kernel " << flops/tmult*1e-3 << " GFlop/s" std::cout << GridLogPerformance<<"Coarse Mult tot "<<ttot<<" us"<<std::endl;
<< " overall " << MultFlopsAccum/MultUsecAccum*1e-3 << " GFlop/s (cumul)" // std::cout << GridLogPerformance<<std::endl;
<< " bw " << bytes/tmult*1e-3 << " GB/s" std::cout << GridLogPerformance<<"Coarse Kernel flops "<< flops<<std::endl;
<< std::endl; std::cout << GridLogPerformance<<"Coarse Kernel flop/s "<< flops/tmult<<" mflop/s"<<std::endl;
std::cout << GridLogPerformance<<"Coarse Kernel bytes/s "<< bytes/tmult<<" MB/s"<<std::endl;
std::cout << GridLogPerformance<<"Coarse overall flops/s "<< flops/ttot<<" mflop/s"<<std::endl;
std::cout << GridLogPerformance<<"Coarse total bytes "<< bytes/1e6<<" MB"<<std::endl;
}; };
void PopulateAdag(void) void PopulateAdag(void)
{ {
#if 0
// Serial global peek/poke reference implementation
for(int64_t bidx=0;bidx<CoarseGrid()->gSites() ;bidx++){ for(int64_t bidx=0;bidx<CoarseGrid()->gSites() ;bidx++){
Coordinate bcoor; Coordinate bcoor;
CoarseGrid()->GlobalIndexToGlobalCoor(bidx,bcoor); CoarseGrid()->GlobalIndexToGlobalCoor(bidx,bcoor);
for(int p=0;p<geom.npoint;p++){ for(int p=0;p<geom.npoint;p++){
Coordinate scoor = bcoor; Coordinate scoor = bcoor;
for(int mu=0;mu<bcoor.size();mu++){ for(int mu=0;mu<bcoor.size();mu++){
@@ -278,36 +262,6 @@ public:
pokeSite(adj(link),_Adag[pp],bcoor); pokeSite(adj(link),_Adag[pp],bcoor);
} }
} }
#else
// Parallel: _Adag[pp](x) = adj( _A[p](x + s_pp) ), pp = Reverse(p), s_pp = -s_p.
// The neighbour fetch reuses the same padded-cell + stencil machinery as Mult,
// reading one matrix element per coalesced access so no whole site matrix
// (230KB at nbasis=60) ever lands on a GPU thread stack (HIP limit 128KB).
// Halo sites compute garbage neighbours; Cell.Extract discards them.
// Must run on the unpadded _A, i.e. before ExchangeCoarseLinks.
const int Nsimd = CComplex::Nsimd();
for(int p=0;p<geom.npoint;p++){
int pp = geom.Reverse(p);
CoarseMatrix Apad = Cell.ExchangePeriodic(_A[p]);
CoarseMatrix Dpad(Apad.Grid());
int64_t osites = Apad.Grid()->oSites();
{
autoView( Apad_v , Apad, AcceleratorRead);
autoView( Dpad_v , Dpad, AcceleratorWriteDiscard);
autoView( Stencil_v, Stencil, AcceleratorRead);
accelerator_for(sj, osites*nbasis, Nsimd, {
int32_t ss = sj/nbasis;
int32_t j = sj%nbasis;
auto SE = Stencil_v.GetEntry(pp,ss);
for(int i=0;i<nbasis;i++){
auto z = coalescedReadGeneralPermute(Apad_v[SE->_offset](i,j),SE->_permute,Nd);
coalescedWrite(Dpad_v[ss](j,i),conjugate(z));
}
});
}
_Adag[pp] = Cell.Extract(Dpad);
}
#endif
} }
///////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////
// //
@@ -463,19 +417,10 @@ public:
int osites=CoarseGrid()->oSites(); int osites=CoarseGrid()->oSites();
autoView( A_v , _A[k], AcceleratorWrite); autoView( A_v , _A[k], AcceleratorWrite);
autoView( FT_v , FT[k], AcceleratorRead); autoView( FT_v , FT[k], AcceleratorRead);
accelerator_for(sss, osites, nbasis, { accelerator_for(sss, osites, 1, {
#ifdef GRID_SIMT
int j = acceleratorSIMTlane(nbasis);
A_v[sss](i,j) = FT_v[sss](j);
#else
// CPU build: acceleratorSIMTlane()==0 -- an un-looped SIMT tensor
// index writes ONLY j=0 and silently drops the other nbasis-1
// columns (caught by Test_schur_dense_coarse import certificate,
// 2026-08-14). Loop explicitly.
for(int j=0;j<nbasis;j++){ for(int j=0;j<nbasis;j++){
A_v[sss](i,j) = FT_v[sss](j); A_v[sss](i,j) = FT_v[sss](j);
} }
#endif
}); });
} }
tinv+=usecond(); tinv+=usecond();
@@ -483,8 +428,8 @@ public:
// Only needed if nonhermitian // Only needed if nonhermitian
if ( ! hermitian ) { if ( ! hermitian ) {
std::cout << GridLogMessage<<"PopulateAdag "<<std::endl; // std::cout << GridLogMessage<<"PopulateAdag "<<std::endl;
PopulateAdag(); // PopulateAdag();
} }
// Need to write something to populate Adag from A // Need to write something to populate Adag from A
@@ -572,9 +517,13 @@ public:
// Now compute the matrix elements of linop between the orthonormal // Now compute the matrix elements of linop between the orthonormal
// set of vectors. // set of vectors.
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
FineField phaV(grid); // Phased block basis vector
FineField MphaV(grid);// Matrix applied
std::vector<FineComplexField> phaF(npoint,grid); std::vector<FineComplexField> phaF(npoint,grid);
std::vector<CoarseComplexField> pha(npoint,CoarseGrid()); std::vector<CoarseComplexField> pha(npoint,CoarseGrid());
CoarseVector coarseInner(CoarseGrid());
typedef typename CComplex::scalar_type SComplex; typedef typename CComplex::scalar_type SComplex;
FineComplexField one(grid); one=SComplex(1.0); FineComplexField one(grid); one=SComplex(1.0);
FineComplexField zz(grid); zz = Zero(); FineComplexField zz(grid); zz = Zero();
@@ -593,52 +542,37 @@ public:
pha[p] =exp(pha[p]*ci); pha[p] =exp(pha[p]*ci);
blockZAXPY(phaF[p],pha[p],one,zz); blockZAXPY(phaF[p],pha[p],one,zz);
} }
tphase+=usecond(); tphase+=usecond();
// Import basis into BLAS layout once; blockProject then reads it once per std::vector<CoarseVector> ComputeProj(npoint,CoarseGrid());
// basis vector rather than once per (i,p) as in scalar blockProject. std::vector<CoarseVector> FT(npoint,CoarseGrid());
// Process all npoint in a single batch.
MultiRHSBlockProject<FineField> Projector;
Projector.Allocate(nbasis, grid, CoarseGrid());
Projector.ImportBasis(U.subspace);
std::vector<FineField> phaV_batch(npoint, grid);
std::vector<FineField> MphaV_batch(npoint, grid);
std::vector<CoarseVector> proj_batch(npoint, CoarseGrid());
std::vector<CoarseVector> ComputeProj(npoint, CoarseGrid());
std::vector<CoarseVector> FT(npoint, CoarseGrid());
// Pre-allocate BLAS_F and BLAS_C to avoid repeated hipMalloc/hipFree of
// ~5.6 GB per blockProject call, which hangs on ROCm for large allocations.
Projector.BLAS_F.resize(Projector.fine_vol * Projector.words * npoint);
Projector.BLAS_C.resize(Projector.coarse_vol * nbasis * npoint);
for(int i=0;i<nbasis;i++){// Loop over basis vectors for(int i=0;i<nbasis;i++){// Loop over basis vectors
accelerator_barrier(); // ensure prior iteration's async writes are retired
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<"/"<<nbasis<< std::endl; std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<"/"<<nbasis<< std::endl;
for(int p=0;p<npoint;p++){ // Loop over momenta in npoint
tphaseBZ-=usecond();
phaV = phaF[p]*V.subspace[i];
tphaseBZ+=usecond();
tphaseBZ-=usecond(); /////////////////////////////////////////////////////////////////////
for(int p=0;p<npoint;p++) // Multiple phased subspace vector by matrix and project to subspace
phaV_batch[p] = phaF[p] * V.subspace[i]; // Remove local bulk phase to leave relative phases
tphaseBZ+=usecond(); /////////////////////////////////////////////////////////////////////
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<" phaseBZ done"<< std::endl; tmat-=usecond();
linop.Op(phaV,MphaV);
tmat+=usecond();
// std::cout << i << " " <<p << " MphaV "<<norm2(MphaV)<<" "<<norm2(phaV)<<std::endl;
tmat-=usecond(); tproj-=usecond();
for(int p=0;p<npoint;p++) blockProject(coarseInner,MphaV,U.subspace);
linop.Op(phaV_batch[p], MphaV_batch[p]); coarseInner = conjugate(pha[p]) * coarseInner;
tmat+=usecond();
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<" mat done"<< std::endl;
// One batched GEMM reads BLAS_V once for all npoint vectors. ComputeProj[p] = coarseInner;
tproj-=usecond(); tproj+=usecond();
Projector.blockProject(MphaV_batch, proj_batch); // std::cout << i << " " <<p << " ComputeProj "<<norm2(ComputeProj[p])<<std::endl;
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<" blockProject done"<< std::endl;
for(int p=0;p<npoint;p++) }
ComputeProj[p] = conjugate(pha[p]) * proj_batch[p];
tproj+=usecond();
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i<<" proj done"<< std::endl;
tinv-=usecond(); tinv-=usecond();
for(int k=0;k<npoint;k++){ for(int k=0;k<npoint;k++){
@@ -646,23 +580,14 @@ public:
for(int l=0;l<npoint;l++){ for(int l=0;l<npoint;l++){
FT[k]= FT[k]+ invMkl(l,k)*ComputeProj[l]; FT[k]= FT[k]+ invMkl(l,k)*ComputeProj[l];
} }
int osites=CoarseGrid()->oSites(); int osites=CoarseGrid()->oSites();
autoView( A_v , _A[k], AcceleratorWrite); autoView( A_v , _A[k], AcceleratorWrite);
autoView( FT_v , FT[k], AcceleratorRead); autoView( FT_v , FT[k], AcceleratorRead);
accelerator_for(sss, osites, nbasis, { accelerator_for(sss, osites, 1, {
#ifdef GRID_SIMT
int j = acceleratorSIMTlane(nbasis);
A_v[sss](i,j) = FT_v[sss](j);
#else
// CPU build: acceleratorSIMTlane()==0 -- an un-looped SIMT tensor
// index writes ONLY j=0 and silently drops the other nbasis-1
// columns (caught by Test_schur_dense_coarse import certificate,
// 2026-08-14). Loop explicitly.
for(int j=0;j<nbasis;j++){ for(int j=0;j<nbasis;j++){
A_v[sss](i,j) = FT_v[sss](j); A_v[sss](i,j) = FT_v[sss](j);
} }
#endif
}); });
} }
tinv+=usecond(); tinv+=usecond();
@@ -670,13 +595,13 @@ public:
// Only needed if nonhermitian // Only needed if nonhermitian
if ( ! hermitian ) { if ( ! hermitian ) {
std::cout << GridLogMessage<<"PopulateAdag "<<std::endl; // std::cout << GridLogMessage<<"PopulateAdag "<<std::endl;
PopulateAdag(); // PopulateAdag();
} }
// for(int p=0;p<geom.npoint;p++){ for(int p=0;p<geom.npoint;p++){
// std::cout << " _A["<<p<<"] "<<norm2(_A[p])<<std::endl; std::cout << " _A["<<p<<"] "<<norm2(_A[p])<<std::endl;
// } }
// Need to write something to populate Adag from A // Need to write something to populate Adag from A
ExchangeCoarseLinks(); ExchangeCoarseLinks();
@@ -691,10 +616,10 @@ public:
void ExchangeCoarseLinks(void){ void ExchangeCoarseLinks(void){
for(int p=0;p<geom.npoint;p++){ for(int p=0;p<geom.npoint;p++){
_A[p] = Cell.ExchangePeriodic(_A[p]); _A[p] = Cell.ExchangePeriodic(_A[p]);
if ( !hermitian ) _Adag[p]= Cell.ExchangePeriodic(_Adag[p]); // _Adag[p]= Cell.ExchangePeriodic(_Adag[p]);
} }
} }
virtual void Mdiag (const Field &in, Field &out){ GRID_ASSERT(0);}; virtual void Mdiag (const Field &in, Field &out){ assert(0);};
virtual void Mdir (const Field &in, Field &out,int dir, int disp){assert(0);}; virtual void Mdir (const Field &in, Field &out,int dir, int disp){assert(0);};
virtual void MdirAll (const Field &in, std::vector<Field> &out){assert(0);}; virtual void MdirAll (const Field &in, std::vector<Field> &out){assert(0);};
}; };
@@ -77,24 +77,15 @@ public:
GridBase * Grid(void) { return _CoarseGridMulti; }; // this is all the linalg routines need to know GridBase * Grid(void) { return _CoarseGridMulti; }; // this is all the linalg routines need to know
GridCartesian * CoarseGrid(void) { return _CoarseGridMulti; }; // this is all the linalg routines need to know GridCartesian * CoarseGrid(void) { return _CoarseGridMulti; }; // this is all the linalg routines need to know
////////////////////////////////////////////////////////////////////////// // Can be used to do I/O on the operator matrices externally
// Bilingual accessors, matching GeneralCoarsenedMatrix. Grid() here is the void SetMatrix (int p,CoarseMatrix & A)
// D+1 multiRHS grid and this class never holds the D dimensional one, so
// ExtractMatrix writes into whatever grid the caller's lattice is on.
//////////////////////////////////////////////////////////////////////////
NonLocalStencilGeometry & Geometry(void) { return geom_srhs; };
void ExtractMatrix(int p,CoarseMatrix &A) { BLAStoGrid(A,BLAS_A[p]); };
// I/O on the operator matrices, via the BLAS layout array. The parameter is
// a vector over the geometry points; the body indexes A[p].
void SetMatrix (int p,std::vector<CoarseMatrix> & A)
{ {
GRID_ASSERT(A.size()==geom_srhs.npoint); assert(A.size()==geom_srhs.npoint);
GridtoBLAS(A[p],BLAS_A[p]); GridtoBLAS(A[p],BLAS_A[p]);
} }
void GetMatrix (int p,std::vector<CoarseMatrix> & A) void GetMatrix (int p,CoarseMatrix & A)
{ {
GRID_ASSERT(A.size()==geom_srhs.npoint); assert(A.size()==geom_srhs.npoint);
BLAStoGrid(A[p],BLAS_A[p]); BLAStoGrid(A[p],BLAS_A[p]);
} }
void CopyMatrix (GeneralCoarseOp &_Op) void CopyMatrix (GeneralCoarseOp &_Op)
@@ -187,14 +178,14 @@ public:
for(int32_t point = 0 ; point < geom.npoint; point++){ for(int32_t point = 0 ; point < geom.npoint; point++){
int i=s*orhs*geom.npoint+point; int i=s*orhs*geom.npoint+point;
int32_t nbr = Stencil._entries[i]._offset*CComplex::Nsimd(); // oSite -> lSite int32_t nbr = Stencil._entries[i]._offset*CComplex::Nsimd(); // oSite -> lSite
GRID_ASSERT(nbr<BLAS_B.size()); assert(nbr<BLAS_B.size());
ComplexD * ptr = (ComplexD *)&BLAS_B[nbr]; ComplexD * ptr = (ComplexD *)&BLAS_B[nbr];
acceleratorPut(BLAS_BP[point][j],ptr); // neighbour indexing in ghost zone volume acceleratorPut(BLAS_BP[point][j],ptr); // neighbour indexing in ghost zone volume
} }
j++; j++;
} }
} }
GRID_ASSERT(j==unpadded_sites); assert(j==unpadded_sites);
} }
template<class vobj> void GridtoBLAS(const Lattice<vobj> &from,deviceVector<typename vobj::scalar_object> &to) template<class vobj> void GridtoBLAS(const Lattice<vobj> &from,deviceVector<typename vobj::scalar_object> &to)
{ {
@@ -203,7 +194,7 @@ public:
typedef typename vobj::vector_type vector_type; typedef typename vobj::vector_type vector_type;
GridBase *Fg = from.Grid(); GridBase *Fg = from.Grid();
GRID_ASSERT(!Fg->_isCheckerBoarded); assert(!Fg->_isCheckerBoarded);
int nd = Fg->_ndimension; int nd = Fg->_ndimension;
to.resize(Fg->lSites()); to.resize(Fg->lSites());
@@ -250,10 +241,10 @@ public:
typedef typename vobj::vector_type vector_type; typedef typename vobj::vector_type vector_type;
GridBase *Tg = grid.Grid(); GridBase *Tg = grid.Grid();
GRID_ASSERT(!Tg->_isCheckerBoarded); assert(!Tg->_isCheckerBoarded);
int nd = Tg->_ndimension; int nd = Tg->_ndimension;
GRID_ASSERT(in.size()==Tg->lSites()); assert(in.size()==Tg->lSites());
Coordinate LocalLatt = Tg->LocalDimensions(); Coordinate LocalLatt = Tg->LocalDimensions();
size_t nsite = 1; size_t nsite = 1;
@@ -678,7 +669,7 @@ Grid : Message : 328.193436 s : CoarsenOperator mat 122213270 us
const int Nsimd = CComplex::Nsimd(); const int Nsimd = CComplex::Nsimd();
int64_t nrhs =pin.Grid()->GlobalDimensions()[0]; int64_t nrhs =pin.Grid()->GlobalDimensions()[0];
GRID_ASSERT(nrhs>=1); assert(nrhs>=1);
RealD flops,bytes; RealD flops,bytes;
int64_t osites=in.Grid()->oSites(); // unpadded int64_t osites=in.Grid()->oSites(); // unpadded
@@ -730,7 +721,7 @@ Grid : Message : 328.193436 s : CoarsenOperator mat 122213270 us
// std::cout << GridLogMessage<<"Coarse overall flops/s "<< flops/t_tot<<" mflop/s"<<std::endl; // std::cout << GridLogMessage<<"Coarse overall flops/s "<< flops/t_tot<<" mflop/s"<<std::endl;
// std::cout << GridLogMessage<<"Coarse total bytes "<< bytes/1e6<<" MB"<<std::endl; // std::cout << GridLogMessage<<"Coarse total bytes "<< bytes/1e6<<" MB"<<std::endl;
}; };
virtual void Mdiag (const Field &in, Field &out){ GRID_ASSERT(0);}; virtual void Mdiag (const Field &in, Field &out){ assert(0);};
virtual void Mdir (const Field &in, Field &out,int dir, int disp){assert(0);}; virtual void Mdir (const Field &in, Field &out,int dir, int disp){assert(0);};
virtual void MdirAll (const Field &in, std::vector<Field> &out){assert(0);}; virtual void MdirAll (const Field &in, std::vector<Field> &out){assert(0);};
}; };
@@ -1,870 +0,0 @@
/*************************************************************************************
Grid physics library, www.github.com/paboyle/Grid
Source file: ./lib/algorithms/GeneralCoarsenedMatrixMultiRHS.h
Copyright (C) 2015
Author: Peter Boyle <pboyle@bnl.gov>
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 */
#pragma once
NAMESPACE_BEGIN(Grid);
// Fine Object == (per site) type of fine field
// nbasis == number of deflation vectors
template<class Fobj,class CComplex,int nbasis>
class MultiGeneralCoarsenedOperatorV2 : public SparseMatrixBase<Lattice<iVector<CComplex,nbasis > > > {
public:
typedef typename CComplex::scalar_object SComplex;
typedef GeneralCoarsenedMatrix<Fobj,CComplex,nbasis> GeneralCoarseOp;
typedef MultiGeneralCoarsenedOperatorV2<Fobj,CComplex,nbasis> MultiGeneralCoarseOp;
typedef iVector<CComplex,nbasis > siteVector;
typedef iMatrix<CComplex,nbasis > siteMatrix;
typedef iVector<SComplex,nbasis > calcVector;
typedef iMatrix<SComplex,nbasis > calcMatrix;
typedef Lattice<iScalar<CComplex> > CoarseComplexField;
typedef Lattice<siteVector> CoarseVector;
typedef Lattice<iMatrix<CComplex,nbasis > > CoarseMatrix;
typedef iMatrix<CComplex,nbasis > Cobj;
typedef iVector<CComplex,nbasis > Cvec;
typedef Lattice< CComplex > CoarseScalar; // used for inner products on fine field
typedef Lattice<Fobj > FineField;
typedef CoarseVector Field;
// Block operations on the fine vectors carry the fine layout, which need
// not be the coarse one
typedef decltype(innerProduct(Fobj(),Fobj())) FineInner;
typedef Lattice<FineInner> FineComplexField;
typedef Lattice<FineInner> BlockComplexField;
////////////////////
// Data members
//
// Nrhs independent: the D dimensional coarse grid, the geometry, the padded
// cell that supplies the stencil grid, the stencil, and the matrix elements.
//
// Nrhs dependent: the D+1 grid, its padded cell, and the BLAS B/C buffers
// with their pointer tables. Owned by SetNRHS().
////////////////////
GridCartesian * _CoarseGrid; // D dimensional
NonLocalStencilGeometry geom;
NonLocalStencilGeometry geom_srhs;
PaddedCell CellD; // D dimensional, supplies stencil grid
GeneralLocalStencil Stencil; // D dimensional
int _Nrhs;
GridCartesian * _CoarseGridMulti; // D+1 dimensional, SetNRHS
PaddedCell * CellMulti; // D+1 dimensional, SetNRHS
deviceVector<calcVector> BLAS_B;
deviceVector<calcVector> BLAS_C;
std::vector<deviceVector<calcMatrix> > BLAS_A;
std::vector<deviceVector<ComplexD *> > BLAS_AP;
std::vector<deviceVector<ComplexD *> > BLAS_BP;
deviceVector<ComplexD *> BLAS_CP;
///////////////////////
// Interface
///////////////////////
GridBase * Grid(void) { CheckGridSet(); return _CoarseGridMulti; };
GridCartesian * CoarseGrid(void) { CheckGridSet(); return _CoarseGridMulti; };
GridCartesian * CoarseGridD(void) { return _CoarseGrid; }; // lower dimensional grid
int Nrhs(void) { CheckGridSet(); return _Nrhs; };
void CheckGridSet(void)
{
if ( _CoarseGridMulti == nullptr ) {
std::cout << GridLogError
<< "MultiGeneralCoarsenedOperatorV2: the multiRHS grid has not been set."
<< std::endl;
std::cout << GridLogError
<< " Call SetGrid(CoarseGridMulti) with the D+1 dimensional grid your"
<< std::endl;
std::cout << GridLogError
<< " coarse vectors live on, before Grid(), Nrhs() or M()."
<< std::endl;
GRID_ASSERT(_CoarseGridMulti != nullptr);
}
}
//////////////////////////////////////////////////////////////////////////
// Bilingual accessors, matching GeneralCoarsenedMatrix. Note Grid() is the
// D+1 multiRHS grid here, so a consumer wanting the space the elements live
// on must ask for CoarseGridD().
//////////////////////////////////////////////////////////////////////////
NonLocalStencilGeometry & Geometry(void) { return geom_srhs; };
void ExtractMatrix(int p,CoarseMatrix &A) { BLAStoGrid(A,BLAS_A[p]); };
// I/O on the operator matrices, via the BLAS layout array. The parameter is
// a vector over the geometry points; the body indexes A[p].
void SetMatrix (int p,std::vector<CoarseMatrix> & A)
{
GRID_ASSERT(A.size()==geom_srhs.npoint);
GridtoBLAS(A[p],BLAS_A[p]);
}
void GetMatrix (int p,std::vector<CoarseMatrix> & A)
{
GRID_ASSERT(A.size()==geom_srhs.npoint);
BLAStoGrid(A[p],BLAS_A[p]);
}
void CopyMatrix (GeneralCoarseOp &_Op)
{
for(int p=0;p<geom.npoint;p++){
auto Aup = _Op.Cell.Extract(_Op._A[p]);
//Unpadded
GridtoBLAS(Aup,BLAS_A[p]);
}
}
/*
void CheckMatrix (GeneralCoarseOp &_Op)
{
std::cout <<"************* Checking the little direc operator mRHS"<<std::endl;
for(int p=0;p<geom.npoint;p++){
//Unpadded
auto Aup = _Op.Cell.Extract(_Op._A[p]);
auto Ack = Aup;
BLAStoGrid(Ack,BLAS_A[p]);
std::cout << p<<" Ack "<<norm2(Ack)<<std::endl;
std::cout << p<<" Aup "<<norm2(Aup)<<std::endl;
}
std::cout <<"************* "<<std::endl;
}
*/
///////////////////////////////////////////////////////////////////////////
// Constructor takes the D dimensional coarse grid. Everything built here
// is independent of Nrhs, in particular the matrix elements, which must
// survive a change of Nrhs untouched.
///////////////////////////////////////////////////////////////////////////
MultiGeneralCoarsenedOperatorV2(NonLocalStencilGeometry &_geom,GridCartesian *CoarseGrid) :
_CoarseGrid(CoarseGrid),
geom_srhs(_geom),
geom(CoarseGrid,_geom.hops,_geom.skip),
CellD(geom.Depth(),CoarseGrid),
Stencil(CellD.grids.back(),geom.shifts), // D dimensional padded cell stencil
_Nrhs(-1),
_CoarseGridMulti(nullptr),
CellMulti(nullptr)
{
int32_t unpadded_sites = _CoarseGrid->lSites();
/////////////////////////////////////////////////
// Matrix elements and their pointer table
/////////////////////////////////////////////////
BLAS_A.resize(geom.npoint);
BLAS_AP.resize(geom.npoint);
for(int p=0;p<geom.npoint;p++){
BLAS_A[p].resize (unpadded_sites); // no ghost zone, npoint elements
BLAS_AP[p].resize(unpadded_sites);
}
// Site identity mapping for A
for(int p=0;p<geom.npoint;p++){
for(int ss=0;ss<unpadded_sites;ss++){
ComplexD *ptr = (ComplexD *)&BLAS_A[p][ss];
acceleratorPut(BLAS_AP[p][ss],ptr);
}
}
}
virtual ~MultiGeneralCoarsenedOperatorV2()
{
ReleaseGrid();
}
///////////////////////////////////////////////////////////////////////////
// Free everything SetGrid allocated. The D+1 grid is borrowed from the
// caller and is never deleted here. Safe to call repeatedly and before
// the destructor.
///////////////////////////////////////////////////////////////////////////
void ReleaseGrid(void)
{
if ( CellMulti != nullptr ) { delete CellMulti; CellMulti = nullptr; }
_CoarseGridMulti = nullptr; // borrowed, not owned
_Nrhs = -1;
BLAS_B.resize(0);
BLAS_C.resize(0);
for(int p=0;p<BLAS_BP.size();p++){
BLAS_BP[p].resize(0);
}
BLAS_BP.resize(0);
BLAS_CP.resize(0);
}
///////////////////////////////////////////////////////////////////////////
// Everything that depends on Nrhs. Idempotent; lazy called on demand.
//
// The stencil lives on the D dimensional padded grid. PaddedCell only pads
// a dimension when it is distributed, and the rhs direction never is, so
// the D+1 padded grid is exactly Nrhs copies of the D dimensional padded
// grid with rhs innermost. The neighbour offset therefore carries an Nrhs
// factor, in the same way the Nsimd factor is carried.
///////////////////////////////////////////////////////////////////////////
void SetGrid(GridCartesian *CoarseGridMulti)
{
GRID_ASSERT(CoarseGridMulti != nullptr);
if ( CoarseGridMulti == _CoarseGridMulti ) return; // idempotent on identity
ReleaseGrid();
/////////////////////////////////////////////////
// The D+1 grid is supplied and owned by the caller. Two operators over
// the same coarse space must share one grid object or their fields will
// not conform, so this is never manufactured internally.
/////////////////////////////////////////////////
int nd = _CoarseGrid->_ndimension;
GRID_ASSERT(CoarseGridMulti->_ndimension == nd+1);
GRID_ASSERT(CoarseGridMulti->_processors[0] == 1); // rhs is not distributed
for(int d=0;d<nd;d++){
GRID_ASSERT(CoarseGridMulti->_fdimensions[d+1] == _CoarseGrid->_fdimensions[d]);
GRID_ASSERT(CoarseGridMulti->_processors [d+1] == _CoarseGrid->_processors [d]);
GRID_ASSERT(CoarseGridMulti->_simd_layout[d+1] == _CoarseGrid->_simd_layout[d]);
}
_CoarseGridMulti = CoarseGridMulti;
_Nrhs = CoarseGridMulti->_fdimensions[0];
GRID_ASSERT(_Nrhs>=1);
int nrhs = _Nrhs;
CellMulti = new PaddedCell(geom.Depth(),_CoarseGridMulti);
int32_t padded_sites = CellD.grids.back()->lSites(); // D dimensional
int32_t unpadded_sites = _CoarseGrid->lSites(); // D dimensional
// The neighbour offset multiplication by nrhs is exact only if the D+1
// padded volume is nrhs copies of the D dimensional one. Check it.
GRID_ASSERT(CellMulti->grids.back()->lSites() == nrhs*padded_sites);
GRID_ASSERT(_CoarseGridMulti->lSites() == nrhs*unpadded_sites);
/////////////////////////////////////////////////
// Device data vector storage
/////////////////////////////////////////////////
BLAS_B.resize(nrhs *padded_sites); // includes ghost zone
BLAS_C.resize(nrhs *unpadded_sites); // no ghost zone
BLAS_BP.resize(geom.npoint);
for(int p=0;p<geom.npoint;p++){
BLAS_BP[p].resize(unpadded_sites);
}
BLAS_CP.resize(unpadded_sites);
// Site identity mapping for C
for(int ss=0;ss<unpadded_sites;ss++){
ComplexD *ptr = (ComplexD *)&BLAS_C[ss*nrhs];
acceleratorPut(BLAS_CP[ss],ptr);
}
// Neighbour table is more complicated
int32_t j=0; // Interior point counter (unpadded)
for(int32_t s=0;s<padded_sites;s++){ // D volume, padded
int ghost_zone=0;
for(int32_t point = 0 ; point < geom.npoint; point++){
int i=s*geom.npoint+point;
if( Stencil._entries[i]._wrap ) { // stencil is indexed by the oSite of the D dim grid
ghost_zone=1; // If general stencil wrapped in any direction, wrap=1
}
}
if( ghost_zone==0) {
for(int32_t point = 0 ; point < geom.npoint; point++){
int i=s*geom.npoint+point;
int32_t nbr = Stencil._entries[i]._offset*CComplex::Nsimd(); // oSite -> lSite, D dim
nbr = nbr*nrhs; // D -> D+1, rhs innermost
GRID_ASSERT(nbr<BLAS_B.size());
ComplexD * ptr = (ComplexD *)&BLAS_B[nbr];
acceleratorPut(BLAS_BP[point][j],ptr); // neighbour indexing in ghost zone volume
}
j++;
}
}
GRID_ASSERT(j==unpadded_sites);
}
template<class vobj> void GridtoBLAS(const Lattice<vobj> &from,deviceVector<typename vobj::scalar_object> &to)
{
typedef typename vobj::scalar_object sobj;
typedef typename vobj::scalar_type scalar_type;
typedef typename vobj::vector_type vector_type;
GridBase *Fg = from.Grid();
GRID_ASSERT(!Fg->_isCheckerBoarded);
int nd = Fg->_ndimension;
to.resize(Fg->lSites());
Coordinate LocalLatt = Fg->LocalDimensions();
size_t nsite = 1;
for(int i=0;i<nd;i++) nsite *= LocalLatt[i];
////////////////////////////////////////////////////////////////////////////////////////////////
// do the index calc on the GPU
////////////////////////////////////////////////////////////////////////////////////////////////
Coordinate f_ostride = Fg->_ostride;
Coordinate f_istride = Fg->_istride;
Coordinate f_rdimensions = Fg->_rdimensions;
autoView(from_v,from,AcceleratorRead);
auto to_v = &to[0];
const int words=sizeof(vobj)/sizeof(vector_type);
accelerator_for(idx,nsite,1,{
Coordinate from_coor, base;
Lexicographic::CoorFromIndex(base,idx,LocalLatt);
for(int i=0;i<nd;i++){
from_coor[i] = base[i];
}
int from_oidx = 0; for(int d=0;d<nd;d++) from_oidx+=f_ostride[d]*(from_coor[d]%f_rdimensions[d]);
int from_lane = 0; for(int d=0;d<nd;d++) from_lane+=f_istride[d]*(from_coor[d]/f_rdimensions[d]);
const vector_type* from = (const vector_type *)&from_v[from_oidx];
scalar_type* to = (scalar_type *)&to_v[idx];
scalar_type stmp;
for(int w=0;w<words;w++){
stmp = getlane(from[w], from_lane);
to[w] = stmp;
}
});
}
template<class vobj> void BLAStoGrid(Lattice<vobj> &grid,deviceVector<typename vobj::scalar_object> &in)
{
typedef typename vobj::scalar_object sobj;
typedef typename vobj::scalar_type scalar_type;
typedef typename vobj::vector_type vector_type;
GridBase *Tg = grid.Grid();
GRID_ASSERT(!Tg->_isCheckerBoarded);
int nd = Tg->_ndimension;
GRID_ASSERT(in.size()==Tg->lSites());
Coordinate LocalLatt = Tg->LocalDimensions();
size_t nsite = 1;
for(int i=0;i<nd;i++) nsite *= LocalLatt[i];
////////////////////////////////////////////////////////////////////////////////////////////////
// do the index calc on the GPU
////////////////////////////////////////////////////////////////////////////////////////////////
Coordinate t_ostride = Tg->_ostride;
Coordinate t_istride = Tg->_istride;
Coordinate t_rdimensions = Tg->_rdimensions;
autoView(to_v,grid,AcceleratorWrite);
auto from_v = &in[0];
const int words=sizeof(vobj)/sizeof(vector_type);
accelerator_for(idx,nsite,1,{
Coordinate to_coor, base;
Lexicographic::CoorFromIndex(base,idx,LocalLatt);
for(int i=0;i<nd;i++){
to_coor[i] = base[i];
}
int to_oidx = 0; for(int d=0;d<nd;d++) to_oidx+=t_ostride[d]*(to_coor[d]%t_rdimensions[d]);
int to_lane = 0; for(int d=0;d<nd;d++) to_lane+=t_istride[d]*(to_coor[d]/t_rdimensions[d]);
vector_type* to = (vector_type *)&to_v[to_oidx];
scalar_type* from = (scalar_type *)&from_v[idx];
scalar_type stmp;
for(int w=0;w<words;w++){
stmp=from[w];
putlane(to[w], stmp, to_lane);
}
});
}
///////////////////////////////////////////////////////////////////////////
// Shared by both CoarsenOperator variants
//
// conj(pha[block]) proj[k (which mom)][j (basis vec cpt)][block]
// = \sum_{l in ball} e^{i q_k . delta_l} < phi_{block,j} | MdagM | phi_{(block+delta_l),i} >
// = \sum_{l in ball} e^{iqk.delta_l} A_ji^{b.b+l}
// = M_{kl} A_ji^{b.b+l}
//
// Where q_k = delta_k . (2*M_PI/global_nb[mu])
// Then A{ji}^{b,b+l} = M^{-1}_{lm} ComputeProj_{m,b,i,j}
///////////////////////////////////////////////////////////////////////////
void CoarsenFourierMatrix(GridBase *CoarseGrid,Eigen::MatrixXcd &invMkl)
{
const int npoint = geom_srhs.npoint;
Coordinate clatt = CoarseGrid->GlobalDimensions();
int Nd = CoarseGrid->Nd();
Eigen::MatrixXcd Mkl = Eigen::MatrixXcd::Zero(npoint,npoint);
ComplexD ci(0.0,1.0);
for(int k=0;k<npoint;k++){ // Loop over momenta
for(int l=0;l<npoint;l++){ // Loop over nbr relative
ComplexD phase(0.0,0.0);
for(int mu=0;mu<Nd;mu++){
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
phase=phase+TwoPiL*geom_srhs.shifts[k][mu]*geom_srhs.shifts[l][mu];
}
phase=exp(phase*ci);
Mkl(k,l) = phase;
}
}
invMkl = Mkl.inverse();
}
///////////////////////////////////////////////////////////////////////////
// blockOrthogonalise and blockZAXPY are block operations on the fine
// vectors, using a coarse shaped field only as an index set. They need a
// grid carrying the fine SIMD layout, which the coarse space no longer
// does. Constructed local to the caller so it cannot be mistaken for the
// coarse grid.
///////////////////////////////////////////////////////////////////////////
void CoarsenBlockGridLayout(GridBase *grid,GridBase *CoarseGrid,
Coordinate &latt,Coordinate &simd,Coordinate &mpi)
{
int nd = CoarseGrid->_ndimension;
latt.resize(nd); simd.resize(nd); mpi.resize(nd);
for(int d=0;d<nd;d++){
latt[d] = CoarseGrid->_fdimensions[d];
simd[d] = grid->_simd_layout[d];
mpi [d] = CoarseGrid->_processors[d];
}
}
// D+1 coarse grid holding the batch, rhs innermost and unvectorised
void CoarsenBatchGridLayout(GridBase *CoarseGrid,int batch,
Coordinate &latt,Coordinate &simd,Coordinate &mpi)
{
latt.resize(1,batch); simd.resize(1,1); mpi.resize(1,1);
latt[0]=batch; simd[0]=1; mpi[0]=1;
for(int d=0;d<CoarseGrid->_ndimension;d++){
latt.push_back(CoarseGrid->_fdimensions[d]);
simd.push_back(CoarseGrid->_simd_layout[d]);
mpi .push_back(CoarseGrid->_processors[d]);
}
}
///////////////////////////////////////////////////////////////////////////
// The Fourier inverse needs the phase in the coarse layout and the basis
// phasing needs it in the fine layout; each is built from its own
// coordinates rather than transferred.
///////////////////////////////////////////////////////////////////////////
void CoarsenPhases(GridBase *grid,GridBase *CoarseGrid,GridCartesian *BlockGrid,
std::vector<CoarseComplexField> &pha,
std::vector<FineComplexField> &phaF)
{
const int npoint = geom_srhs.npoint;
Coordinate clatt = CoarseGrid->GlobalDimensions();
int Nd = CoarseGrid->Nd();
ComplexD ci(0.0,1.0);
typedef typename CComplex::scalar_type SComplex;
FineComplexField one(grid); one=SComplex(1.0);
FineComplexField zz(grid); zz = Zero();
BlockComplexField pha_blk (BlockGrid);
BlockComplexField blk_coor(BlockGrid);
for(int p=0;p<npoint;p++){ // Loop over momenta in npoint
CoarseComplexField coor(CoarseGrid);
pha[p] =Zero();
pha_blk=Zero();
for(int mu=0;mu<Nd;mu++){
RealD TwoPiL = M_PI * 2.0/ clatt[mu];
LatticeCoordinate(coor,mu);
pha[p] = pha[p] + (TwoPiL * geom_srhs.shifts[p][mu]) * coor;
LatticeCoordinate(blk_coor,mu);
pha_blk = pha_blk + (TwoPiL * geom_srhs.shifts[p][mu]) * blk_coor;
}
pha[p] =exp(pha[p] *ci);
pha_blk=exp(pha_blk*ci);
blockZAXPY(phaF[p],pha_blk,one,zz);
}
}
///////////////////////////////////////////////////////////////////////////
// Remove the bulk phase from the batch of coarse projections and
// accumulate the Fourier inverse into A. Both variants reach here with
// TmpProj in the same batch coarse order, so this is shared verbatim.
///////////////////////////////////////////////////////////////////////////
void CoarsenAccumulate(int p,int i0,int nbv,int batch,
Eigen::MatrixXcd &invMkl,
std::vector<CoarseComplexField> &pha,
CoarseComplexField &phaB,
CoarseVector &TmpProj,
std::vector<CoarseMatrix> &_A,
GridBase *CoarseGrid)
{
typedef typename CComplex::scalar_type SComplex;
const int npoint = geom_srhs.npoint;
for(int b=0;b<batch;b++) InsertSliceFast(pha[p],phaB,b,0);
TmpProj = conjugate(phaB)*TmpProj;
int osites=CoarseGrid->oSites();
for(int k=0;k<npoint;k++){
SComplex sc(invMkl(p,k).real(),invMkl(p,k).imag());
CComplex coef(sc);
autoView( A_v , _A[k], AcceleratorWrite);
autoView( TP_v , TmpProj, AcceleratorRead);
accelerator_for(sss, osites, 1, {
for(int b=0;b<nbv;b++){
for(int j=0;j<nbasis;j++){
A_v[sss](i0+b,j) = A_v[sss](i0+b,j) + coef*TP_v[b+batch*sss](j);
}
}
});
}
}
void CoarsenReport(RealD tphase,RealD tphaseBZ,RealD tslice,
RealD tmat,RealD tproj,RealD tinv)
{
std::cout << GridLogMessage<<"CoarsenOperator phase "<<tphase<<" us"<<std::endl;
std::cout << GridLogMessage<<"CoarsenOperator phaseBZ "<<tphaseBZ<<" us"<<std::endl;
std::cout << GridLogMessage<<"CoarsenOperator slice "<<tslice <<" us"<<std::endl;
std::cout << GridLogMessage<<"CoarsenOperator mat "<<tmat <<" us"<<std::endl;
std::cout << GridLogMessage<<"CoarsenOperator proj "<<tproj<<" us"<<std::endl;
std::cout << GridLogMessage<<"CoarsenOperator inv "<<tinv<<" us"<<std::endl;
}
///////////////////////////////////////////////////////////////////////////
// Coarsen a NATIVELY multiRHS fine operator.
//
// linop acts on the D+1 dimensional fine grid FineGridMulti, with the batch
// of phased basis vectors carried in the rhs direction. A single RHS
// operator can be promoted with MrhsPromotedOperator, but that pays an
// ExtractSlice/InsertSlice pair per rhs; prefer the single RHS variant
// below in that case.
///////////////////////////////////////////////////////////////////////////
void CoarsenOperator(LinearOperatorBase<Lattice<Fobj> > &linop,
GridCartesian *FineGridMulti,
std::vector<FineField> &Subspace,
GridBase *CoarseGrid)
{
RealD tproj=0.0, tmat=0.0, tphase=0.0, tphaseBZ=0.0, tslice=0.0, tinv=0.0;
std::cout << GridLogMessage<< "GeneralCoarsenMatrixMrhs (multiRHS fine operator)"<< std::endl;
GRID_ASSERT(Subspace.size()==nbasis);
GridBase *grid = Subspace[0].Grid();
GRID_ASSERT(FineGridMulti->_ndimension == grid->_ndimension+1);
GRID_ASSERT(FineGridMulti->_processors[0] == 1);
for(int d=0;d<grid->_ndimension;d++){
GRID_ASSERT(FineGridMulti->_fdimensions[d+1] == grid->_fdimensions[d]);
GRID_ASSERT(FineGridMulti->_processors [d+1] == grid->_processors [d]);
}
int batch = FineGridMulti->_fdimensions[0];
Coordinate blatt,bsimd,bmpi;
CoarsenBlockGridLayout(grid,CoarseGrid,blatt,bsimd,bmpi);
GridCartesian BlockGrid(blatt,bsimd,bmpi);
BlockComplexField InnerProd(&BlockGrid);
blockOrthogonalise(InnerProd,Subspace);
MultiRHSBlockProject<Lattice<Fobj> > Projector;
Projector.Allocate(nbasis,grid,CoarseGrid);
Projector.ImportBasis(Subspace);
const int npoint = geom_srhs.npoint;
Eigen::MatrixXcd invMkl;
CoarsenFourierMatrix(CoarseGrid,invMkl);
FineField phaV(grid);
std::vector<FineComplexField> phaF(npoint,grid);
std::vector<CoarseComplexField> pha (npoint,CoarseGrid);
tphase=-usecond();
CoarsenPhases(grid,CoarseGrid,&BlockGrid,pha,phaF);
tphase+=usecond();
std::vector<CoarseMatrix> _A;
_A.resize(npoint,CoarseGrid);
for(int k=0;k<npoint;k++) _A[k] = Zero();
Coordinate cmlatt,cmsimd,cmmpi;
CoarsenBatchGridLayout(CoarseGrid,batch,cmlatt,cmsimd,cmmpi);
GridCartesian CoarseBatchGrid(cmlatt,cmsimd,cmmpi);
CoarseVector TmpProj(&CoarseBatchGrid);
CoarseComplexField phaB(&CoarseBatchGrid);
FineField hi_in (FineGridMulti);
FineField hi_out(FineGridMulti);
FineField zzF(grid); zzF = Zero();
for(int i0=0;i0<nbasis;i0+=batch){ // Loop over batches of basis vectors
int nbv = MIN(batch,nbasis-i0);
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i0<<"/"<<nbasis<< std::endl;
for(int p=0;p<npoint;p++){ // Loop over momenta
// One phase, applied to the whole batch. Tail slices are zeroed so
// the operator never sees undefined data.
for(int b=0;b<nbv;b++){
tphaseBZ-=usecond();
phaV = phaF[p]*Subspace[i0+b];
tphaseBZ+=usecond();
tslice-=usecond();
InsertSliceFast(phaV,hi_in,b,0);
tslice+=usecond();
}
tslice-=usecond();
for(int b=nbv;b<batch;b++){
InsertSliceFast(zzF,hi_in,b,0);
}
tslice+=usecond();
tmat-=usecond();
linop.Op(hi_in,hi_out);
tmat+=usecond();
tproj-=usecond();
Projector.blockProject(hi_out,TmpProj);
tproj+=usecond();
tinv-=usecond();
CoarsenAccumulate(p,i0,nbv,batch,invMkl,pha,phaB,TmpProj,_A,CoarseGrid);
tinv+=usecond();
}
}
for(int p=0;p<npoint;p++){
GridtoBLAS(_A[p],BLAS_A[p]);
}
CoarsenReport(tphase,tphaseBZ,tslice,tmat,tproj,tinv);
}
///////////////////////////////////////////////////////////////////////////
// Coarsen a SINGLE RHS fine operator.
//
// No multiRHS packing: the operator is applied once per phased basis
// vector and the batch is assembled on the coarse side by the mixed
// blockProject, which takes a vector of fine fields and writes the batch
// coarse field the accumulate expects. Only nbv applications per momentum,
// so a batch that does not divide nbasis wastes nothing, and the live fine
// storage is batch fields rather than two D+1 fields of extent batch.
///////////////////////////////////////////////////////////////////////////
void CoarsenOperator(LinearOperatorBase<Lattice<Fobj> > &linop,
std::vector<FineField> &Subspace,
GridBase *CoarseGrid,
int batch)
{
RealD tproj=0.0, tmat=0.0, tphase=0.0, tphaseBZ=0.0, tslice=0.0, tinv=0.0;
std::cout << GridLogMessage<< "GeneralCoarsenMatrixMrhs (single RHS fine operator)"<< std::endl;
GRID_ASSERT(Subspace.size()==nbasis);
GRID_ASSERT(batch>=1);
GridBase *grid = Subspace[0].Grid();
Coordinate blatt,bsimd,bmpi;
CoarsenBlockGridLayout(grid,CoarseGrid,blatt,bsimd,bmpi);
GridCartesian BlockGrid(blatt,bsimd,bmpi);
BlockComplexField InnerProd(&BlockGrid);
blockOrthogonalise(InnerProd,Subspace);
MultiRHSBlockProject<Lattice<Fobj> > Projector;
Projector.Allocate(nbasis,grid,CoarseGrid);
Projector.ImportBasis(Subspace);
const int npoint = geom_srhs.npoint;
Eigen::MatrixXcd invMkl;
CoarsenFourierMatrix(CoarseGrid,invMkl);
FineField phaV(grid);
std::vector<FineComplexField> phaF(npoint,grid);
std::vector<CoarseComplexField> pha (npoint,CoarseGrid);
tphase=-usecond();
CoarsenPhases(grid,CoarseGrid,&BlockGrid,pha,phaF);
tphase+=usecond();
std::vector<CoarseMatrix> _A;
_A.resize(npoint,CoarseGrid);
for(int k=0;k<npoint;k++) _A[k] = Zero();
Coordinate cmlatt,cmsimd,cmmpi;
CoarsenBatchGridLayout(CoarseGrid,batch,cmlatt,cmsimd,cmmpi);
GridCartesian CoarseBatchGrid(cmlatt,cmsimd,cmmpi);
CoarseVector TmpProj(&CoarseBatchGrid);
CoarseComplexField phaB(&CoarseBatchGrid);
std::vector<FineField> MphaV(batch,grid);
for(int i0=0;i0<nbasis;i0+=batch){ // Loop over batches of basis vectors
int nbv = MIN(batch,nbasis-i0);
std::cout << GridLogMessage<< "CoarsenMatrixColoured vec "<<i0<<"/"<<nbasis<< std::endl;
for(int p=0;p<npoint;p++){ // Loop over momenta
for(int b=0;b<nbv;b++){
tphaseBZ-=usecond();
phaV = phaF[p]*Subspace[i0+b];
tphaseBZ+=usecond();
tmat-=usecond();
linop.Op(phaV,MphaV[b]);
tmat+=usecond();
}
// The accumulate reads only the first nbv slices, but the projector
// sees the whole vector, so the tail must not be undefined.
for(int b=nbv;b<batch;b++) MphaV[b] = Zero();
tproj-=usecond();
Projector.blockProject(MphaV,TmpProj);
tproj+=usecond();
tinv-=usecond();
CoarsenAccumulate(p,i0,nbv,batch,invMkl,pha,phaB,TmpProj,_A,CoarseGrid);
tinv+=usecond();
}
}
for(int p=0;p<npoint;p++){
GridtoBLAS(_A[p],BLAS_A[p]);
}
CoarsenReport(tphase,tphaseBZ,tslice,tmat,tproj,tinv);
}
void Mdag(const CoarseVector &in, CoarseVector &out)
{
this->M(in,out);
}
void M (const CoarseVector &in, CoarseVector &out)
{
// std::cout << GridLogMessage << "New Mrhs coarse"<<std::endl;
conformable(CoarseGrid(),in.Grid());
conformable(in.Grid(),out.Grid());
out.Checkerboard() = in.Checkerboard();
RealD t_tot;
RealD t_exch;
RealD t_GtoB;
RealD t_BtoG;
RealD t_mult;
CheckGridSet();
if ( in.Grid() != _CoarseGridMulti ) {
std::cout << GridLogError
<< "MultiGeneralCoarsenedOperatorV2::M called with a field on a"
<< std::endl;
std::cout << GridLogError
<< " different grid object from the one given to SetGrid(). Two"
<< std::endl;
std::cout << GridLogError
<< " grids of identical shape do not conform; share one object."
<< std::endl;
GRID_ASSERT(in.Grid() == _CoarseGridMulti);
}
GRID_TRACE("CoarseV2Mult");
t_tot=-usecond();
CoarseVector tin=in;
t_exch=-usecond();
// lambda scope so the roctx range covers exactly the exchange; the
// PaddedCellFwd/BwdMPI markers inside it then nest properly.
CoarseVector pin = [&](){ GRID_TRACE("CoarseV2Exchange");
return CellMulti->ExchangePeriodic(tin); }(); //padded input
t_exch+=usecond();
CoarseVector pout(pin.Grid());
int npoint = geom.npoint;
typedef calcMatrix* Aview;
typedef LatticeView<Cvec> Vview;
const int Nsimd = CComplex::Nsimd();
int64_t nrhs =pin.Grid()->GlobalDimensions()[0];
GRID_ASSERT(nrhs>=1);
RealD flops,bytes;
int64_t osites=in.Grid()->oSites(); // unpadded
int64_t unpadded_vol = CoarseGrid()->lSites()/nrhs;
flops = 1.0* npoint * nbasis * nbasis * 8.0 * osites * CComplex::Nsimd();
bytes = 1.0*osites*sizeof(siteMatrix)*npoint/pin.Grid()->GlobalDimensions()[0]
+ 2.0*osites*sizeof(siteVector)*npoint;
t_GtoB=-usecond();
{ GRID_TRACE("CoarseV2GridToBLAS");
GridtoBLAS(pin,BLAS_B);
}
t_GtoB+=usecond();
GridBLAS BLAS;
t_mult=-usecond();
{ GRID_TRACE("CoarseV2StencilGEMM");
for(int p=0;p<geom.npoint;p++){
RealD c = 1.0;
if (p==0) c = 0.0;
ComplexD beta(c);
BLAS.gemmBatched(nbasis,nrhs,nbasis,
ComplexD(1.0),
BLAS_AP[p],
BLAS_BP[p],
ComplexD(c),
BLAS_CP);
}
BLAS.synchronise();
}
t_mult+=usecond();
t_BtoG=-usecond();
{ GRID_TRACE("CoarseV2BLASToGrid");
BLAStoGrid(out,BLAS_C);
}
t_BtoG+=usecond();
t_tot+=usecond();
/*
std::cout << GridLogMessage << "New Mrhs coarse DONE "<<std::endl;
std::cout << GridLogMessage<<"Coarse Mult exch "<<t_exch<<" us"<<std::endl;
std::cout << GridLogMessage<<"Coarse Mult mult "<<t_mult<<" us"<<std::endl;
std::cout << GridLogMessage<<"Coarse Mult GtoB "<<t_GtoB<<" us"<<std::endl;
std::cout << GridLogMessage<<"Coarse Mult BtoG "<<t_BtoG<<" us"<<std::endl;
std::cout << GridLogMessage<<"Coarse Mult tot "<<t_tot<<" us"<<std::endl;
*/
// std::cout << GridLogMessage<<std::endl;
// std::cout << GridLogMessage<<"Coarse Kernel flops "<< flops<<std::endl;
// std::cout << GridLogMessage<<"Coarse Kernel flop/s "<< flops/t_mult<<" mflop/s"<<std::endl;
// std::cout << GridLogMessage<<"Coarse Kernel bytes/s "<< bytes/t_mult/1000<<" GB/s"<<std::endl;
// std::cout << GridLogMessage<<"Coarse overall flops/s "<< flops/t_tot<<" mflop/s"<<std::endl;
// std::cout << GridLogMessage<<"Coarse total bytes "<< bytes/1e6<<" MB"<<std::endl;
};
virtual void Mdiag (const Field &in, Field &out){ GRID_ASSERT(0);};
virtual void Mdir (const Field &in, Field &out,int dir, int disp){assert(0);};
virtual void MdirAll (const Field &in, std::vector<Field> &out){assert(0);};
};
NAMESPACE_END(Grid);
+3 -3
View File
@@ -67,8 +67,8 @@ public:
} }
int point(int dir, int disp) { int point(int dir, int disp) {
GRID_ASSERT(disp == -1 || disp == 0 || disp == 1); assert(disp == -1 || disp == 0 || disp == 1);
GRID_ASSERT(base+0 <= dir && dir < base+4); assert(base+0 <= dir && dir < base+4);
// directions faster index = new indexing // directions faster index = new indexing
// 4d (base = 0): // 4d (base = 0):
@@ -131,7 +131,7 @@ public:
return p; return p;
} }
} }
GRID_ASSERT(0); assert(0);
return -1; return -1;
} }
void BuildShifts(void) void BuildShifts(void)
@@ -1,161 +0,0 @@
/*************************************************************************************
Grid physics library, www.github.com/paboyle/Grid
Source file: ./lib/algorithms/multigrid/MrhsPromotedOperator.h
Copyright (C) 2026
Author: Peter Boyle <pboyle@bnl.gov>
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 */
#pragma once
NAMESPACE_BEGIN(Grid);
///////////////////////////////////////////////////////////////////////////////
// Present a D dimensional operator as a D+1 dimensional operator with Nrhs in
// dimension 0. Field type is unchanged; only the Grid differs, so this is a
// LinearOperatorBase and callers need no template parameter: a native mrhs
// operator derives from the same base and substitutes without a call site
// change.
//
// Slices in and out around the wrapped operator. No arithmetic beyond the
// wrapped call, but one ExtractSliceFast/InsertSliceFast pair per right hand
// side: data motion, not work.
//
// AdjOp is carried so that A^dag may be coarsened as a separate coarse
// operator when needed, rather than doubling coarse storage.
///////////////////////////////////////////////////////////////////////////////
template<class Field>
class MrhsPromotedOperator : public LinearOperatorBase<Field>
{
private:
LinearOperatorBase<Field> &_LinOp;
GridBase *_LowGrid;
int _Nrhs;
public:
MrhsPromotedOperator(LinearOperatorBase<Field> &LinOp,GridBase *LowGrid,int Nrhs)
: _LinOp(LinOp), _LowGrid(LowGrid), _Nrhs(Nrhs)
{
GRID_ASSERT(_Nrhs>=1);
}
GridBase *LowGrid(void) { return _LowGrid; }
int Nrhs(void) { return _Nrhs; }
// Reset on each call; retrieve and accumulate in the caller
RealD tslice;
RealD top;
void OpDiag (const Field &in, Field &out)
{
SliceLoop(in,out,[&](Field &i,Field &o){ _LinOp.OpDiag(i,o); });
}
void Op (const Field &in, Field &out)
{
SliceLoop(in,out,[&](Field &i,Field &o){ _LinOp.Op(i,o); });
}
void AdjOp (const Field &in, Field &out)
{
SliceLoop(in,out,[&](Field &i,Field &o){ _LinOp.AdjOp(i,o); });
}
void HermOp (const Field &in, Field &out)
{
SliceLoop(in,out,[&](Field &i,Field &o){ _LinOp.HermOp(i,o); });
}
void OpDir (const Field &in, Field &out,int dir,int disp)
{
SliceLoop(in,out,[&](Field &i,Field &o){ _LinOp.OpDir(i,o,dir,disp); });
}
//////////////////////////////////////////////////////////////////
// Norms of the D+1 field are the sums over slices
//////////////////////////////////////////////////////////////////
void HermOpAndNorm(const Field &in, Field &out,RealD &n1,RealD &n2)
{
Conformable(in,out);
Field lo_in (_LowGrid);
Field lo_out(_LowGrid);
n1=0.0;
n2=0.0;
for(int r=0;r<_Nrhs;r++){
RealD r1,r2;
ExtractSliceFast(lo_in,in,r,0);
_LinOp.HermOpAndNorm(lo_in,lo_out,r1,r2);
InsertSliceFast(lo_out,out,r,0);
n1=n1+r1;
n2=n2+r2;
}
}
void OpDirAll(const Field &in, std::vector<Field> &out)
{
int npoint = out.size();
Field lo_in(_LowGrid);
std::vector<Field> lo_out(npoint,_LowGrid);
for(int r=0;r<_Nrhs;r++){
ExtractSliceFast(lo_in,in,r,0);
_LinOp.OpDirAll(lo_in,lo_out);
for(int p=0;p<npoint;p++){
InsertSliceFast(lo_out[p],out[p],r,0);
}
}
}
private:
void Conformable(const Field &in,const Field &out)
{
conformable(in.Grid(),out.Grid());
GRID_ASSERT(in.Grid()->_ndimension == _LowGrid->_ndimension+1);
GRID_ASSERT(in.Grid()->_fdimensions[0] == _Nrhs);
}
template<class Kernel>
void SliceLoop(const Field &in,Field &out,Kernel K)
{
Conformable(in,out);
Field lo_in (_LowGrid);
Field lo_out(_LowGrid);
tslice=0.0;
top=0.0;
for(int r=0;r<_Nrhs;r++){
tslice-=usecond();
ExtractSliceFast(lo_in,in,r,0);
tslice+=usecond();
top-=usecond();
K(lo_in,lo_out);
top+=usecond();
tslice-=usecond();
InsertSliceFast(lo_out,out,r,0);
tslice+=usecond();
}
}
};
NAMESPACE_END(Grid);
-3
View File
@@ -32,6 +32,3 @@ Author: Peter Boyle <pboyle@bnl.gov>
#include <Grid/algorithms/multigrid/CoarsenedMatrix.h> #include <Grid/algorithms/multigrid/CoarsenedMatrix.h>
#include <Grid/algorithms/multigrid/GeneralCoarsenedMatrix.h> #include <Grid/algorithms/multigrid/GeneralCoarsenedMatrix.h>
#include <Grid/algorithms/multigrid/GeneralCoarsenedMatrixMultiRHS.h> #include <Grid/algorithms/multigrid/GeneralCoarsenedMatrixMultiRHS.h>
#include <Grid/algorithms/multigrid/GeneralCoarsenedMatrixMultiRHSV2.h>
#include <Grid/algorithms/multigrid/MrhsPromotedOperator.h>
#include <Grid/algorithms/multigrid/Smoothers.h>
@@ -1,874 +0,0 @@
/*************************************************************************************
Grid physics library, www.github.com/paboyle/Grid
Source file: RecursiveSchurInverse.h
Copyright (C) 2026
Author: Peter Boyle <pboyle@bnl.gov>
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 */
#pragma once
#include <Grid/algorithms/blas/BatchedBlas.h>
#include <Grid/algorithms/blas/BatchedInverse.h>
#include <algorithm>
NAMESPACE_BEGIN(Grid);
///////////////////////////////////////////////////////////////////////////////
// RecursiveSchurInverse: distributed dense inversion by recursive Schur
// complement over a binary rank-range tree.
//
// Contract: rank r owns global rows [rowStart[r], rowStart[r+1]) of an
// N x N matrix in rank-major ordering, and receives its rows of the
// inverse in the same layout. Consumes GridBase collectives, GridBLAS
// and GridBLASInverse only; Eigen reference backends permit CPU unit
// testing under mpirun (Test_schur_inverse).
//
// Arithmetic is fp64 throughout; the caller rounds once into fp32 storage.
//
// Execution: SPMD full-tree walk. Every rank makes the identical call
// sequence; data participation is ownership-gated; all collectives are
// world-wide, so no deadlock surface exists.
//
// Storage: BlockRows is column-major, ld = rows; element (i,j) at
// data[i + j*ld]; a column window is the contiguous slice at data[col0*ld].
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// My rows of a distributed dense matrix: rows x cols, column major, ld = rows.
///////////////////////////////////////////////////////////////////////////////
class BlockRows
{
public:
deviceVector<ComplexD> data;
int64_t rows;
int64_t cols;
int64_t ld;
BlockRows()
{
rows = 0;
cols = 0;
ld = 0;
}
void Resize(int64_t r, int64_t c)
{
rows = r;
cols = c;
ld = r;
data.resize((uint64_t)r*c);
}
ComplexD *ColumnWindow(int64_t col0)
{
GRID_ASSERT( col0 >= 0 );
GRID_ASSERT( col0 <= cols );
return &data[(uint64_t)col0*ld];
}
};
class RecursiveSchurInverse
{
public:
GridBase *grid; // world collectives only
int64_t N; // global matrix dimension
int P; // ranks
int me; // this rank
std::vector<int64_t> rowStart; // P+1 entries: rank-major row ownership
int64_t myRow0;
int64_t myNrows;
int64_t panelBytes; // gather panel budget (DENSE_PANEL_BYTES)
GridBLAS BLAS;
GridBLASInverse INV;
// Growth telemetry: one entry per merge node, walk order
std::vector<double> telNormB; // ||B||_F = ||A11inv A12||_F
std::vector<double> telSratio; // ||S||_F / ||A22||_F
double telLeafMaxInv; // max |(leaf inverse)_ij| over leaves
// Phase timers/counters, reported by ReportTelemetry
double tMemset; // device panel zero-fill
double tDeposit; // owner rows -> panel (device kernel)
double tAllreduce; // panel collective (see tBarrier)
double tBarrier; // arrival skew, when DENSE_BARRIER_PROBE
double tRepack; // rank-major -> panel (gather path)
double tGemm; // strided gemm + synchronise
double tLeaf; // leaf inversions
double tARmin; // fastest single panel collective
double tARmax; // slowest single panel collective
std::vector<double> tARall; // every panel collective, for percentiles
uint64_t bytesAllreduce;
uint64_t nAllreduce; // panel collectives
uint64_t nGatherGemm; // GatherGemm calls
uint64_t nGather; // gather-path collectives (debug gate)
uint64_t nGatherV; // ... of which MPI_Allgatherv
uint64_t nBcast; // ... of which Bcast-assembled
// Persistent grow-only device panel; assembly and collectives are
// device-resident. Device builds require GPU-aware MPI.
deviceVector<ComplexD> dPanelBuf;
// Rank-major receive staging for the AllGatherV path, plus the per-panel-row
// owner maps that drive the repack. Grow-only, same discipline as dPanelBuf:
// a fresh device allocation per collective is catastrophic (measured).
deviceVector<ComplexD> dRecvBuf;
deviceVector<int64_t> dOwnerOff; // panel row -> owner's first panel row
deviceVector<int64_t> dOwnerRows; // panel row -> owner's row count
// DENSE_GATHER : transport for the panel assembly.
// 0 = zero-fill + GlobalSumVector (default; moves the payload twice)
// 1 = MPI_Allgatherv (KNOWN BROKEN here, see guard below)
// 2 = C sequential MPI_Bcast, one per contributing rank. Bcast has no
// count vector, so the zero-count shape that breaks Allgatherv
// cannot arise. Costs C collectives instead of 1 and the roots do
// not transmit concurrently, so the byte cost is ~2*panel per rank
// -- the same as the allreduce. It wins only if Bcast is faster
// PER BYTE than Allreduce, which is why it must be measured.
// DENSE_GATHER_MIN_BYTES : panels below this stay on the allreduce path.
// Default 0 (always gather). The Schur tree puts
// 94% of its bytes in panels with ~3.7 MB per-rank
// messages, i.e. squarely bandwidth bound; only the
// two deepest levels (1.2% of bytes, 16-65 KB per
// rank) are latency bound. This exists so that
// tail can be excluded with an env var rather than
// a rebuild, if it ever proves to matter.
// DENSE_BARRIER_PROBE : Barrier() before each panel collective.
// DEFAULTS ON, and it is an optimisation rather than instrumentation.
// Measured at 288 ranks, N=138240: comms 380.3 s -> 314.7 s and the whole
// invert 394.3 s -> 327.7 s, with the worst single collective falling
// 2474.9 ms -> 256.2 ms and effective rate 2.40 -> 4.27 GB/s. Convoy
// accounting alone predicts NO saving (barrier and collective both
// complete at max-over-ranks), so the mechanism is that the collective
// itself degrades under skewed arrival, not that skew is rebooked.
// Set DENSE_BARRIER_PROBE=0 to recover the old behaviour. It also
// separates arrival skew (tBarrier) from transfer (tAllreduce).
int useGather;
int64_t gatherMinBytes;
int barrierProbe;
// DENSE_GATHER_DEBUG=N : dump the descriptor of the first N AllGatherV
// calls from rank 0. The tiling invariant below is checked ALWAYS -- it
// costs one O(P) host loop against a collective, and a counts array that
// does not tile the panel is exactly the failure we are hunting.
int gatherDebug;
///////////////////////////////////////////////////////////////////////////
// Ownership-table validation: a proper partition of [0,N).
// Static and communicator-free so synthetic tables unit-test directly.
///////////////////////////////////////////////////////////////////////////
static void CheckRowStart(const std::vector<int64_t> &table, int64_t N)
{
int P = (int)table.size() - 1;
GRID_ASSERT( P >= 1 );
GRID_ASSERT( table[0] == 0 );
GRID_ASSERT( table[P] == N );
for(int r=0; r<P; r++)
{
GRID_ASSERT( table[r+1] >= table[r] ); // zero-row ranks permitted
}
}
///////////////////////////////////////////////////////////////////////////
// Build the ownership table from each rank's local row count: zero-fill
// allgather (the standing comms idiom) then prefix sum. Every rank
// returns the identical table.
///////////////////////////////////////////////////////////////////////////
static std::vector<int64_t> MakeRowStart(GridBase *g, int64_t myNrows)
{
int P = g->ProcessorCount();
int me = g->ThisRank();
std::vector<uint64_t> counts(P, 0);
counts[me] = (uint64_t)myNrows;
g->GlobalSumVector(&counts[0], P);
std::vector<int64_t> table(P+1);
table[0] = 0;
for(int r=0; r<P; r++)
{
table[r+1] = table[r] + (int64_t)counts[r];
}
CheckRowStart(table, table[P]);
return table;
}
RecursiveSchurInverse(GridBase *g,
int64_t N_,
std::vector<int64_t> &rowStart_,
int64_t panelBytes_)
{
grid = g;
N = N_;
P = g->ProcessorCount();
me = g->ThisRank();
rowStart = rowStart_;
panelBytes = panelBytes_;
GRID_ASSERT( (int)rowStart.size() == P+1 );
CheckRowStart(rowStart, N);
myRow0 = rowStart[me];
myNrows = rowStart[me+1] - rowStart[me];
telLeafMaxInv = 0.0;
useGather = getenv("DENSE_GATHER") ? atoi(getenv("DENSE_GATHER")) : 0;
gatherMinBytes = getenv("DENSE_GATHER_MIN_BYTES")
? atol(getenv("DENSE_GATHER_MIN_BYTES")) : 0;
barrierProbe = getenv("DENSE_BARRIER_PROBE") ? atoi(getenv("DENSE_BARRIER_PROBE")) : 1;
gatherDebug = getenv("DENSE_GATHER_DEBUG") ? atoi(getenv("DENSE_GATHER_DEBUG")) : 0;
///////////////////////////////////////////////////////////////////////
// DENSE_GATHER=1 is KNOWN BROKEN on Cray MPICH and refuses to start.
//
// The gather asks MPI_Allgatherv to assemble a panel to which only the
// rank sub-range [rB0,rB1) contributes; every other rank passes count 0.
// At Schur depth d that is P/2^(d+1) contributors out of P, i.e. 9 of 288
// at depth 4 and ~1 of 288 at depth 7. Measured consequences at 288
// ranks (tests/debug/Test_allgather):
// T5 288/288 contributing, 18.4 MB on device : sub-second.
// T6 144/288 contributing, 9.2 MB on device : ~53 s.
// and in production, 9/288 contributing at 299 MB hangs and then aborts
// inside PMPI_Allgatherv ("req != NULL", mpir_request.h:508).
//
// The primitive itself is sound -- Test_allgather T1-T6 pass, and T3/T4
// verify it BITWISE against the zero-fill+GlobalSum idiom. What fails is
// MPI's handling of a collective in which almost every rank contributes
// nothing. Fixing it needs either P2P (a bisection allgather over
// [r0,r1)) or the 2D block-cyclic layout, which removes the shape
// altogether by giving every rank part of every sub-block.
//
// Fail here, in the first second, rather than 100 s and 36 nodes into a
// job. DENSE_GATHER_FORCE=1 proceeds anyway for debugging.
///////////////////////////////////////////////////////////////////////
if ( (useGather==1) && !getenv("DENSE_GATHER_FORCE") )
{
std::cout << GridLogError
<< "DENSE_GATHER=1 is disabled: MPI_Allgatherv on this MPI is"
<< " pathological when most ranks contribute count 0 (see the"
<< " note at RecursiveSchurInverse.h, and Test_allgather T5 vs"
<< " T6). Unset DENSE_GATHER, or set DENSE_GATHER_FORCE=1 to"
<< " proceed anyway." << std::endl;
GRID_ASSERT(0 && "DENSE_GATHER=1 known broken: see comment above");
}
}
///////////////////////////////////////////////////////////////////////////
// The communication primitive.
//
// C(:, colC : colC+widthB) <- beta * C(:, colC : colC+widthB)
// + alpha * A(:, colA : colA+widthA) * Bsub
//
// Bsub is the widthA x widthB sub-block of a row-distributed operand
// owned by ranks [rB0, rB1): owner r contributes its rows of
// B(:, colB : colB+widthB) at sub-block row offset
// rowStart[r] - rowStart[rB0], gathered in panelBytes row-chunks by
// device zero-fill + deposit kernel + GlobalSumVector.
//
// Every rank calls; non-owners of B add zeros; ranks with A.rows == 0
// skip local compute but make every collective call. Column offsets
// are local buffer offsets -- non-participants pass 0.
///////////////////////////////////////////////////////////////////////////
void GatherGemm(ComplexD alpha,
BlockRows &A, int64_t colA, int64_t widthA,
int rB0, int rB1,
BlockRows &B, int64_t colB, int64_t widthB,
ComplexD beta,
BlockRows &C, int64_t colC)
{
GRID_ASSERT( rB0 >= 0 );
GRID_ASSERT( rB1 > rB0 );
GRID_ASSERT( rB1 <= P );
int64_t k = rowStart[rB1] - rowStart[rB0];
int64_t m = A.rows;
int64_t n = widthB;
GRID_ASSERT( widthA == k );
GRID_ASSERT( n >= 1 );
int owner = ( me >= rB0 ) && ( me < rB1 ) && ( B.rows > 0 );
int64_t myOff = 0;
if ( owner )
{
myOff = rowStart[me] - rowStart[rB0];
}
if ( m > 0 )
{
GRID_ASSERT( colA + widthA <= A.cols );
GRID_ASSERT( colC + widthB <= C.cols );
GRID_ASSERT( C.rows == m );
}
nGatherGemm++;
GRID_TRACE("GatherGemm");
if ( owner )
{
GRID_ASSERT( colB + widthB <= B.cols );
}
// COLUMN chunking, not row chunking. Each rank's contribution to a
// column chunk is rows_r x nchunk at ld = B.rows -- i.e. exactly the
// contiguous window B.ColumnWindow(colB+j0) -- so the AllGatherV send
// needs no pack. Row chunking would slice each column and force one.
// It also makes every chunk a full-k product, so beta applies directly
// and the cross-chunk accumulation disappears.
int64_t bufs = useGather ? 2 : 1; // panel, plus receive staging
int64_t nc = panelBytes / ( bufs * (int64_t)sizeof(ComplexD) * k );
if ( nc < 1 ) nc = 1;
if ( nc > n ) nc = n;
GRID_ASSERT( k*nc < 2147483647L ); // collective counts are int
deviceVector<ComplexD> &dPanel = dPanelBuf;
if ( dPanel.size() < (uint64_t)k*nc ) dPanel.resize((uint64_t)k*nc);
deviceVector<ComplexD*> ap(1);
deviceVector<ComplexD*> bp(1);
deviceVector<ComplexD*> cp(1);
std::vector<ComplexD*> ptr(1);
// Panel-row -> owner maps for the repack. Depend only on [rB0,rB1),
// so they are built once per call rather than once per chunk.
if ( useGather )
{
if ( dRecvBuf.size() < (uint64_t)k*nc ) dRecvBuf.resize((uint64_t)k*nc);
if ( dOwnerOff.size() < (uint64_t)k ) dOwnerOff.resize((uint64_t)k);
if ( dOwnerRows.size() < (uint64_t)k ) dOwnerRows.resize((uint64_t)k);
std::vector<int64_t> hoff(k), hrows(k);
for(int r=rB0; r<rB1; r++)
{
int64_t off_r = rowStart[r] - rowStart[rB0];
int64_t rows_r = rowStart[r+1] - rowStart[r];
for(int64_t i=0;i<rows_r;i++){ hoff[off_r+i]=off_r; hrows[off_r+i]=rows_r; }
}
acceleratorCopyToDevice(&hoff[0], &dOwnerOff[0], (uint64_t)k*sizeof(int64_t));
acceleratorCopyToDevice(&hrows[0],&dOwnerRows[0],(uint64_t)k*sizeof(int64_t));
if ( owner ) GRID_ASSERT( B.rows == rowStart[me+1]-rowStart[me] );
}
for(int64_t j0=0; j0<n; j0+=nc)
{
int64_t nchunk = std::min(nc, n-j0);
uint64_t panelWords = (uint64_t)k*nchunk;
uint64_t panelBytesThis = panelWords*sizeof(ComplexD);
// The MODE (0/1/2), not a boolean: `useGather && cond` collapses 2 to 1
// and silently dispatched DENSE_GATHER=2 onto the known-broken
// AllGatherV path (Frontier hang, 2026-08-24). Identical on all ranks.
int gatherThis = ( (int64_t)panelBytesThis >= gatherMinBytes ) ? useGather : 0;
// Arrival skew is charged to the barrier, so that tAllreduce measures
// transfer alone. Diagnostic only; off by default.
if ( barrierProbe ) { double tb = -usecond(); grid->Barrier(); tBarrier += tb+usecond(); }
double tar = -usecond();
if ( gatherThis )
{
//////////////////////////////////////////////////////////////////
// AllGatherV: move the payload once, no arithmetic, no zero fill.
// Receive is rank major -- a concatenation of rows_r x nchunk
// column-major blocks -- which is the correct ROW order but the
// wrong LAYOUT, so one device repack follows.
//////////////////////////////////////////////////////////////////
std::vector<int> counts(P,0), displs(P,0);
for(int r=rB0; r<rB1; r++)
{
counts[r] = (int)((rowStart[r+1]-rowStart[r])*nchunk);
displs[r] = (int)((rowStart[r] -rowStart[rB0])*nchunk);
}
//////////////////////////////////////////////////////////////////
// The counts MUST tile the panel exactly: every one of the
// k*nchunk words has exactly one owner, or MPI is handed an
// inconsistent descriptor. Always checked; O(P) against a
// collective is free.
//////////////////////////////////////////////////////////////////
int64_t csum = 0; int nz = 0;
for(int r=0;r<P;r++){ csum += counts[r]; if ( counts[r] ) nz++; }
GRID_ASSERT( csum == (int64_t)panelWords );
GRID_ASSERT( displs[rB1-1] + counts[rB1-1] == (int)panelWords );
// Gate on the count of GATHERS, not of all collectives. nAllreduce
// is already in the hundreds by the first gather -- the recursion is
// depth first and everything below the size threshold takes the
// allreduce path -- so gating on it prints nothing at any sane value.
nGather++;
if ( gatherDebug && ((int)nGather <= gatherDebug) && (me==0) ) {
std::cout << GridLogMessage << "GATHER["<<nGather-1<<"]"
<< " ranks ["<<rB0<<","<<rB1<<")"
<< " k "<<k<<" n "<<n<<" nchunk "<<nchunk
<< " panelWords "<<panelWords
<< " ("<<panelBytesThis/1024/1024<<" MB)"
<< " contributors "<<nz<<"/"<<P
<< " maxcount "<<*std::max_element(counts.begin(),counts.end())
<< " rank0: count "<<counts[me]<<(owner?" owner":" non-owner")
<< std::endl;
}
if ( gatherThis == 1 )
{
nGatherV++;
void *send = owner ? (void *)B.ColumnWindow(colB+j0) : (void *)&dRecvBuf[0];
grid->AllGatherV(send, counts[me],
(void *)&dRecvBuf[0], counts, displs, sizeof(ComplexD));
}
else
{
////////////////////////////////////////////////////////////////
// C sequential broadcasts into the SAME rank-major staging
// buffer, so the repack below is shared with the AllGatherV arm.
// The root must already hold its own block: B.ColumnWindow is
// contiguous rows_r x nchunk, and so is its slot in dRecvBuf.
// Every rank issues all C broadcasts in the same order, so
// collective matching stays positional and safe.
////////////////////////////////////////////////////////////////
nBcast++;
if ( owner )
{
int64_t off_me = rowStart[me] - rowStart[rB0];
acceleratorCopyDeviceToDevice((void *)B.ColumnWindow(colB+j0),
(void *)&dRecvBuf[off_me*nchunk],
(uint64_t)B.rows*nchunk*sizeof(ComplexD));
}
for(int r=rB0;r<rB1;r++)
{
int64_t off_r = rowStart[r] - rowStart[rB0];
int64_t rows_r = rowStart[r+1] - rowStart[r];
if ( rows_r )
grid->Broadcast(r,(void *)&dRecvBuf[off_r*nchunk],
(uint64_t)rows_r*nchunk*sizeof(ComplexD));
}
}
tar += usecond();
tRepack -= usecond();
{
ComplexD *pan = &dPanel[0];
ComplexD *rcv = &dRecvBuf[0];
int64_t *ooff = &dOwnerOff[0];
int64_t *orows = &dOwnerRows[0];
int64_t kk = k, ncw = nchunk;
accelerator_for(idx, panelWords, 1, {
int64_t j = idx / kk;
int64_t p = idx - j*kk;
int64_t o = ooff[p];
int64_t rr= orows[p];
pan[idx] = rcv[(uint64_t)(o*ncw + (p-o) + j*rr)];
});
}
tRepack += usecond();
}
else
{
//////////////////////////////////////////////////////////////////
// Zero fill + deposit + GlobalSumVector. Exact because the zero
// fill leaves exactly one contributing rank per element, but it
// moves the payload twice and reduces over zeros.
//////////////////////////////////////////////////////////////////
tar += usecond();
tMemset -= usecond();
acceleratorMemSet(&dPanel[0], 0, panelBytesThis);
tMemset += usecond();
if ( owner )
{
int64_t brows = B.rows;
int64_t kk = k;
int64_t dof = myOff;
ComplexD *src = B.ColumnWindow(colB+j0);
ComplexD *dst = &dPanel[0];
tDeposit -= usecond();
accelerator_for(idx, (uint64_t)(brows*nchunk), 1, {
int64_t j = idx / brows;
int64_t i = idx - j*brows;
dst[(uint64_t)(dof + i + j*kk)] = src[(uint64_t)(i + j*brows)];
});
tDeposit += usecond();
}
tar = -usecond();
grid->GlobalSumVector(&dPanel[0], (int)panelWords);
tar += usecond();
}
tAllreduce += tar;
tARmin = std::min(tARmin, tar);
tARmax = std::max(tARmax, tar);
tARall.push_back(tar);
bytesAllreduce += panelBytesThis;
nAllreduce++;
if ( m > 0 )
{
// Full-k product into this column chunk of C: beta applies directly.
ptr[0] = A.ColumnWindow(colA);
acceleratorCopyToDevice(&ptr[0], &ap[0], sizeof(ComplexD*));
ptr[0] = &dPanel[0];
acceleratorCopyToDevice(&ptr[0], &bp[0], sizeof(ComplexD*));
ptr[0] = C.ColumnWindow(colC+j0);
acceleratorCopyToDevice(&ptr[0], &cp[0], sizeof(ComplexD*));
tGemm -= usecond();
BLAS.gemmBatched(GridBLAS_OP_N, GridBLAS_OP_N,
(int)m, (int)nchunk, (int)k,
alpha, ap, (int)A.ld,
bp, (int)k,
beta, cp, (int)C.ld);
BLAS.synchronise();
tGemm += usecond();
}
}
}
///////////////////////////////////////////////////////////////////////////
// Local Frobenius norm-squared of a full-height column window.
// NO comms; callers GlobalSum the result. Host staging, setup-scale.
///////////////////////////////////////////////////////////////////////////
double FrobNorm2Local(BlockRows &X, int64_t col0, int64_t w)
{
if ( X.rows == 0 ) return 0.0;
GRID_ASSERT( col0 + w <= X.cols );
uint64_t len = (uint64_t)X.rows*w;
std::vector<ComplexD> h(len);
acceleratorCopyFromDevice(X.ColumnWindow(col0), &h[0], len*sizeof(ComplexD));
// Member real()/imag(): portable across std::complex (CPU) and
// thrust::complex (HIP), where std::norm does not resolve.
double s = 0.0;
for(uint64_t i=0; i<len; i++)
{
double re = h[i].real();
double im = h[i].imag();
s += re*re + im*im;
}
return s;
}
///////////////////////////////////////////////////////////////////////////
// dst(:, dcol0 : dcol0+w) = - src(:, 0:w). Both operands have ld == rows
// so full-height windows are contiguous: flat elementwise device copy.
///////////////////////////////////////////////////////////////////////////
void NegateCopy(BlockRows &src, BlockRows &dst, int64_t dcol0, int64_t w)
{
GRID_ASSERT( src.rows == dst.rows );
GRID_ASSERT( w <= src.cols );
GRID_ASSERT( dcol0 + w <= dst.cols );
if ( src.rows == 0 ) return;
uint64_t len = (uint64_t)src.rows*w;
ComplexD *s = &src.data[0];
ComplexD *d = dst.ColumnWindow(dcol0);
accelerator_for(i, len, 1, {
d[i] = -s[i];
});
}
///////////////////////////////////////////////////////////////////////////
// Leaf inversion: local, in place on the contiguous diagonal window.
// No collectives.
///////////////////////////////////////////////////////////////////////////
void LeafInvert(int64_t col0, int64_t width, BlockRows &Arows)
{
GRID_TRACE("SchurLeaf");
GRID_ASSERT( width == Arows.rows );
GRID_ASSERT( col0 + width <= Arows.cols );
int64_t w = width;
uint64_t len = (uint64_t)w*w;
tLeaf -= usecond();
deviceVector<ComplexD*> bp(1);
std::vector<ComplexD*> ptr(1);
ptr[0] = Arows.ColumnWindow(col0);
acceleratorCopyToDevice(&ptr[0], &bp[0], sizeof(ComplexD*));
INV.inverseBatched(w, bp);
// Telemetry: max |element| of the leaf inverse
{
std::vector<ComplexD> h(len);
acceleratorCopyFromDevice(Arows.ColumnWindow(col0), &h[0], len*sizeof(ComplexD));
double mx = 0.0;
for(uint64_t i=0; i<len; i++)
{
double re = h[i].real();
double im = h[i].imag();
mx = std::max(mx, re*re + im*im);
}
telLeafMaxInv = std::max(telLeafMaxInv, std::sqrt(mx));
}
tLeaf += usecond();
}
///////////////////////////////////////////////////////////////////////////
// The recursion (plan 3.5 / 4B.3). Inverts the diagonal block of the
// rank-major matrix spanned by ranks [r0, r1), living in every member
// rank's column window [col0, col0+width) -- IN PLACE.
//
// SPMD: every rank calls with IDENTICAL (r0, r1, width) and its own
// local (col0, Arows); ranks outside [r0, r1) participate in the
// collectives only (dummy operands, zero contributions). The collective
// sequence -- 5 GatherGemm calls + 3 scalar GlobalSums per merge node --
// is identical on every rank by construction.
//
// I = [r0, mid) J = [mid, r1) widths WI, WJ
// 1. recurse I: A11 -> A11inv
// 2. B = A11inv.A12 (I rows)
// 3. C = A21.A11inv (J rows)
// 4. S = A22 - A21.B in place (J rows) [alpha=-1, beta=1]
// 5. recurse J: S -> Sinv
// 6. T = Sinv.C (J rows)
// 7. U = B.Sinv (I rows)
// 8. X11 = A11inv + U.C in place (I rows) [beta=1]
// 9. X12 = -U, X21 = -T local negates; X22 = Sinv already in place
///////////////////////////////////////////////////////////////////////////
void SchurNode(int r0, int r1, int64_t col0, int64_t width, BlockRows &Arows)
{
int span = r1 - r0;
GRID_ASSERT( span >= 1 );
GRID_ASSERT( width == rowStart[r1] - rowStart[r0] );
if ( span == 1 )
{
if ( ( me == r0 ) && ( myNrows > 0 ) )
{
LeafInvert(col0, width, Arows);
}
return;
}
int mid = ( r0 + r1 ) / 2;
int64_t WI = rowStart[mid] - rowStart[r0];
int64_t WJ = rowStart[r1] - rowStart[mid];
// Zero-width child ranges (all ranks of a half owning no rows) are a
// KNOWN LIMITATION: fail loudly rather than divide mysteriously.
GRID_ASSERT( WI > 0 );
GRID_ASSERT( WJ > 0 );
int inI = ( me >= r0 ) && ( me < mid );
int inJ = ( me >= mid ) && ( me < r1 );
ComplexD one ( 1.0,0.0);
ComplexD mone (-1.0,0.0);
ComplexD zero ( 0.0,0.0);
BlockRows dummy;
// 1. A11 -> A11inv
SchurNode(r0, mid, col0, WI, Arows);
// 2. B = A11inv . A12 (I rows; gather A12 from I owners)
BlockRows Bbuf;
if ( inI ) Bbuf.Resize(myNrows, WJ);
{
BlockRows &Aop = inI ? Arows : dummy;
BlockRows &Cop = inI ? Bbuf : dummy;
int64_t cA = inI ? col0 : 0;
GatherGemm(one, Aop, cA, WI,
r0, mid,
Arows, col0+WI, WJ,
zero, Cop, 0);
}
double nB = FrobNorm2Local(Bbuf, 0, inI ? WJ : 0);
grid->GlobalSumVector(&nB, 1);
telNormB.push_back(std::sqrt(nB));
// 3. C = A21 . A11inv (J rows; gather A11inv from I owners)
BlockRows Cbuf;
if ( inJ ) Cbuf.Resize(myNrows, WI);
{
BlockRows &Aop = inJ ? Arows : dummy;
BlockRows &Cop = inJ ? Cbuf : dummy;
int64_t cA = inJ ? col0 : 0;
GatherGemm(one, Aop, cA, WI,
r0, mid,
Arows, col0, WI,
zero, Cop, 0);
}
// 4. S = A22 - A21 . B in place on my A22 window (J rows)
double nA22 = FrobNorm2Local( inJ ? Arows : dummy, inJ ? col0+WI : 0, inJ ? WJ : 0 );
grid->GlobalSumVector(&nA22, 1);
{
BlockRows &Aop = inJ ? Arows : dummy;
BlockRows &Cop = inJ ? Arows : dummy;
int64_t cA = inJ ? col0 : 0;
int64_t cC = inJ ? col0+WI : 0;
GatherGemm(mone, Aop, cA, WI,
r0, mid,
Bbuf, 0, WJ,
one, Cop, cC);
}
double nS = FrobNorm2Local( inJ ? Arows : dummy, inJ ? col0+WI : 0, inJ ? WJ : 0 );
grid->GlobalSumVector(&nS, 1);
telSratio.push_back( std::sqrt(nS) / ( std::sqrt(nA22) + 1.0e-300 ) );
// 5. S -> Sinv
SchurNode(mid, r1, col0+WI, WJ, Arows);
// 6. T = Sinv . C (J rows; gather C from J owners)
BlockRows Tbuf;
if ( inJ ) Tbuf.Resize(myNrows, WI);
{
BlockRows &Aop = inJ ? Arows : dummy;
BlockRows &Cop = inJ ? Tbuf : dummy;
int64_t cA = inJ ? col0+WI : 0;
GatherGemm(one, Aop, cA, WJ,
mid, r1,
Cbuf, 0, WI,
zero, Cop, 0);
}
// 7. U = B . Sinv (I rows; gather Sinv from J owners)
BlockRows Ubuf;
if ( inI ) Ubuf.Resize(myNrows, WJ);
{
BlockRows &Aop = inI ? Bbuf : dummy;
BlockRows &Cop = inI ? Ubuf : dummy;
GatherGemm(one, Aop, 0, WJ,
mid, r1,
Arows, col0+WI, WJ,
zero, Cop, 0);
}
// 8. X11 = A11inv + U . C in place (I rows; gather C from J owners)
{
BlockRows &Aop = inI ? Ubuf : dummy;
BlockRows &Cop = inI ? Arows : dummy;
int64_t cC = inI ? col0 : 0;
GatherGemm(one, Aop, 0, WJ,
mid, r1,
Cbuf, 0, WI,
one, Cop, cC);
}
// 9. Off-diagonal signs, local
if ( inI ) NegateCopy(Ubuf, Arows, col0+WI, WJ);
if ( inJ ) NegateCopy(Tbuf, Arows, col0, WI);
}
///////////////////////////////////////////////////////////////////////////
// PUBLIC ENTRY. Arows: my rows of the rank-major N x N matrix (fp64).
// On exit Arows holds my rows of the inverse, still fp64; the caller
// owns the single terminal rounding into its fp32 apply storage.
///////////////////////////////////////////////////////////////////////////
void Invert(BlockRows &Arows)
{
GRID_ASSERT( Arows.rows == myNrows );
GRID_ASSERT( Arows.cols == N );
telNormB.resize(0);
telSratio.resize(0);
telLeafMaxInv = 0.0;
tMemset = 0.0;
tDeposit = 0.0;
tAllreduce = 0.0;
tBarrier = 0.0;
tRepack = 0.0;
tGemm = 0.0;
tLeaf = 0.0;
tARmin = 1.0e30;
tARmax = 0.0;
tARall.clear();
bytesAllreduce = 0;
nAllreduce = 0;
nGatherGemm = 0;
nGather = 0;
nGatherV = 0;
nBcast = 0;
SchurNode(0, P, 0, N, Arows);
RealD mx = telLeafMaxInv;
grid->GlobalMax(mx);
telLeafMaxInv = mx;
}
// All telemetry values are globally reduced or boss-local; safe to
// stream on every rank (Grid quiesces stdout to the boss unless
// --debug-stdout). NOTE: tAllreduce INCLUDES wait/imbalance -- a rank
// arriving early books its wait here; the min/max spread across ranks
// separates true wire time (~min) from skew (max-min).
void ReportTelemetry(void)
{
for(uint64_t i=0; i<telNormB.size(); i++)
{
std::cout << GridLogPerformance
<< "SchurNode " << i
<< " ||B||_F " << telNormB[i]
<< " ||S||/||A22|| " << telSratio[i]
<< std::endl;
}
std::cout << GridLogPerformance
<< "Schur leaves max|Ainv| " << telLeafMaxInv
<< std::endl;
RealD armax = tAllreduce;
RealD armin = -tAllreduce;
grid->GlobalMax(armax);
grid->GlobalMax(armin);
armin = -armin;
std::cout << GridLogMessage << "Schur phases (boss rank, seconds):"
<< " memset " << tMemset/1.0e6
<< " deposit " << tDeposit/1.0e6
<< " allreduce " << tAllreduce/1.0e6
<< " barrier " << tBarrier/1.0e6
<< " repack " << tRepack/1.0e6
<< " gemm " << tGemm/1.0e6
<< " leaf " << tLeaf/1.0e6
<< std::endl;
std::cout << GridLogMessage << "Schur comms:"
<< " [transports run: allreduce " << (nAllreduce - nGatherV - nBcast)
<< " allgatherv " << nGatherV << " bcast " << nBcast << "]"
<< ( barrierProbe ? " [barrier probe on]" : "" )
<< " GatherGemm calls " << nGatherGemm
<< " panel allreduces " << nAllreduce
<< " allreduce GB " << bytesAllreduce/1024./1024./1024.
<< " allreduce s min/max over ranks " << armin/1.0e6
<< " / " << armax/1.0e6
<< std::endl;
std::cout << GridLogMessage << "Schur comms per-call (boss):"
<< " min " << (nAllreduce ? tARmin/1.0e3 : 0.0) << " ms"
<< " avg " << (nAllreduce ? tAllreduce/nAllreduce/1.0e3 : 0.0) << " ms"
<< " max " << tARmax/1.0e3 << " ms"
<< " effective " << (tAllreduce>0 ? bytesAllreduce/tAllreduce*1.0e6/1.0e9 : 0.0)
<< " GB/s"
<< std::endl;
//////////////////////////////////////////////////////////////////////
// Distribution, not just min/avg/max. A barrier that merely REBOOKS
// skew shifts the median; a barrier that suppresses pathological
// collectives shortens the tail. p50 vs p99 separates the two from a
// single run, which min/avg/max cannot.
//////////////////////////////////////////////////////////////////////
if ( tARall.size() ) {
std::vector<double> v = tARall;
std::sort(v.begin(),v.end());
size_t n = v.size();
auto pc = [&](double f){ size_t i=(size_t)(f*(n-1)); return v[i]/1.0e3; };
double med = pc(0.50);
// how much time is spent in calls that are gross outliers
double tail=0.0; size_t ntail=0;
for(size_t i=0;i<n;i++) if ( v[i] > 4.0*med*1.0e3 ) { tail+=v[i]; ntail++; }
std::cout << GridLogMessage << "Schur comms distribution (boss):"
<< " p50 " << med
<< " p90 " << pc(0.90)
<< " p99 " << pc(0.99)
<< " ms calls > 4x median: " << ntail << "/" << n
<< " carrying " << tail/1.0e6 << " s"
<< std::endl;
}
}
};
NAMESPACE_END(Grid);
-269
View File
@@ -1,269 +0,0 @@
/*************************************************************************************
Grid physics library, www.github.com/paboyle/Grid
Source file: ./Grid/algorithms/multigrid/Smoothers.h
Copyright (C) 2026
Author: Peter Boyle <pboyle@bnl.gov>
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 */
#pragma once
#include <Grid/algorithms/iterative/GCRCoefficients.h>
NAMESPACE_BEGIN(Grid);
//////////////////////////////////////////////////////////////////////////////
// Multigrid smoothers as LinearFunction<Field> objects.
//
// Gathered here from ~15 copies in tests/solver and tests/debug (HDCR /
// HDCG era), plus the fixed-polynomial smoothers of 2026:
//
// ChebyshevSmoother Chebyshev approx to 1/x on [lo,hi], applied
// through HermOp (the original HDCG smoother on
// a Hermitian shifted operator).
// ChebyshevNonHermitianSmoother same polynomial applied through Op(), for
// a non-Hermitian (near-normal, real-spectrum)
// smoother operator such as shifted PVdagM.
// ChebyshevInverter one Chebyshev-corrected step with residual print.
// MirsSmoother shifted-MdagM CG, HDCG arXiv:1402.2585.
// GCRReplaySmoother replays a GCR's recorded step lengths a_k and
// orthogonalisation coefficients b_kj with NO
// inner products: one matvec per step, zero
// reductions. The "PreconditionerMirsPoly"
// idea of 1402.2585 p.13 applied to GCR.
//
// Recording: PrecGeneralisedConjugateResidualNonHermitian::SetCoefficientRecorder
// accumulates per-step means over calls into a GCRCoefficients; construct a
// GCRReplaySmoother from it.
//////////////////////////////////////////////////////////////////////////////
inline RealD InverseApproximation(RealD x){ return 1.0/x; }
//////////////////////////////////////////////////////////////////////////////
// HermOp-based Chebyshev smoother. The second template parameter and the
// 5-argument constructor exist only so the historical call sites
// ChebyshevSmoother<LatticeFermion,DomainWallFermionD> S(lo,hi,ord,HermOp,Ddwf);
// compile unchanged; the Matrix argument was never used.
//////////////////////////////////////////////////////////////////////////////
template<class Field,class Matrix=void> class ChebyshevSmoother : public LinearFunction<Field>
{
public:
using LinearFunction<Field>::operator();
typedef LinearOperatorBase<Field> FineOperator;
FineOperator & _SmootherOperator;
Chebyshev<Field> Cheby;
ChebyshevSmoother(RealD _lo,RealD _hi,int _ord, FineOperator &SmootherOperator) :
_SmootherOperator(SmootherOperator),
Cheby(_lo,_hi,_ord,InverseApproximation)
{
std::cout << GridLogMessage<<" Chebyshev smoother order "<<_ord<<" ["<<_lo<<","<<_hi<<"]"<<std::endl;
};
template<class M>
ChebyshevSmoother(RealD _lo,RealD _hi,int _ord, FineOperator &SmootherOperator, M &) :
ChebyshevSmoother(_lo,_hi,_ord,SmootherOperator) {};
void operator() (const Field &in, Field &out)
{
Cheby(_SmootherOperator,in,out);
}
};
//////////////////////////////////////////////////////////////////////////////
// Op()-based Chebyshev smoother: x = S(A) r with S the Chebyshev fit to 1/x
// on [lo,hi]. Same three-term recurrence as Chebyshev<Field>::operator()
// but through Op, for the non-Hermitian smoother operators of the PVdagM
// multigrid (real coefficients / near-normal, as the recorded GCR
// coefficients show). `order` matvecs, no reductions.
//////////////////////////////////////////////////////////////////////////////
template<class Field> class ChebyshevNonHermitianSmoother : public LinearFunction<Field>
{
public:
using LinearFunction<Field>::operator();
LinearOperatorBase<Field> &Linop;
RealD lo, hi; int order;
std::vector<RealD> Coeffs;
ChebyshevNonHermitianSmoother(RealD _lo,RealD _hi,int _order,LinearOperatorBase<Field> &Op)
: Linop(Op), lo(_lo), hi(_hi), order(_order)
{
GRID_ASSERT(order>=2);
Coeffs.resize(order);
for(int j=0;j<order;j++){
RealD s=0;
for(int k=0;k<order;k++){
RealD y=std::cos(M_PI*(k+0.5)/order);
RealD x=0.5*(y*(hi-lo)+(hi+lo));
s=s+InverseApproximation(x)*std::cos( j*M_PI*(k+0.5)/order );
}
Coeffs[j] = s * 2.0/order;
}
std::cout << GridLogMessage<<" ChebyshevNonHermitian smoother order "<<order<<" ["<<lo<<","<<hi<<"]"<<std::endl;
}
void operator() (const Field &in, Field &out)
{
GRID_TRACE("ChebyshevSmoother");
GridBase *grid=in.Grid();
Field T0(grid); T0 = in;
Field T1(grid), T2(grid), y(grid);
Field *Tnm=&T0, *Tn=&T1, *Tnp=&T2;
RealD xscale = 2.0/(hi-lo);
RealD mscale = -(hi+lo)/(hi-lo);
Linop.Op(T0,y);
axpby(T1,xscale,mscale,y,in);
axpby(out,0.5*Coeffs[0],Coeffs[1],T0,T1);
for(int n=2;n<order;n++){
Linop.Op(*Tn,y);
axpby(y,xscale,mscale,y,(*Tn));
axpby(*Tnp,2.0,-1.0,y,(*Tnm));
if ( Coeffs[n] != 0.0 ) axpy(out,Coeffs[n],*Tnp,out);
Field *swizzle=Tnm; Tnm=Tn; Tn=Tnp; Tnp=swizzle;
}
}
};
template<class Field> class ChebyshevInverter : public LinearFunction<Field>
{
public:
using LinearFunction<Field>::operator();
typedef LinearOperatorBase<Field> FineOperator;
FineOperator & _Operator;
Chebyshev<Field> Cheby;
ChebyshevInverter(RealD _lo,RealD _hi,int _ord, FineOperator &Operator) :
_Operator(Operator),
Cheby(_lo,_hi,_ord,InverseApproximation)
{
std::cout << GridLogMessage<<" Chebyshev Inverter order "<<_ord<<" ["<<_lo<<","<<_hi<<"]"<<std::endl;
};
void operator() (const Field &in, Field &out)
{
Field r(in.Grid());
Field AinvR(in.Grid());
_Operator.HermOp(out,r);
r = in - r; // b - A x
Cheby(_Operator,r,AinvR); // A^{-1} ( b - A x ) ~ A^{-1} b - x
out = out + AinvR;
_Operator.HermOp(out,r);
r = in - r; // b - A x
RealD rr = norm2(r);
RealD ss = norm2(in);
std::cout << GridLogMessage << "ChebshevInverse resid " <<::sqrt(rr/ss)<<std::endl;
}
};
//////////////////////////////////////////////////////////////////////////////
// MIRS: CG on the infra-red shifted MdagM (HDCG, arXiv:1402.2585).
//////////////////////////////////////////////////////////////////////////////
template<class Field,class Matrix> class MirsSmoother : public LinearFunction<Field>
{
public:
using LinearFunction<Field>::operator();
typedef LinearOperatorBase<Field> FineOperator;
Matrix & SmootherMatrix;
FineOperator & SmootherOperator;
RealD tol;
RealD shift;
int maxit;
MirsSmoother(RealD _shift,RealD _tol,int _maxit,FineOperator &_SmootherOperator,Matrix &_SmootherMatrix) :
shift(_shift),tol(_tol),maxit(_maxit),
SmootherOperator(_SmootherOperator),
SmootherMatrix(_SmootherMatrix)
{};
void operator() (const Field &in, Field &out)
{
ZeroGuesser<Field> Guess;
ConjugateGradient<Field> CG(tol,maxit,false);
Field src(in.Grid());
ShiftedMdagMLinearOperator<SparseMatrixBase<Field>,Field> MdagMOp(SmootherMatrix,shift);
SmootherOperator.AdjOp(in,src);
Guess(src,out);
CG(MdagMOp,src,out);
}
};
//////////////////////////////////////////////////////////////////////////////
// Replay of a recorded GCR with a trivial preconditioner:
// p_0 = r_0 ; x_{k+1} = x_k + a_k p_k ; r_{k+1} = r_k - a_k A p_k ;
// p_{k+1} = r_{k+1} + sum_{j<northog(k)} b_kj p_{k-j}
// One matvec per step, no reductions, history of mmax p vectors held
// persistently (allocated on first use, per grid).
//////////////////////////////////////////////////////////////////////////////
template<class Field> class GCRReplaySmoother : public LinearFunction<Field>
{
public:
using LinearFunction<Field>::operator();
LinearOperatorBase<Field> &Linop;
int mmax, nstep;
std::vector<ComplexD> a;
std::vector<std::vector<ComplexD> > b;
GridBase *hist_grid = nullptr;
std::vector<Field> p;
GCRReplaySmoother(LinearOperatorBase<Field> &Op, const GCRCoefficients &c) : Linop(Op)
{
mmax = c.mmax; GRID_ASSERT(mmax>=1);
nstep = c.Steps(); GRID_ASSERT(nstep>=1);
a.resize(nstep); b.resize(nstep);
for(int k=0;k<nstep;k++){
a[k] = c.A(k);
b[k].resize(c.NB(k));
for(int j=0;j<c.NB(k);j++) b[k][j] = c.B(k,j);
}
std::cout << GridLogMessage << " GCRReplaySmoother: " << nstep << " steps, mmax " << mmax
<< ", from " << c.Calls() << " recorded calls" << std::endl;
}
void operator() (const Field &src, Field &psi)
{
GRID_TRACE("GCRReplaySmoother");
GridBase *grid = src.Grid();
if ( hist_grid != grid ) {
p.clear(); p.reserve(mmax);
for(int i=0;i<mmax;i++) p.emplace_back(grid);
hist_grid = grid;
}
Field r(grid), q(grid);
r = src;
psi = Zero();
p[0] = r;
for(int k=0;k<nstep;k++){
int kp=k+1, peri_k=k%mmax, peri_kp=kp%mmax;
Linop.Op(p[peri_k],q); // q_k = A p_k
axpy(psi, a[k], p[peri_k], psi);
if ( k==nstep-1 ) break;
axpy(r, -a[k], q, r);
p[peri_kp] = r;
for(int j=0;j<(int)b[k].size();j++){
int peri_back=(k-j)%mmax;
axpy(p[peri_kp], b[k][j], p[peri_back], p[peri_kp]);
}
}
}
};
//////////////////////////////////////////////////////////////////////////////
// A LinearFunction that forwards to a replaceable target: lets a V-cycle be
// built once around a smoother slot whose implementation is swapped at run
// time (record with the adaptive GCR, then replay the polynomial).
//////////////////////////////////////////////////////////////////////////////
template<class Field> class SwitchableSmoother : public LinearFunction<Field>
{
public:
using LinearFunction<Field>::operator();
LinearFunction<Field> *current;
std::string label;
SwitchableSmoother(LinearFunction<Field> &initial, const std::string &l="initial") : current(&initial), label(l) {}
void Set(LinearFunction<Field> &f, const std::string &l)
{
current = &f; label = l;
std::cout << GridLogMessage << " SwitchableSmoother -> " << l << std::endl;
}
void operator() (const Field &in, Field &out) { (*current)(in,out); }
};
NAMESPACE_END(Grid);
+3 -3
View File
@@ -57,7 +57,7 @@ public:
if ( (_Tp*)ptr == (_Tp *) NULL ) { if ( (_Tp*)ptr == (_Tp *) NULL ) {
printf("Grid CPU Allocator got NULL for %lu bytes\n",(unsigned long) bytes ); printf("Grid CPU Allocator got NULL for %lu bytes\n",(unsigned long) bytes );
} }
GRID_ASSERT( ( (_Tp*)ptr != (_Tp *)NULL ) ); assert( ( (_Tp*)ptr != (_Tp *)NULL ) );
return ptr; return ptr;
} }
@@ -106,7 +106,7 @@ public:
if ( (_Tp*)ptr == (_Tp *) NULL ) { if ( (_Tp*)ptr == (_Tp *) NULL ) {
printf("Grid Shared Allocator got NULL for %lu bytes\n",(unsigned long) bytes ); printf("Grid Shared Allocator got NULL for %lu bytes\n",(unsigned long) bytes );
} }
GRID_ASSERT( ( (_Tp*)ptr != (_Tp *)NULL ) ); assert( ( (_Tp*)ptr != (_Tp *)NULL ) );
return ptr; return ptr;
} }
@@ -154,7 +154,7 @@ public:
if ( (_Tp*)ptr == (_Tp *) NULL ) { if ( (_Tp*)ptr == (_Tp *) NULL ) {
printf("Grid Device Allocator got NULL for %lu bytes\n",(unsigned long) bytes ); printf("Grid Device Allocator got NULL for %lu bytes\n",(unsigned long) bytes );
} }
GRID_ASSERT( ( (_Tp*)ptr != (_Tp *)NULL ) ); assert( ( (_Tp*)ptr != (_Tp *)NULL ) );
return ptr; return ptr;
} }
+8 -6
View File
@@ -63,10 +63,12 @@ void MemoryManager::PrintBytes(void)
std::cout << " MemoryManager : "<<(total_device>>20)<<" accelerator Mbytes "<<std::endl; std::cout << " MemoryManager : "<<(total_device>>20)<<" accelerator Mbytes "<<std::endl;
std::cout << " MemoryManager : "<<(total_host>>20) <<" cpu Mbytes "<<std::endl; std::cout << " MemoryManager : "<<(total_host>>20) <<" cpu Mbytes "<<std::endl;
uint64_t cacheBytes; uint64_t cacheBytes;
cacheBytes = HostCacheBytes(); cacheBytes = CacheBytes[Cpu];
std::cout << " MemoryManager : "<<(cacheBytes>>20) <<" cpu alloc cache Mbytes "<<std::endl; std::cout << " MemoryManager : "<<(cacheBytes>>20) <<" cpu cache Mbytes "<<std::endl;
cacheBytes = DeviceCacheBytes(); cacheBytes = CacheBytes[Acc];
std::cout << " MemoryManager : "<<(cacheBytes>>20) <<" acc alloc cache Mbytes "<<std::endl; std::cout << " MemoryManager : "<<(cacheBytes>>20) <<" acc cache Mbytes "<<std::endl;
cacheBytes = CacheBytes[Shared];
std::cout << " MemoryManager : "<<(cacheBytes>>20) <<" shared cache Mbytes "<<std::endl;
#ifdef GRID_CUDA #ifdef GRID_CUDA
cuda_mem(); cuda_mem();
@@ -290,7 +292,7 @@ void *MemoryManager::Insert(void *ptr,size_t bytes,int type)
void *MemoryManager::Insert(void *ptr,size_t bytes,AllocationCacheEntry *entries,int ncache,int &victim, uint64_t &cacheBytes) void *MemoryManager::Insert(void *ptr,size_t bytes,AllocationCacheEntry *entries,int ncache,int &victim, uint64_t &cacheBytes)
{ {
#ifdef GRID_OMP #ifdef GRID_OMP
GRID_ASSERT(omp_in_parallel()==0); assert(omp_in_parallel()==0);
#endif #endif
if (ncache == 0) return ptr; if (ncache == 0) return ptr;
@@ -343,7 +345,7 @@ void *MemoryManager::Lookup(size_t bytes,int type)
void *MemoryManager::Lookup(size_t bytes,AllocationCacheEntry *entries,int ncache,uint64_t & cacheBytes) void *MemoryManager::Lookup(size_t bytes,AllocationCacheEntry *entries,int ncache,uint64_t & cacheBytes)
{ {
#ifdef GRID_OMP #ifdef GRID_OMP
GRID_ASSERT(omp_in_parallel()==0); assert(omp_in_parallel()==0);
#endif #endif
for(int e=0;e<ncache;e++){ for(int e=0;e<ncache;e++){
if ( entries[e].valid && ( entries[e].bytes == bytes ) ) { if ( entries[e].valid && ( entries[e].bytes == bytes ) ) {
+1 -2
View File
@@ -113,7 +113,7 @@ private:
static void *Insert(void *ptr,size_t bytes,AllocationCacheEntry *entries,int ncache,int &victim,uint64_t &cbytes) ; static void *Insert(void *ptr,size_t bytes,AllocationCacheEntry *entries,int ncache,int &victim,uint64_t &cbytes) ;
static void *Lookup(size_t bytes,AllocationCacheEntry *entries,int ncache,uint64_t &cbytes) ; static void *Lookup(size_t bytes,AllocationCacheEntry *entries,int ncache,uint64_t &cbytes) ;
public: public:
static void PrintBytes(void); static void PrintBytes(void);
static void Audit(std::string s); static void Audit(std::string s);
static void Init(void); static void Init(void);
@@ -215,7 +215,6 @@ public:
static void NotifyDeletion(void * CpuPtr); static void NotifyDeletion(void * CpuPtr);
static void Print(void); static void Print(void);
static void PrintAll(void); static void PrintAll(void);
static void EvictAll(void);
static void PrintState( void* CpuPtr); static void PrintState( void* CpuPtr);
static int isOpen (void* CpuPtr); static int isOpen (void* CpuPtr);
static void ViewClose(void* CpuPtr,ViewMode mode); static void ViewClose(void* CpuPtr,ViewMode mode);
+64 -111
View File
@@ -50,12 +50,12 @@ int MemoryManager::EntryPresent(uint64_t CpuPtr)
{ {
if(AccViewTable.empty()) return 0; if(AccViewTable.empty()) return 0;
auto count = AccViewTable.count(CpuPtr); GRID_ASSERT((count==0)||(count==1)); auto count = AccViewTable.count(CpuPtr); assert((count==0)||(count==1));
return count; return count;
} }
void MemoryManager::EntryCreate(uint64_t CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint) void MemoryManager::EntryCreate(uint64_t CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint)
{ {
GRID_ASSERT(!EntryPresent(CpuPtr)); assert(!EntryPresent(CpuPtr));
AcceleratorViewEntry AccCache; AcceleratorViewEntry AccCache;
AccCache.CpuPtr = CpuPtr; AccCache.CpuPtr = CpuPtr;
AccCache.AccPtr = (uint64_t)NULL; AccCache.AccPtr = (uint64_t)NULL;
@@ -69,9 +69,9 @@ void MemoryManager::EntryCreate(uint64_t CpuPtr,size_t bytes,ViewMode mode,View
} }
MemoryManager::AccViewTableIterator MemoryManager::EntryLookup(uint64_t CpuPtr) MemoryManager::AccViewTableIterator MemoryManager::EntryLookup(uint64_t CpuPtr)
{ {
GRID_ASSERT(EntryPresent(CpuPtr)); assert(EntryPresent(CpuPtr));
auto AccCacheIterator = AccViewTable.find(CpuPtr); auto AccCacheIterator = AccViewTable.find(CpuPtr);
GRID_ASSERT(AccCacheIterator!=AccViewTable.end()); assert(AccCacheIterator!=AccViewTable.end());
return AccCacheIterator; return AccCacheIterator;
} }
void MemoryManager::EntryErase(uint64_t CpuPtr) void MemoryManager::EntryErase(uint64_t CpuPtr)
@@ -79,28 +79,9 @@ void MemoryManager::EntryErase(uint64_t CpuPtr)
auto AccCache = EntryLookup(CpuPtr); auto AccCache = EntryLookup(CpuPtr);
AccViewTable.erase(CpuPtr); AccViewTable.erase(CpuPtr);
} }
/////////////////////////////////////////////////////////////////////////////////
// LRU membership invariant:
//
// LRU_valid == 1 <=> AccPtr != NULL && accLock == 0 && cpuLock == 0
//
// i.e. the LRU queue contains exactly the device-resident, completely unlocked
// entries -- the evictable set. Membership is maintained EAGERLY at the lock
// 0<->1 edges, O(1) via the stored LRU_entry iterator:
//
// AcceleratorViewOpen lock 0->1 : LRUremove (gated on LRU_valid)
// AcceleratorViewClose accLock->0: LRUinsert (AccPtr necessarily exists)
// CpuViewOpen lock 0->1 : LRUremove (gated on LRU_valid)
// CpuViewClose cpuLock->0: LRUinsert (iff AccPtr exists)
// Evict/AccDiscard : LRUremove (frees the device copy)
//
// Consequences: victims taken from LRU.back() are evictable by construction;
// Evict() on a locked entry is an invariant violation (asserted), and the
// eviction loops (EvictVictims/EvictAll) cannot spin.
/////////////////////////////////////////////////////////////////////////////////
void MemoryManager::LRUinsert(AcceleratorViewEntry &AccCache) void MemoryManager::LRUinsert(AcceleratorViewEntry &AccCache)
{ {
GRID_ASSERT(AccCache.LRU_valid==0); assert(AccCache.LRU_valid==0);
if (AccCache.transient) { if (AccCache.transient) {
LRU.push_back(AccCache.CpuPtr); LRU.push_back(AccCache.CpuPtr);
AccCache.LRU_entry = --LRU.end(); AccCache.LRU_entry = --LRU.end();
@@ -113,7 +94,7 @@ void MemoryManager::LRUinsert(AcceleratorViewEntry &AccCache)
} }
void MemoryManager::LRUremove(AcceleratorViewEntry &AccCache) void MemoryManager::LRUremove(AcceleratorViewEntry &AccCache)
{ {
GRID_ASSERT(AccCache.LRU_valid==1); assert(AccCache.LRU_valid==1);
LRU.erase(AccCache.LRU_entry); LRU.erase(AccCache.LRU_entry);
AccCache.LRU_valid = 0; AccCache.LRU_valid = 0;
DeviceLRUBytes-=AccCache.bytes; DeviceLRUBytes-=AccCache.bytes;
@@ -127,12 +108,12 @@ void MemoryManager::AccDiscard(AcceleratorViewEntry &AccCache)
// Remove from Accelerator, remove entry, without flush // Remove from Accelerator, remove entry, without flush
// Cannot be locked. If allocated Must be in LRU pool. // Cannot be locked. If allocated Must be in LRU pool.
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
GRID_ASSERT(AccCache.state!=Empty); assert(AccCache.state!=Empty);
dprintf("MemoryManager: Discard(%lx) %lx",(uint64_t)AccCache.CpuPtr,(uint64_t)AccCache.AccPtr); dprintf("MemoryManager: Discard(%lx) %lx",(uint64_t)AccCache.CpuPtr,(uint64_t)AccCache.AccPtr);
GRID_ASSERT(AccCache.accLock==0); assert(AccCache.accLock==0);
GRID_ASSERT(AccCache.cpuLock==0); assert(AccCache.cpuLock==0);
GRID_ASSERT(AccCache.CpuPtr!=(uint64_t)NULL); assert(AccCache.CpuPtr!=(uint64_t)NULL);
if(AccCache.AccPtr) { if(AccCache.AccPtr) {
AcceleratorFree((void *)AccCache.AccPtr,AccCache.bytes); AcceleratorFree((void *)AccCache.AccPtr,AccCache.bytes);
DeviceDestroy++; DeviceDestroy++;
@@ -149,21 +130,21 @@ void MemoryManager::Evict(AcceleratorViewEntry &AccCache)
{ {
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Make CPU consistent, remove from Accelerator, remove from LRU, LEAVE CPU only entry // Make CPU consistent, remove from Accelerator, remove from LRU, LEAVE CPU only entry
// Cannot be locked. If allocated must be in LRU pool. // Cannot be acclocked. If allocated must be in LRU pool.
// //
// (Historical: a Nov 2022 incident (two CpuPtrs; eviction called from // Nov 2022... Felix issue: Allocating two CpuPtrs, can have an entry in LRU-q with CPUlock.
// CpuViewOpen -- since excised) could present a cpuLocked entry here, and // and require to evict the AccPtr copy. Eviction was a mistake in CpuViewOpen
// silent-return guards were added. The LRU membership invariant (see // but there is a weakness where CpuLock entries are attempted for erase
// LRUinsert) now excludes ALL locked entries from the queue eagerly at the // Take these OUT LRU queue when CPU locked?
// lock edges, so a locked victim is an invariant violation: asserted.) // Cannot take out the table as cpuLock data is important.
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
GRID_ASSERT(AccCache.state!=Empty); assert(AccCache.state!=Empty);
mprintf("MemoryManager: Evict CpuPtr %lx AccPtr %lx cpuLock %ld accLock %ld", mprintf("MemoryManager: Evict CpuPtr %lx AccPtr %lx cpuLock %ld accLock %ld",
(uint64_t)AccCache.CpuPtr,(uint64_t)AccCache.AccPtr, (uint64_t)AccCache.CpuPtr,(uint64_t)AccCache.AccPtr,
(uint64_t)AccCache.cpuLock,(uint64_t)AccCache.accLock); (uint64_t)AccCache.cpuLock,(uint64_t)AccCache.accLock);
GRID_ASSERT(AccCache.accLock==0); if (AccCache.accLock!=0) return;
GRID_ASSERT(AccCache.cpuLock==0); if (AccCache.cpuLock!=0) return;
if(AccCache.state==AccDirty) { if(AccCache.state==AccDirty) {
Flush(AccCache); Flush(AccCache);
} }
@@ -181,11 +162,11 @@ void MemoryManager::Evict(AcceleratorViewEntry &AccCache)
} }
void MemoryManager::Flush(AcceleratorViewEntry &AccCache) void MemoryManager::Flush(AcceleratorViewEntry &AccCache)
{ {
GRID_ASSERT(AccCache.state==AccDirty); assert(AccCache.state==AccDirty);
GRID_ASSERT(AccCache.cpuLock==0); assert(AccCache.cpuLock==0);
GRID_ASSERT(AccCache.accLock==0); assert(AccCache.accLock==0);
GRID_ASSERT(AccCache.AccPtr!=(uint64_t)NULL); assert(AccCache.AccPtr!=(uint64_t)NULL);
GRID_ASSERT(AccCache.CpuPtr!=(uint64_t)NULL); assert(AccCache.CpuPtr!=(uint64_t)NULL);
acceleratorCopyFromDevice((void *)AccCache.AccPtr,(void *)AccCache.CpuPtr,AccCache.bytes); acceleratorCopyFromDevice((void *)AccCache.AccPtr,(void *)AccCache.CpuPtr,AccCache.bytes);
mprintf("MemoryManager: acceleratorCopyFromDevice Flush size %ld AccPtr %lx -> CpuPtr %lx",(uint64_t)AccCache.bytes,(uint64_t)AccCache.AccPtr,(uint64_t)AccCache.CpuPtr); fflush(stdout); mprintf("MemoryManager: acceleratorCopyFromDevice Flush size %ld AccPtr %lx -> CpuPtr %lx",(uint64_t)AccCache.bytes,(uint64_t)AccCache.AccPtr,(uint64_t)AccCache.CpuPtr); fflush(stdout);
DeviceToHostBytes+=AccCache.bytes; DeviceToHostBytes+=AccCache.bytes;
@@ -194,10 +175,10 @@ void MemoryManager::Flush(AcceleratorViewEntry &AccCache)
} }
void MemoryManager::Clone(AcceleratorViewEntry &AccCache) void MemoryManager::Clone(AcceleratorViewEntry &AccCache)
{ {
GRID_ASSERT(AccCache.state==CpuDirty); assert(AccCache.state==CpuDirty);
GRID_ASSERT(AccCache.cpuLock==0); assert(AccCache.cpuLock==0);
GRID_ASSERT(AccCache.accLock==0); assert(AccCache.accLock==0);
GRID_ASSERT(AccCache.CpuPtr!=(uint64_t)NULL); assert(AccCache.CpuPtr!=(uint64_t)NULL);
if(AccCache.AccPtr==(uint64_t)NULL){ if(AccCache.AccPtr==(uint64_t)NULL){
AccCache.AccPtr=(uint64_t)AcceleratorAllocate(AccCache.bytes); AccCache.AccPtr=(uint64_t)AcceleratorAllocate(AccCache.bytes);
DeviceBytes+=AccCache.bytes; DeviceBytes+=AccCache.bytes;
@@ -213,10 +194,10 @@ void MemoryManager::Clone(AcceleratorViewEntry &AccCache)
void MemoryManager::CpuDiscard(AcceleratorViewEntry &AccCache) void MemoryManager::CpuDiscard(AcceleratorViewEntry &AccCache)
{ {
GRID_ASSERT(AccCache.state!=Empty); assert(AccCache.state!=Empty);
GRID_ASSERT(AccCache.cpuLock==0); assert(AccCache.cpuLock==0);
GRID_ASSERT(AccCache.accLock==0); assert(AccCache.accLock==0);
GRID_ASSERT(AccCache.CpuPtr!=(uint64_t)NULL); assert(AccCache.CpuPtr!=(uint64_t)NULL);
if(AccCache.AccPtr==(uint64_t)NULL){ if(AccCache.AccPtr==(uint64_t)NULL){
AccCache.AccPtr=(uint64_t)AcceleratorAllocate(AccCache.bytes); AccCache.AccPtr=(uint64_t)AcceleratorAllocate(AccCache.bytes);
DeviceBytes+=AccCache.bytes; DeviceBytes+=AccCache.bytes;
@@ -235,7 +216,7 @@ void MemoryManager::ViewClose(void* Ptr,ViewMode mode)
} else if( (mode==CpuRead)||(mode==CpuWrite)){ } else if( (mode==CpuRead)||(mode==CpuWrite)){
CpuViewClose((uint64_t)Ptr); CpuViewClose((uint64_t)Ptr);
} else { } else {
GRID_ASSERT(0); assert(0);
} }
} }
void *MemoryManager::ViewOpen(void* _CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint) void *MemoryManager::ViewOpen(void* _CpuPtr,size_t bytes,ViewMode mode,ViewAdvise hint)
@@ -247,7 +228,7 @@ void *MemoryManager::ViewOpen(void* _CpuPtr,size_t bytes,ViewMode mode,ViewAdvis
} else if( (mode==CpuRead)||(mode==CpuWrite)){ } else if( (mode==CpuRead)||(mode==CpuWrite)){
return (void *)CpuViewOpen(CpuPtr,bytes,mode,hint); return (void *)CpuViewOpen(CpuPtr,bytes,mode,hint);
} else { } else {
GRID_ASSERT(0); assert(0);
return NULL; return NULL;
} }
} }
@@ -256,23 +237,10 @@ void MemoryManager::EvictVictims(uint64_t bytes)
if(bytes>=DeviceMaxBytes) { if(bytes>=DeviceMaxBytes) {
printf("EvictVictims bytes %ld DeviceMaxBytes %ld\n",bytes,DeviceMaxBytes); printf("EvictVictims bytes %ld DeviceMaxBytes %ld\n",bytes,DeviceMaxBytes);
} }
GRID_ASSERT(bytes<DeviceMaxBytes); assert(bytes<DeviceMaxBytes);
while(bytes+DeviceLRUBytes > DeviceMaxBytes){ while(bytes+DeviceLRUBytes > DeviceMaxBytes){
if ( DeviceLRUBytes > 0){ if ( DeviceLRUBytes > 0){
GRID_ASSERT(LRU.size()>0); assert(LRU.size()>0);
uint64_t victim = LRU.back(); // From the LRU
auto AccCacheIterator = EntryLookup(victim);
auto & AccCache = AccCacheIterator->second;
Evict(AccCache);
} else {
return;
}
}
}
void MemoryManager::EvictAll(void)
{
while(LRU.size()>0){
if ( DeviceLRUBytes > 0){
uint64_t victim = LRU.back(); // From the LRU uint64_t victim = LRU.back(); // From the LRU
auto AccCacheIterator = EntryLookup(victim); auto AccCacheIterator = EntryLookup(victim);
auto & AccCache = AccCacheIterator->second; auto & AccCache = AccCacheIterator->second;
@@ -296,9 +264,9 @@ uint64_t MemoryManager::AcceleratorViewOpen(uint64_t CpuPtr,size_t bytes,ViewMod
if (!AccCache.AccPtr) { if (!AccCache.AccPtr) {
EvictVictims(bytes); EvictVictims(bytes);
} }
GRID_ASSERT((mode==AcceleratorRead)||(mode==AcceleratorWrite)||(mode==AcceleratorWriteDiscard)); assert((mode==AcceleratorRead)||(mode==AcceleratorWrite)||(mode==AcceleratorWriteDiscard));
GRID_ASSERT(AccCache.cpuLock==0); // Programming error assert(AccCache.cpuLock==0); // Programming error
if(AccCache.state!=Empty) { if(AccCache.state!=Empty) {
dprintf("ViewOpen found entry %lx %lx : sizes %ld %ld accLock %ld", dprintf("ViewOpen found entry %lx %lx : sizes %ld %ld accLock %ld",
@@ -307,8 +275,8 @@ uint64_t MemoryManager::AcceleratorViewOpen(uint64_t CpuPtr,size_t bytes,ViewMod
(uint64_t)AccCache.bytes, (uint64_t)AccCache.bytes,
(uint64_t)bytes, (uint64_t)bytes,
(uint64_t)AccCache.accLock); (uint64_t)AccCache.accLock);
GRID_ASSERT(AccCache.CpuPtr == CpuPtr); assert(AccCache.CpuPtr == CpuPtr);
GRID_ASSERT(AccCache.bytes ==bytes); assert(AccCache.bytes ==bytes);
} }
/* /*
* State transitions and actions * State transitions and actions
@@ -325,7 +293,7 @@ uint64_t MemoryManager::AcceleratorViewOpen(uint64_t CpuPtr,size_t bytes,ViewMod
* AccWrite AccDirty AccDirty - - * AccWrite AccDirty AccDirty - -
*/ */
if(AccCache.state==Empty) { if(AccCache.state==Empty) {
GRID_ASSERT(AccCache.LRU_valid==0); assert(AccCache.LRU_valid==0);
AccCache.CpuPtr = CpuPtr; AccCache.CpuPtr = CpuPtr;
AccCache.AccPtr = (uint64_t)NULL; AccCache.AccPtr = (uint64_t)NULL;
AccCache.bytes = bytes; AccCache.bytes = bytes;
@@ -370,10 +338,10 @@ uint64_t MemoryManager::AcceleratorViewOpen(uint64_t CpuPtr,size_t bytes,ViewMod
AccCache.accLock++; AccCache.accLock++;
dprintf("AccDirty entry ++accLock= %d",AccCache.accLock); dprintf("AccDirty entry ++accLock= %d",AccCache.accLock);
} else { } else {
GRID_ASSERT(0); assert(0);
} }
GRID_ASSERT(AccCache.accLock>0); assert(AccCache.accLock>0);
// If view is opened on device must remove from LRU // If view is opened on device must remove from LRU
if(AccCache.LRU_valid==1){ if(AccCache.LRU_valid==1){
// must possibly remove from LRU as now locked on GPU // must possibly remove from LRU as now locked on GPU
@@ -394,8 +362,8 @@ void MemoryManager::AcceleratorViewClose(uint64_t CpuPtr)
auto AccCacheIterator = EntryLookup(CpuPtr); auto AccCacheIterator = EntryLookup(CpuPtr);
auto & AccCache = AccCacheIterator->second; auto & AccCache = AccCacheIterator->second;
GRID_ASSERT(AccCache.cpuLock==0); assert(AccCache.cpuLock==0);
GRID_ASSERT(AccCache.accLock>0); assert(AccCache.accLock>0);
AccCache.accLock--; AccCache.accLock--;
// Move to LRU queue if not locked and close on device // Move to LRU queue if not locked and close on device
@@ -411,17 +379,10 @@ void MemoryManager::CpuViewClose(uint64_t CpuPtr)
auto AccCacheIterator = EntryLookup(CpuPtr); auto AccCacheIterator = EntryLookup(CpuPtr);
auto & AccCache = AccCacheIterator->second; auto & AccCache = AccCacheIterator->second;
GRID_ASSERT(AccCache.cpuLock>0); assert(AccCache.cpuLock>0);
GRID_ASSERT(AccCache.accLock==0); assert(AccCache.accLock==0);
AccCache.cpuLock--; AccCache.cpuLock--;
// Return to LRU queue when fully unlocked -- mirrors AcceleratorViewClose.
// Asymmetry vs the Acc side: a device copy need not exist for a host view;
// only device-resident entries belong in the (evictable) LRU queue.
if( (AccCache.cpuLock==0) && (AccCache.AccPtr!=(uint64_t)NULL) ) {
dprintf("CpuViewClose %lx cpuLock decremented to zero, move to LRU queue",(uint64_t)CpuPtr);
LRUinsert(AccCache);
}
} }
/* /*
* Action State StateNext Flush Clone * Action State StateNext Flush Clone
@@ -452,12 +413,12 @@ uint64_t MemoryManager::CpuViewOpen(uint64_t CpuPtr,size_t bytes,ViewMode mode,V
// EvictVictims(bytes); // EvictVictims(bytes);
// } // }
GRID_ASSERT((mode==CpuRead)||(mode==CpuWrite)); assert((mode==CpuRead)||(mode==CpuWrite));
GRID_ASSERT(AccCache.accLock==0); // Programming error assert(AccCache.accLock==0); // Programming error
if(AccCache.state!=Empty) { if(AccCache.state!=Empty) {
GRID_ASSERT(AccCache.CpuPtr == CpuPtr); assert(AccCache.CpuPtr == CpuPtr);
GRID_ASSERT(AccCache.bytes==bytes); assert(AccCache.bytes==bytes);
} }
if(AccCache.state==Empty) { if(AccCache.state==Empty) {
@@ -472,28 +433,20 @@ uint64_t MemoryManager::CpuViewOpen(uint64_t CpuPtr,size_t bytes,ViewMode mode,V
AccCache.state = CpuDirty; // CpuDirty +CpuRead/CpuWrite => CpuDirty AccCache.state = CpuDirty; // CpuDirty +CpuRead/CpuWrite => CpuDirty
AccCache.cpuLock++; AccCache.cpuLock++;
} else if(AccCache.state==Consistent) { } else if(AccCache.state==Consistent) {
GRID_ASSERT(AccCache.AccPtr != (uint64_t)NULL); assert(AccCache.AccPtr != (uint64_t)NULL);
if(mode==CpuWrite) if(mode==CpuWrite)
AccCache.state = CpuDirty; // Consistent +CpuWrite => CpuDirty AccCache.state = CpuDirty; // Consistent +CpuWrite => CpuDirty
else else
AccCache.state = Consistent; // Consistent +CpuRead => Consistent AccCache.state = Consistent; // Consistent +CpuRead => Consistent
AccCache.cpuLock++; AccCache.cpuLock++;
} else if(AccCache.state==AccDirty) { } else if(AccCache.state==AccDirty) {
GRID_ASSERT(AccCache.AccPtr != (uint64_t)NULL); assert(AccCache.AccPtr != (uint64_t)NULL);
Flush(AccCache); Flush(AccCache);
if(mode==CpuWrite) AccCache.state = CpuDirty; // AccDirty +CpuWrite => CpuDirty, Flush if(mode==CpuWrite) AccCache.state = CpuDirty; // AccDirty +CpuWrite => CpuDirty, Flush
else AccCache.state = Consistent; // AccDirty +CpuRead => Consistent, Flush else AccCache.state = Consistent; // AccDirty +CpuRead => Consistent, Flush
AccCache.cpuLock++; AccCache.cpuLock++;
} else { } else {
GRID_ASSERT(0); // should be unreachable assert(0); // should be unreachable
}
GRID_ASSERT(AccCache.cpuLock>0);
// If view is opened on host must remove from LRU -- mirrors AcceleratorViewOpen.
// LRU_valid==1 here implies this is the 0->1 lock edge of a device-resident entry.
if(AccCache.LRU_valid==1){
dprintf("CpuViewOpen: entry removed from LRU ");
LRUremove(AccCache);
} }
AccCache.transient= transient? EvictNext : 0; AccCache.transient= transient? EvictNext : 0;
@@ -575,12 +528,12 @@ void MemoryManager::Audit(std::string s)
std::cout << " Memory Manager::Audit() from "<<s<<std::endl; std::cout << " Memory Manager::Audit() from "<<s<<std::endl;
for(auto it=LRU.begin();it!=LRU.end();it++){ for(auto it=LRU.begin();it!=LRU.end();it++){
uint64_t cpuPtr = *it; uint64_t cpuPtr = *it;
GRID_ASSERT(EntryPresent(cpuPtr)); assert(EntryPresent(cpuPtr));
auto AccCacheIterator = EntryLookup(cpuPtr); auto AccCacheIterator = EntryLookup(cpuPtr);
auto & AccCache = AccCacheIterator->second; auto & AccCache = AccCacheIterator->second;
LruBytes2+=AccCache.bytes; LruBytes2+=AccCache.bytes;
GRID_ASSERT(AccCache.LRU_valid==1); assert(AccCache.LRU_valid==1);
GRID_ASSERT(AccCache.LRU_entry==it); assert(AccCache.LRU_entry==it);
} }
std::cout << " Memory Manager::Audit() LRU queue matches table entries "<<std::endl; std::cout << " Memory Manager::Audit() LRU queue matches table entries "<<std::endl;
@@ -599,7 +552,7 @@ void MemoryManager::Audit(std::string s)
if( AccCache.LRU_valid ) LruCnt++; if( AccCache.LRU_valid ) LruCnt++;
if ( AccCache.cpuLock || AccCache.accLock ) { if ( AccCache.cpuLock || AccCache.accLock ) {
GRID_ASSERT(AccCache.LRU_valid==0); assert(AccCache.LRU_valid==0);
std::cout << GridLogError << s<< "\n\t 0x"<<std::hex<<AccCache.CpuPtr<<std::dec std::cout << GridLogError << s<< "\n\t 0x"<<std::hex<<AccCache.CpuPtr<<std::dec
<< "\t0x"<<std::hex<<AccCache.AccPtr<<std::dec<<"\t" <<str << "\t0x"<<std::hex<<AccCache.AccPtr<<std::dec<<"\t" <<str
@@ -608,16 +561,16 @@ void MemoryManager::Audit(std::string s)
<< "\t LRUvalid " << AccCache.LRU_valid<<std::endl; << "\t LRUvalid " << AccCache.LRU_valid<<std::endl;
} }
GRID_ASSERT( AccCache.cpuLock== 0 ) ; assert( AccCache.cpuLock== 0 ) ;
GRID_ASSERT( AccCache.accLock== 0 ) ; assert( AccCache.accLock== 0 ) ;
} }
std::cout << " Memory Manager::Audit() no locked table entries "<<std::endl; std::cout << " Memory Manager::Audit() no locked table entries "<<std::endl;
GRID_ASSERT(LruBytes1==LruBytes2); assert(LruBytes1==LruBytes2);
GRID_ASSERT(LruBytes1==DeviceLRUBytes); assert(LruBytes1==DeviceLRUBytes);
std::cout << " Memory Manager::Audit() evictable bytes matches sum over table "<<std::endl; std::cout << " Memory Manager::Audit() evictable bytes matches sum over table "<<std::endl;
GRID_ASSERT(AccBytes==DeviceBytes); assert(AccBytes==DeviceBytes);
std::cout << " Memory Manager::Audit() device bytes matches sum over table "<<std::endl; std::cout << " Memory Manager::Audit() device bytes matches sum over table "<<std::endl;
GRID_ASSERT(LruCnt == LRU.size()); assert(LruCnt == LRU.size());
std::cout << " Memory Manager::Audit() LRU entry count matches "<<std::endl; std::cout << " Memory Manager::Audit() LRU entry count matches "<<std::endl;
} }
+3 -3
View File
@@ -10,16 +10,16 @@ void check_huge_pages(void *Buf,uint64_t BYTES)
{ {
#ifdef __linux__ #ifdef __linux__
int fd = open("/proc/self/pagemap", O_RDONLY); int fd = open("/proc/self/pagemap", O_RDONLY);
GRID_ASSERT(fd >= 0); assert(fd >= 0);
const int page_size = 4096; const int page_size = 4096;
uint64_t virt_pfn = (uint64_t)Buf / page_size; uint64_t virt_pfn = (uint64_t)Buf / page_size;
off_t offset = sizeof(uint64_t) * virt_pfn; off_t offset = sizeof(uint64_t) * virt_pfn;
uint64_t npages = (BYTES + page_size-1) / page_size; uint64_t npages = (BYTES + page_size-1) / page_size;
std::vector<uint64_t> pagedata(npages); std::vector<uint64_t> pagedata(npages);
uint64_t ret = lseek(fd, offset, SEEK_SET); uint64_t ret = lseek(fd, offset, SEEK_SET);
GRID_ASSERT(ret == offset); assert(ret == offset);
ret = ::read(fd, &pagedata[0], sizeof(uint64_t)*npages); ret = ::read(fd, &pagedata[0], sizeof(uint64_t)*npages);
GRID_ASSERT(ret == sizeof(uint64_t) * npages); assert(ret == sizeof(uint64_t) * npages);
int nhugepages = npages / 512; int nhugepages = npages / 512;
int n4ktotal, nnothuge; int n4ktotal, nnothuge;
n4ktotal = 0; n4ktotal = 0;
+4 -4
View File
@@ -165,7 +165,7 @@ public:
// //
if ( _simd_layout[dimension] > 2 ) { if ( _simd_layout[dimension] > 2 ) {
for(int d=0;d<_ndimension;d++){ for(int d=0;d<_ndimension;d++){
if ( d != dimension ) GRID_ASSERT ( (_simd_layout[d]==1) ); if ( d != dimension ) assert ( (_simd_layout[d]==1) );
} }
permute_type = RotateBit; // How to specify distance; this is not just direction. permute_type = RotateBit; // How to specify distance; this is not just direction.
return permute_type; return permute_type;
@@ -187,7 +187,7 @@ public:
inline int64_t gSites(void) const { return (int64_t)_isites*(int64_t)_osites*(int64_t)_Nprocessors; }; inline int64_t gSites(void) const { return (int64_t)_isites*(int64_t)_osites*(int64_t)_Nprocessors; };
inline int Nd (void) const { return _ndimension;}; inline int Nd (void) const { return _ndimension;};
inline const Coordinate &LocalStarts(void) { return _lstart; }; inline const Coordinate LocalStarts(void) { return _lstart; };
inline const Coordinate &FullDimensions(void) { return _fdimensions;}; inline const Coordinate &FullDimensions(void) { return _fdimensions;};
inline const Coordinate &GlobalDimensions(void) { return _gdimensions;}; inline const Coordinate &GlobalDimensions(void) { return _gdimensions;};
inline const Coordinate &LocalDimensions(void) { return _ldimensions;}; inline const Coordinate &LocalDimensions(void) { return _ldimensions;};
@@ -216,11 +216,11 @@ public:
// Global addressing // Global addressing
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
void GlobalIndexToGlobalCoor(int64_t gidx,Coordinate &gcoor){ void GlobalIndexToGlobalCoor(int64_t gidx,Coordinate &gcoor){
GRID_ASSERT(gidx< gSites()); assert(gidx< gSites());
Lexicographic::CoorFromIndex(gcoor,gidx,_gdimensions); Lexicographic::CoorFromIndex(gcoor,gidx,_gdimensions);
} }
void LocalIndexToLocalCoor(int lidx,Coordinate &lcoor){ void LocalIndexToLocalCoor(int lidx,Coordinate &lcoor){
GRID_ASSERT(lidx<lSites()); assert(lidx<lSites());
Lexicographic::CoorFromIndex(lcoor,lidx,_ldimensions); Lexicographic::CoorFromIndex(lcoor,lidx,_ldimensions);
} }
void GlobalCoorToGlobalIndex(const Coordinate & gcoor,int64_t & gidx){ void GlobalCoorToGlobalIndex(const Coordinate & gcoor,int64_t & gidx){
+2 -2
View File
@@ -128,10 +128,10 @@ public:
// Use a reduced simd grid // Use a reduced simd grid
_ldimensions[d] = _gdimensions[d] / _processors[d]; //local dimensions _ldimensions[d] = _gdimensions[d] / _processors[d]; //local dimensions
//std::cout << _ldimensions[d] << " " << _gdimensions[d] << " " << _processors[d] << std::endl; //std::cout << _ldimensions[d] << " " << _gdimensions[d] << " " << _processors[d] << std::endl;
GRID_ASSERT(_ldimensions[d] * _processors[d] == _gdimensions[d]); assert(_ldimensions[d] * _processors[d] == _gdimensions[d]);
_rdimensions[d] = _ldimensions[d] / _simd_layout[d]; //overdecomposition _rdimensions[d] = _ldimensions[d] / _simd_layout[d]; //overdecomposition
GRID_ASSERT(_rdimensions[d] * _simd_layout[d] == _ldimensions[d]); assert(_rdimensions[d] * _simd_layout[d] == _ldimensions[d]);
_lstart[d] = _processor_coor[d] * _ldimensions[d]; _lstart[d] = _processor_coor[d] * _ldimensions[d];
_lend[d] = _processor_coor[d] * _ldimensions[d] + _ldimensions[d] - 1; _lend[d] = _processor_coor[d] * _ldimensions[d] + _ldimensions[d] - 1;
+9 -9
View File
@@ -67,7 +67,7 @@ public:
} }
virtual int CheckerBoard(const Coordinate &site){ virtual int CheckerBoard(const Coordinate &site){
int linear=0; int linear=0;
GRID_ASSERT(site.size()==_ndimension); assert(site.size()==_ndimension);
for(int d=0;d<_ndimension;d++){ for(int d=0;d<_ndimension;d++){
if(_checker_dim_mask[d]) if(_checker_dim_mask[d])
linear=linear+site[d]; linear=linear+site[d];
@@ -160,11 +160,11 @@ public:
_isCheckerBoarded = true; _isCheckerBoarded = true;
_checker_dim = checker_dim; _checker_dim = checker_dim;
GRID_ASSERT(checker_dim_mask[checker_dim] == 1); assert(checker_dim_mask[checker_dim] == 1);
_ndimension = dimensions.size(); _ndimension = dimensions.size();
GRID_ASSERT(checker_dim_mask.size() == _ndimension); assert(checker_dim_mask.size() == _ndimension);
GRID_ASSERT(processor_grid.size() == _ndimension); assert(processor_grid.size() == _ndimension);
GRID_ASSERT(simd_layout.size() == _ndimension); assert(simd_layout.size() == _ndimension);
_fdimensions.resize(_ndimension); _fdimensions.resize(_ndimension);
_gdimensions.resize(_ndimension); _gdimensions.resize(_ndimension);
@@ -190,20 +190,20 @@ public:
if (d == _checker_dim) if (d == _checker_dim)
{ {
GRID_ASSERT((_gdimensions[d] & 0x1) == 0); assert((_gdimensions[d] & 0x1) == 0);
_gdimensions[d] = _gdimensions[d] / 2; // Remove a checkerboard _gdimensions[d] = _gdimensions[d] / 2; // Remove a checkerboard
_gsites /= 2; _gsites /= 2;
} }
_ldimensions[d] = _gdimensions[d] / _processors[d]; _ldimensions[d] = _gdimensions[d] / _processors[d];
GRID_ASSERT(_ldimensions[d] * _processors[d] == _gdimensions[d]); assert(_ldimensions[d] * _processors[d] == _gdimensions[d]);
_lstart[d] = _processor_coor[d] * _ldimensions[d]; _lstart[d] = _processor_coor[d] * _ldimensions[d];
_lend[d] = _processor_coor[d] * _ldimensions[d] + _ldimensions[d] - 1; _lend[d] = _processor_coor[d] * _ldimensions[d] + _ldimensions[d] - 1;
// Use a reduced simd grid // Use a reduced simd grid
_simd_layout[d] = simd_layout[d]; _simd_layout[d] = simd_layout[d];
_rdimensions[d] = _ldimensions[d] / _simd_layout[d]; // this is not checking if this is integer _rdimensions[d] = _ldimensions[d] / _simd_layout[d]; // this is not checking if this is integer
GRID_ASSERT(_rdimensions[d] * _simd_layout[d] == _ldimensions[d]); assert(_rdimensions[d] * _simd_layout[d] == _ldimensions[d]);
GRID_ASSERT(_rdimensions[d] > 0); assert(_rdimensions[d] > 0);
// all elements of a simd vector must have same checkerboard. // all elements of a simd vector must have same checkerboard.
// If Ls vectorised, this must still be the case; e.g. dwf rb5d // If Ls vectorised, this must still be the case; e.g. dwf rb5d
+15 -43
View File
@@ -108,7 +108,7 @@ public:
// very VERY rarely (Log, serial RNG) we need world without a grid // very VERY rarely (Log, serial RNG) we need world without a grid
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
static int RankWorld(void) ; static int RankWorld(void) ;
static void BroadcastWorld(int root,void* data, uint64_t bytes); static void BroadcastWorld(int root,void* data, int bytes);
static void BarrierWorld(void); static void BarrierWorld(void);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
@@ -149,7 +149,7 @@ public:
sizeof(obj),d*100+p); sizeof(obj),d*100+p);
} }
if (!list.empty()) // avoid triggering GRID_ASSERT in comms == none if (!list.empty()) // avoid triggering assert in comms == none
CommsComplete(list); CommsComplete(list);
for(int p=1;p<_processors[d];p++){ for(int p=1;p<_processors[d];p++){
accum = accum + column[p]; accum = accum + column[p];
@@ -175,38 +175,37 @@ public:
int dest, int dest,
void *recv, void *recv,
int from, int from,
uint64_t bytes,int dir); int bytes,int dir);
void SendToRecvFrom(void *xmit, void SendToRecvFrom(void *xmit,
int xmit_to_rank, int xmit_to_rank,
void *recv, void *recv,
int recv_from_rank, int recv_from_rank,
uint64_t bytes); int bytes);
int IsOffNode(int rank);
double StencilSendToRecvFrom(void *xmit, double StencilSendToRecvFrom(void *xmit,
int xmit_to_rank,int do_xmit, int xmit_to_rank,int do_xmit,
void *recv, void *recv,
int recv_from_rank,int do_recv, int recv_from_rank,int do_recv,
uint64_t bytes,int dir); int bytes,int dir);
double StencilSendToRecvFromPrepare(std::vector<CommsRequest_t> &list, double StencilSendToRecvFromPrepare(std::vector<CommsRequest_t> &list,
void *xmit, void *xmit,
int xmit_to_rank,int do_xmit, int xmit_to_rank,int do_xmit,
void *recv, void *recv,
int recv_from_rank,int do_recv, int recv_from_rank,int do_recv,
uint64_t xbytes,uint64_t rbytes,int dir); int xbytes,int rbytes,int dir);
// Could do a PollHtoD and have a CommsMerge dependence // Could do a PollHtoD and have a CommsMerge dependence
void StencilSendToRecvFromPollDtoH (std::vector<CommsRequest_t> &list); void StencilSendToRecvFromPollDtoH (std::vector<CommsRequest_t> &list);
void StencilSendToRecvFromPollIRecv(std::vector<CommsRequest_t> &list); void StencilSendToRecvFromPollIRecv(std::vector<CommsRequest_t> &list);
double StencilSendToRecvFromBegin(std::vector<CommsRequest_t> &list, double StencilSendToRecvFromBegin(std::vector<CommsRequest_t> &list,
void *xmit,void *xmit_comp, void *xmit,
int xmit_to_rank,int do_xmit, int xmit_to_rank,int do_xmit,
void *recv,void *recv_comp, void *recv,
int recv_from_rank,int do_recv, int recv_from_rank,int do_recv,
uint64_t xbytes,uint64_t rbytes,int dir); int xbytes,int rbytes,int dir);
void StencilSendToRecvFromComplete(std::vector<CommsRequest_t> &waitall,int i); void StencilSendToRecvFromComplete(std::vector<CommsRequest_t> &waitall,int i);
@@ -220,51 +219,24 @@ public:
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// Broadcast a buffer and composite larger // Broadcast a buffer and composite larger
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
void Broadcast(int root,void* data, uint64_t bytes); void Broadcast(int root,void* data, int bytes);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// All2All down one dimension // All2All down one dimension
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
template<class T> void AllToAll(int dim,std::vector<T> &in, std::vector<T> &out){ template<class T> void AllToAll(int dim,std::vector<T> &in, std::vector<T> &out){
GRID_ASSERT(dim>=0); assert(dim>=0);
GRID_ASSERT(dim<_ndimension); assert(dim<_ndimension);
GRID_ASSERT(in.size()==out.size()); assert(in.size()==out.size());
int numnode = _processors[dim]; int numnode = _processors[dim];
uint64_t bytes=sizeof(T); uint64_t bytes=sizeof(T);
uint64_t words=in.size()/numnode; uint64_t words=in.size()/numnode;
GRID_ASSERT(numnode * words == in.size()); assert(numnode * words == in.size());
GRID_ASSERT(words < (1ULL<<31)); assert(words < (1ULL<<31));
AllToAll(dim,(void *)&in[0],(void *)&out[0],words,bytes); AllToAll(dim,(void *)&in[0],(void *)&out[0],words,bytes);
} }
void AllToAll(int dim ,void *in,void *out,uint64_t words,uint64_t bytes); void AllToAll(int dim ,void *in,void *out,uint64_t words,uint64_t bytes);
void AllToAll(void *in,void *out,uint64_t words ,uint64_t bytes); void AllToAll(void *in,void *out,uint64_t words ,uint64_t bytes);
////////////////////////////////////////////////////////////////////////////
// Variable count all to all. Counts and displacements are in units of
// "bytes" sized words and are indexed by rank within this communicator.
// For exchanges that are a permutation but do not divide evenly between
// ranks; AllToAll above is the uniform count special case.
////////////////////////////////////////////////////////////////////////////
void AllToAllV(void *in ,const std::vector<int> &sendcounts,const std::vector<int> &senddispls,
void *out,const std::vector<int> &recvcounts,const std::vector<int> &recvdispls,
uint64_t bytes);
////////////////////////////////////////////////////////////////////////////
// Gather to all. Every rank contributes sendcount words from "in"; every
// rank receives the concatenation of all contributions in rank order.
// Counts and displacements are in units of "bytes" sized words and are
// indexed by rank within this communicator; recvcounts[ThisRank()] must
// equal sendcount. AllGather below is the uniform count special case.
//
// This is the primitive form of the zero-fill + GlobalSumVector idiom used
// wherever each element of the result has exactly one contributing rank.
// That idiom moves the payload twice and reduces over zeros; this moves it
// once and performs no arithmetic.
////////////////////////////////////////////////////////////////////////////
void AllGatherV(void *in ,int sendcount,
void *out,const std::vector<int> &recvcounts,const std::vector<int> &recvdispls,
uint64_t bytes);
void AllGather (void *in ,void *out,uint64_t words,uint64_t bytes);
template<class obj> void Broadcast(int root,obj &data) template<class obj> void Broadcast(int root,obj &data)
{ {
+86 -224
View File
@@ -28,17 +28,10 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
#include <Grid/GridCore.h> #include <Grid/GridCore.h>
#include <Grid/communicator/SharedMemory.h> #include <Grid/communicator/SharedMemory.h>
void GridAbort(void) { MPI_Abort(MPI_COMM_WORLD,SIGABRT); }
extern void * Grid_backtrace_buffer[_NBACKTRACE];
NAMESPACE_BEGIN(Grid); NAMESPACE_BEGIN(Grid);
Grid_MPI_Comm CartesianCommunicator::communicator_world; Grid_MPI_Comm CartesianCommunicator::communicator_world;
#ifdef GRID_CHECKSUM_COMMS
uint64_t checksum_index = 1;
#endif
//////////////////////////////////////////// ////////////////////////////////////////////
// First initialise of comms system // First initialise of comms system
@@ -63,11 +56,11 @@ void CartesianCommunicator::Init(int *argc, char ***argv)
#endif #endif
//If only 1 comms thread we require any threading mode other than SINGLE, but for multiple comms threads we need MULTIPLE //If only 1 comms thread we require any threading mode other than SINGLE, but for multiple comms threads we need MULTIPLE
if( (nCommThreads == 1) && (provided == MPI_THREAD_SINGLE) ) { if( (nCommThreads == 1) && (provided == MPI_THREAD_SINGLE) ) {
GRID_ASSERT(0); assert(0);
} }
if( (nCommThreads > 1) && (provided != MPI_THREAD_MULTIPLE) ) { if( (nCommThreads > 1) && (provided != MPI_THREAD_MULTIPLE) ) {
GRID_ASSERT(0); assert(0);
} }
} }
@@ -88,20 +81,20 @@ void CartesianCommunicator::Init(int *argc, char ***argv)
void CartesianCommunicator::ShiftedRanks(int dim,int shift,int &source,int &dest) void CartesianCommunicator::ShiftedRanks(int dim,int shift,int &source,int &dest)
{ {
int ierr=MPI_Cart_shift(communicator,dim,shift,&source,&dest); int ierr=MPI_Cart_shift(communicator,dim,shift,&source,&dest);
GRID_ASSERT(ierr==0); assert(ierr==0);
} }
int CartesianCommunicator::RankFromProcessorCoor(Coordinate &coor) int CartesianCommunicator::RankFromProcessorCoor(Coordinate &coor)
{ {
int rank; int rank;
int ierr=MPI_Cart_rank (communicator, &coor[0], &rank); int ierr=MPI_Cart_rank (communicator, &coor[0], &rank);
GRID_ASSERT(ierr==0); assert(ierr==0);
return rank; return rank;
} }
void CartesianCommunicator::ProcessorCoorFromRank(int rank, Coordinate &coor) void CartesianCommunicator::ProcessorCoorFromRank(int rank, Coordinate &coor)
{ {
coor.resize(_ndimension); coor.resize(_ndimension);
int ierr=MPI_Cart_coords (communicator, rank, _ndimension,&coor[0]); int ierr=MPI_Cart_coords (communicator, rank, _ndimension,&coor[0]);
GRID_ASSERT(ierr==0); assert(ierr==0);
} }
//////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -128,8 +121,8 @@ CartesianCommunicator::CartesianCommunicator(const Coordinate &processors)
////////////////////////////////// //////////////////////////////////
CartesianCommunicator::CartesianCommunicator(const Coordinate &processors,const CartesianCommunicator &parent,int &srank) CartesianCommunicator::CartesianCommunicator(const Coordinate &processors,const CartesianCommunicator &parent,int &srank)
{ {
_ndimension = processors.size(); GRID_ASSERT(_ndimension>=1); _ndimension = processors.size(); assert(_ndimension>=1);
int parent_ndimension = parent._ndimension; GRID_ASSERT(_ndimension >= parent._ndimension); int parent_ndimension = parent._ndimension; assert(_ndimension >= parent._ndimension);
Coordinate parent_processor_coor(_ndimension,0); Coordinate parent_processor_coor(_ndimension,0);
Coordinate parent_processors (_ndimension,1); Coordinate parent_processors (_ndimension,1);
Coordinate shm_processors (_ndimension,1); Coordinate shm_processors (_ndimension,1);
@@ -153,7 +146,7 @@ CartesianCommunicator::CartesianCommunicator(const Coordinate &processors,const
childsize *= processors[d]; childsize *= processors[d];
} }
int Nchild = Nparent/childsize; int Nchild = Nparent/childsize;
GRID_ASSERT (childsize * Nchild == Nparent); assert (childsize * Nchild == Nparent);
Coordinate ccoor(_ndimension); // coor within subcommunicator Coordinate ccoor(_ndimension); // coor within subcommunicator
Coordinate scoor(_ndimension); // coor of split within parent Coordinate scoor(_ndimension); // coor of split within parent
@@ -179,12 +172,12 @@ CartesianCommunicator::CartesianCommunicator(const Coordinate &processors,const
// Split the communicator // Split the communicator
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
int ierr= MPI_Comm_split(parent.communicator,srank,crank,&comm_split); int ierr= MPI_Comm_split(parent.communicator,srank,crank,&comm_split);
GRID_ASSERT(ierr==0); assert(ierr==0);
} else { } else {
srank = 0; srank = 0;
int ierr = MPI_Comm_dup (parent.communicator,&comm_split); int ierr = MPI_Comm_dup (parent.communicator,&comm_split);
GRID_ASSERT(ierr==0); assert(ierr==0);
} }
////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -209,7 +202,7 @@ CartesianCommunicator::CartesianCommunicator(const Coordinate &processors,const
} }
} }
for(int d=0;d<processors.size();d++){ for(int d=0;d<processors.size();d++){
GRID_ASSERT(_processor_coor[d] == ccoor[d] ); assert(_processor_coor[d] == ccoor[d] );
} }
} }
@@ -251,7 +244,7 @@ void CartesianCommunicator::InitFromMPICommunicator(const Coordinate &processors
for(int i=0;i<_ndimension*2;i++){ for(int i=0;i<_ndimension*2;i++){
MPI_Comm_dup(communicator,&communicator_halo[i]); MPI_Comm_dup(communicator,&communicator_halo[i]);
} }
GRID_ASSERT(Size==_Nprocessors); assert(Size==_Nprocessors);
} }
CartesianCommunicator::~CartesianCommunicator() CartesianCommunicator::~CartesianCommunicator()
@@ -267,74 +260,62 @@ CartesianCommunicator::~CartesianCommunicator()
} }
#ifdef USE_GRID_REDUCTION #ifdef USE_GRID_REDUCTION
void CartesianCommunicator::GlobalSum(float &f){ void CartesianCommunicator::GlobalSum(float &f){
FlightRecorder::StepLog("GlobalSumP2P");
CartesianCommunicator::GlobalSumP2P(f); CartesianCommunicator::GlobalSumP2P(f);
} }
void CartesianCommunicator::GlobalSum(double &d) void CartesianCommunicator::GlobalSum(double &d)
{ {
FlightRecorder::StepLog("GlobalSumP2P");
CartesianCommunicator::GlobalSumP2P(d); CartesianCommunicator::GlobalSumP2P(d);
} }
#else #else
void CartesianCommunicator::GlobalSum(float &f){ void CartesianCommunicator::GlobalSum(float &f){
FlightRecorder::StepLog("AllReduce float");
int ierr=MPI_Allreduce(MPI_IN_PLACE,&f,1,MPI_FLOAT,MPI_SUM,communicator); int ierr=MPI_Allreduce(MPI_IN_PLACE,&f,1,MPI_FLOAT,MPI_SUM,communicator);
GRID_ASSERT(ierr==0); assert(ierr==0);
} }
void CartesianCommunicator::GlobalSum(double &d) void CartesianCommunicator::GlobalSum(double &d)
{ {
FlightRecorder::StepLog("AllReduce double");
int ierr = MPI_Allreduce(MPI_IN_PLACE,&d,1,MPI_DOUBLE,MPI_SUM,communicator); int ierr = MPI_Allreduce(MPI_IN_PLACE,&d,1,MPI_DOUBLE,MPI_SUM,communicator);
GRID_ASSERT(ierr==0); assert(ierr==0);
} }
#endif #endif
void CartesianCommunicator::GlobalSum(uint32_t &u){ void CartesianCommunicator::GlobalSum(uint32_t &u){
FlightRecorder::StepLog("AllReduce uint32_t");
int ierr=MPI_Allreduce(MPI_IN_PLACE,&u,1,MPI_UINT32_T,MPI_SUM,communicator); int ierr=MPI_Allreduce(MPI_IN_PLACE,&u,1,MPI_UINT32_T,MPI_SUM,communicator);
GRID_ASSERT(ierr==0); assert(ierr==0);
} }
void CartesianCommunicator::GlobalSum(uint64_t &u){ void CartesianCommunicator::GlobalSum(uint64_t &u){
FlightRecorder::StepLog("AllReduce uint64_t");
int ierr=MPI_Allreduce(MPI_IN_PLACE,&u,1,MPI_UINT64_T,MPI_SUM,communicator); int ierr=MPI_Allreduce(MPI_IN_PLACE,&u,1,MPI_UINT64_T,MPI_SUM,communicator);
GRID_ASSERT(ierr==0); assert(ierr==0);
} }
void CartesianCommunicator::GlobalSumVector(uint64_t* u,int N){ void CartesianCommunicator::GlobalSumVector(uint64_t* u,int N){
FlightRecorder::StepLog("AllReduceVector");
int ierr=MPI_Allreduce(MPI_IN_PLACE,u,N,MPI_UINT64_T,MPI_SUM,communicator); int ierr=MPI_Allreduce(MPI_IN_PLACE,u,N,MPI_UINT64_T,MPI_SUM,communicator);
GRID_ASSERT(ierr==0); assert(ierr==0);
} }
void CartesianCommunicator::GlobalXOR(uint32_t &u){ void CartesianCommunicator::GlobalXOR(uint32_t &u){
int ierr=MPI_Allreduce(MPI_IN_PLACE,&u,1,MPI_UINT32_T,MPI_BXOR,communicator); int ierr=MPI_Allreduce(MPI_IN_PLACE,&u,1,MPI_UINT32_T,MPI_BXOR,communicator);
GRID_ASSERT(ierr==0); assert(ierr==0);
} }
void CartesianCommunicator::GlobalXOR(uint64_t &u){ void CartesianCommunicator::GlobalXOR(uint64_t &u){
FlightRecorder::StepLog("GlobalXOR");
int ierr=MPI_Allreduce(MPI_IN_PLACE,&u,1,MPI_UINT64_T,MPI_BXOR,communicator); int ierr=MPI_Allreduce(MPI_IN_PLACE,&u,1,MPI_UINT64_T,MPI_BXOR,communicator);
GRID_ASSERT(ierr==0); assert(ierr==0);
} }
void CartesianCommunicator::GlobalMax(float &f) void CartesianCommunicator::GlobalMax(float &f)
{ {
FlightRecorder::StepLog("GlobalMax");
int ierr=MPI_Allreduce(MPI_IN_PLACE,&f,1,MPI_FLOAT,MPI_MAX,communicator); int ierr=MPI_Allreduce(MPI_IN_PLACE,&f,1,MPI_FLOAT,MPI_MAX,communicator);
GRID_ASSERT(ierr==0); assert(ierr==0);
} }
void CartesianCommunicator::GlobalMax(double &d) void CartesianCommunicator::GlobalMax(double &d)
{ {
FlightRecorder::StepLog("GlobalMax");
int ierr = MPI_Allreduce(MPI_IN_PLACE,&d,1,MPI_DOUBLE,MPI_MAX,communicator); int ierr = MPI_Allreduce(MPI_IN_PLACE,&d,1,MPI_DOUBLE,MPI_MAX,communicator);
GRID_ASSERT(ierr==0); assert(ierr==0);
} }
void CartesianCommunicator::GlobalSumVector(float *f,int N) void CartesianCommunicator::GlobalSumVector(float *f,int N)
{ {
FlightRecorder::StepLog("GlobalSumVector(float *)");
int ierr=MPI_Allreduce(MPI_IN_PLACE,f,N,MPI_FLOAT,MPI_SUM,communicator); int ierr=MPI_Allreduce(MPI_IN_PLACE,f,N,MPI_FLOAT,MPI_SUM,communicator);
GRID_ASSERT(ierr==0); assert(ierr==0);
} }
void CartesianCommunicator::GlobalSumVector(double *d,int N) void CartesianCommunicator::GlobalSumVector(double *d,int N)
{ {
FlightRecorder::StepLog("GlobalSumVector(double *)");
int ierr = MPI_Allreduce(MPI_IN_PLACE,d,N,MPI_DOUBLE,MPI_SUM,communicator); int ierr = MPI_Allreduce(MPI_IN_PLACE,d,N,MPI_DOUBLE,MPI_SUM,communicator);
GRID_ASSERT(ierr==0); assert(ierr==0);
} }
void CartesianCommunicator::SendToRecvFromBegin(std::vector<MpiCommsRequest_t> &list, void CartesianCommunicator::SendToRecvFromBegin(std::vector<MpiCommsRequest_t> &list,
@@ -342,23 +323,24 @@ void CartesianCommunicator::SendToRecvFromBegin(std::vector<MpiCommsRequest_t> &
int dest, int dest,
void *recv, void *recv,
int from, int from,
uint64_t bytes,int dir) int bytes,int dir)
{ {
MPI_Request xrq; MPI_Request xrq;
MPI_Request rrq; MPI_Request rrq;
GRID_ASSERT(dest != _processor); assert(dest != _processor);
GRID_ASSERT(from != _processor); assert(from != _processor);
int tag; int tag;
tag= dir+from*32; tag= dir+from*32;
int ierr=MPI_Irecv(recv,(int)( bytes/sizeof(int32_t)), MPI_INT32_T,from,tag,communicator,&rrq); int ierr=MPI_Irecv(recv, bytes, MPI_CHAR,from,tag,communicator,&rrq);
GRID_ASSERT(ierr==0); assert(ierr==0);
list.push_back(rrq); list.push_back(rrq);
tag= dir+_processor*32; tag= dir+_processor*32;
ierr =MPI_Isend(xmit,(int)(bytes/sizeof(int32_t)), MPI_INT32_T,dest,tag,communicator,&xrq); ierr =MPI_Isend(xmit, bytes, MPI_CHAR,dest,tag,communicator,&xrq);
GRID_ASSERT(ierr==0); assert(ierr==0);
list.push_back(xrq); list.push_back(xrq);
} }
void CartesianCommunicator::CommsComplete(std::vector<MpiCommsRequest_t> &list) void CartesianCommunicator::CommsComplete(std::vector<MpiCommsRequest_t> &list)
@@ -369,7 +351,7 @@ void CartesianCommunicator::CommsComplete(std::vector<MpiCommsRequest_t> &list)
std::vector<MPI_Status> status(nreq); std::vector<MPI_Status> status(nreq);
int ierr = MPI_Waitall(nreq,&list[0],&status[0]); int ierr = MPI_Waitall(nreq,&list[0],&status[0]);
GRID_ASSERT(ierr==0); assert(ierr==0);
list.resize(0); list.resize(0);
} }
@@ -378,7 +360,7 @@ void CartesianCommunicator::SendToRecvFrom(void *xmit,
int dest, int dest,
void *recv, void *recv,
int from, int from,
uint64_t bytes) int bytes)
{ {
std::vector<MpiCommsRequest_t> reqs(0); std::vector<MpiCommsRequest_t> reqs(0);
@@ -386,15 +368,15 @@ void CartesianCommunicator::SendToRecvFrom(void *xmit,
int ierr; int ierr;
// Enforce no UVM in comms, device or host OK // Enforce no UVM in comms, device or host OK
GRID_ASSERT(acceleratorIsCommunicable(xmit)); assert(acceleratorIsCommunicable(xmit));
GRID_ASSERT(acceleratorIsCommunicable(recv)); assert(acceleratorIsCommunicable(recv));
// Give the CPU to MPI immediately; can use threads to overlap optionally // Give the CPU to MPI immediately; can use threads to overlap optionally
// printf("proc %d SendToRecvFrom %d bytes Sendrecv \n",_processor,bytes); // printf("proc %d SendToRecvFrom %d bytes Sendrecv \n",_processor,bytes);
ierr=MPI_Sendrecv(xmit,(int)(bytes/sizeof(int32_t)),MPI_INT32_T,dest,myrank, ierr=MPI_Sendrecv(xmit,bytes,MPI_CHAR,dest,myrank,
recv,(int)(bytes/sizeof(int32_t)),MPI_INT32_T,from, from, recv,bytes,MPI_CHAR,from, from,
communicator,MPI_STATUS_IGNORE); communicator,MPI_STATUS_IGNORE);
GRID_ASSERT(ierr==0); assert(ierr==0);
} }
// Basic Halo comms primitive // Basic Halo comms primitive
@@ -402,20 +384,15 @@ double CartesianCommunicator::StencilSendToRecvFrom( void *xmit,
int dest, int dox, int dest, int dox,
void *recv, void *recv,
int from, int dor, int from, int dor,
uint64_t bytes,int dir) int bytes,int dir)
{ {
std::vector<CommsRequest_t> list; std::vector<CommsRequest_t> list;
double offbytes = StencilSendToRecvFromPrepare(list,xmit,dest,dox,recv,from,dor,bytes,bytes,dir); double offbytes = StencilSendToRecvFromPrepare(list,xmit,dest,dox,recv,from,dor,bytes,bytes,dir);
offbytes += StencilSendToRecvFromBegin(list,xmit,xmit,dest,dox,recv,recv,from,dor,bytes,bytes,dir); offbytes += StencilSendToRecvFromBegin(list,xmit,dest,dox,recv,from,dor,bytes,bytes,dir);
StencilSendToRecvFromComplete(list,dir); StencilSendToRecvFromComplete(list,dir);
return offbytes; return offbytes;
} }
int CartesianCommunicator::IsOffNode(int rank)
{
int grank = ShmRanks[rank];
if ( grank == MPI_UNDEFINED ) return true;
else return false;
}
#ifdef ACCELERATOR_AWARE_MPI #ifdef ACCELERATOR_AWARE_MPI
void CartesianCommunicator::StencilSendToRecvFromPollIRecv(std::vector<CommsRequest_t> &list) {}; void CartesianCommunicator::StencilSendToRecvFromPollIRecv(std::vector<CommsRequest_t> &list) {};
@@ -425,16 +402,16 @@ double CartesianCommunicator::StencilSendToRecvFromPrepare(std::vector<CommsRequ
int dest,int dox, int dest,int dox,
void *recv, void *recv,
int from,int dor, int from,int dor,
uint64_t xbytes,uint64_t rbytes,int dir) int xbytes,int rbytes,int dir)
{ {
return 0.0; // Do nothing -- no preparation required return 0.0; // Do nothing -- no preparation required
} }
double CartesianCommunicator::StencilSendToRecvFromBegin(std::vector<CommsRequest_t> &list, double CartesianCommunicator::StencilSendToRecvFromBegin(std::vector<CommsRequest_t> &list,
void *xmit,void *xmit_comp, void *xmit,
int dest,int dox, int dest,int dox,
void *recv,void *recv_comp, void *recv,
int from,int dor, int from,int dor,
uint64_t xbytes,uint64_t rbytes,int dir) int xbytes,int rbytes,int dir)
{ {
int ncomm =communicator_halo.size(); int ncomm =communicator_halo.size();
int commdir=dir%ncomm; int commdir=dir%ncomm;
@@ -447,26 +424,24 @@ double CartesianCommunicator::StencilSendToRecvFromBegin(std::vector<CommsReques
int gfrom = ShmRanks[from]; int gfrom = ShmRanks[from];
int gme = ShmRanks[_processor]; int gme = ShmRanks[_processor];
GRID_ASSERT(dest != _processor); assert(dest != _processor);
GRID_ASSERT(from != _processor); assert(from != _processor);
GRID_ASSERT(gme == ShmRank); assert(gme == ShmRank);
double off_node_bytes=0.0; double off_node_bytes=0.0;
int tag; int tag;
if ( dor ) { if ( dor ) {
if ( (gfrom ==MPI_UNDEFINED) || Stencil_force_mpi ) { if ( (gfrom ==MPI_UNDEFINED) || Stencil_force_mpi ) {
tag= dir+from*32; tag= dir+from*32;
// std::cout << " StencilSendToRecvFrom "<<dir<<" MPI_Irecv "<<std::hex<<recv<<std::dec<<std::endl; ierr=MPI_Irecv(recv, rbytes, MPI_CHAR,from,tag,communicator_halo[commdir],&rrq);
ierr=MPI_Irecv(recv_comp,(int)(rbytes/sizeof(int32_t)), MPI_INT32_T,from,tag,communicator_halo[commdir],&rrq); assert(ierr==0);
GRID_ASSERT(ierr==0);
list.push_back(rrq); list.push_back(rrq);
off_node_bytes+=rbytes; off_node_bytes+=rbytes;
} }
#ifdef NVLINK_GET #ifdef NVLINK_GET
else { else {
void *shm = (void *) this->ShmBufferTranslate(from,xmit); void *shm = (void *) this->ShmBufferTranslate(from,xmit);
GRID_ASSERT(shm!=NULL); assert(shm!=NULL);
// std::cout << " StencilSendToRecvFrom "<<dir<<" CopyDeviceToDevice recv "<<std::hex<<recv<<" remote "<<shm <<std::dec<<std::endl;
acceleratorCopyDeviceToDeviceAsynch(shm,recv,rbytes); acceleratorCopyDeviceToDeviceAsynch(shm,recv,rbytes);
} }
#endif #endif
@@ -475,14 +450,14 @@ double CartesianCommunicator::StencilSendToRecvFromBegin(std::vector<CommsReques
if (dox) { if (dox) {
if ( (gdest == MPI_UNDEFINED) || Stencil_force_mpi ) { if ( (gdest == MPI_UNDEFINED) || Stencil_force_mpi ) {
tag= dir+_processor*32; tag= dir+_processor*32;
ierr =MPI_Isend(xmit_comp,(int)(xbytes/sizeof(int32_t)), MPI_INT32_T,dest,tag,communicator_halo[commdir],&xrq); ierr =MPI_Isend(xmit, xbytes, MPI_CHAR,dest,tag,communicator_halo[commdir],&xrq);
GRID_ASSERT(ierr==0); assert(ierr==0);
list.push_back(xrq); list.push_back(xrq);
off_node_bytes+=xbytes; off_node_bytes+=xbytes;
} else { } else {
#ifndef NVLINK_GET #ifndef NVLINK_GET
void *shm = (void *) this->ShmBufferTranslate(dest,recv); void *shm = (void *) this->ShmBufferTranslate(dest,recv);
GRID_ASSERT(shm!=NULL); assert(shm!=NULL);
acceleratorCopyDeviceToDeviceAsynch(xmit,shm,xbytes); acceleratorCopyDeviceToDeviceAsynch(xmit,shm,xbytes);
#endif #endif
} }
@@ -499,7 +474,7 @@ void CartesianCommunicator::StencilSendToRecvFromComplete(std::vector<CommsReque
if (nreq==0) return; if (nreq==0) return;
std::vector<MPI_Status> status(nreq); std::vector<MPI_Status> status(nreq);
int ierr = MPI_Waitall(nreq,&list[0],&status[0]); int ierr = MPI_Waitall(nreq,&list[0],&status[0]);
GRID_ASSERT(ierr==0); assert(ierr==0);
list.resize(0); list.resize(0);
this->StencilBarrier(); this->StencilBarrier();
} }
@@ -542,7 +517,7 @@ double CartesianCommunicator::StencilSendToRecvFromPrepare(std::vector<CommsRequ
int dest,int dox, int dest,int dox,
void *recv, void *recv,
int from,int dor, int from,int dor,
uint64_t xbytes,uint64_t rbytes,int dir) int xbytes,int rbytes,int dir)
{ {
/* /*
* Bring sequence from Stencil.h down to lower level. * Bring sequence from Stencil.h down to lower level.
@@ -559,9 +534,9 @@ double CartesianCommunicator::StencilSendToRecvFromPrepare(std::vector<CommsRequ
int gfrom = ShmRanks[from]; int gfrom = ShmRanks[from];
int gme = ShmRanks[_processor]; int gme = ShmRanks[_processor];
GRID_ASSERT(dest != _processor); assert(dest != _processor);
GRID_ASSERT(from != _processor); assert(from != _processor);
GRID_ASSERT(gme == ShmRank); assert(gme == ShmRank);
double off_node_bytes=0.0; double off_node_bytes=0.0;
int tag; int tag;
@@ -574,24 +549,18 @@ double CartesianCommunicator::StencilSendToRecvFromPrepare(std::vector<CommsRequ
* - post device - host send buffer transfer asynch * - post device - host send buffer transfer asynch
*/ */
#ifdef GRID_CHECKSUM_COMMS
rbytes += 8;
xbytes += 8;
#endif
if ( dor ) { if ( dor ) {
if ( (gfrom ==MPI_UNDEFINED) || Stencil_force_mpi ) { if ( (gfrom ==MPI_UNDEFINED) || Stencil_force_mpi ) {
tag= dir+from*32; tag= dir+from*32;
host_recv = this->HostBufferMalloc(rbytes); host_recv = this->HostBufferMalloc(rbytes);
ierr=MPI_Irecv(host_recv,(int)(rbytes/sizeof(int32_t)), MPI_INT32_T,from,tag,communicator_halo[commdir],&rrq); ierr=MPI_Irecv(host_recv, rbytes, MPI_CHAR,from,tag,communicator_halo[commdir],&rrq);
GRID_ASSERT(ierr==0); assert(ierr==0);
CommsRequest_t srq; CommsRequest_t srq;
srq.PacketType = InterNodeRecv; srq.PacketType = InterNodeRecv;
srq.bytes = rbytes; srq.bytes = rbytes;
srq.req = rrq; srq.req = rrq;
srq.host_buf = host_recv; srq.host_buf = host_recv;
srq.device_buf = recv; srq.device_buf = recv;
srq.tag = tag;
list.push_back(srq); list.push_back(srq);
off_node_bytes+=rbytes; off_node_bytes+=rbytes;
} }
@@ -605,19 +574,10 @@ double CartesianCommunicator::StencilSendToRecvFromPrepare(std::vector<CommsRequ
host_xmit = this->HostBufferMalloc(xbytes); host_xmit = this->HostBufferMalloc(xbytes);
CommsRequest_t srq; CommsRequest_t srq;
#ifdef GRID_CHECKSUM_COMMS
uint64_t xbytes_data = xbytes - 8;
srq.ev = acceleratorCopyFromDeviceAsynch(xmit, host_xmit,xbytes_data); // Make this Asynch
GRID_ASSERT(xbytes % 8 == 0);
// flip one bit so that a zero buffer is not consistent
uint64_t xsum = checksum_gpu((uint64_t*)xmit, xbytes_data / 8) ^ (checksum_index + 1 + 1000 * tag);
*(uint64_t*)(((char*)host_xmit) + xbytes_data) = xsum;
#else
srq.ev = acceleratorCopyFromDeviceAsynch(xmit, host_xmit,xbytes); // Make this Asynch srq.ev = acceleratorCopyFromDeviceAsynch(xmit, host_xmit,xbytes); // Make this Asynch
#endif
// ierr =MPI_Isend(host_xmit, xbytes, MPI_CHAR,dest,tag,communicator_halo[commdir],&xrq); // ierr =MPI_Isend(host_xmit, xbytes, MPI_CHAR,dest,tag,communicator_halo[commdir],&xrq);
// GRID_ASSERT(ierr==0); // assert(ierr==0);
// off_node_bytes+=xbytes; // off_node_bytes+=xbytes;
srq.PacketType = InterNodeXmit; srq.PacketType = InterNodeXmit;
@@ -656,11 +616,7 @@ void CartesianCommunicator::StencilSendToRecvFromPollIRecv(std::vector<CommsRequ
if ( flag ) { if ( flag ) {
// std::cout << " PollIrecv "<<idx<<" flag "<<flag<<std::endl; // std::cout << " PollIrecv "<<idx<<" flag "<<flag<<std::endl;
#ifdef GRID_CHECKSUM_COMMS
acceleratorCopyToDeviceAsynch(list[idx].host_buf,list[idx].device_buf,list[idx].bytes - 8);
#else
acceleratorCopyToDeviceAsynch(list[idx].host_buf,list[idx].device_buf,list[idx].bytes); acceleratorCopyToDeviceAsynch(list[idx].host_buf,list[idx].device_buf,list[idx].bytes);
#endif
list[idx].PacketType=InterNodeReceiveHtoD; list[idx].PacketType=InterNodeReceiveHtoD;
} else { } else {
pending ++; pending ++;
@@ -685,7 +641,7 @@ void CartesianCommunicator::StencilSendToRecvFromPollDtoH(std::vector<CommsReque
if ( acceleratorEventIsComplete(list[idx].ev) ) { if ( acceleratorEventIsComplete(list[idx].ev) ) {
void *host_xmit = list[idx].host_buf; void *host_xmit = list[idx].host_buf;
uint64_t xbytes = list[idx].bytes; uint32_t xbytes = list[idx].bytes;
int dest = list[idx].dest; int dest = list[idx].dest;
int tag = list[idx].tag; int tag = list[idx].tag;
int commdir = list[idx].commdir; int commdir = list[idx].commdir;
@@ -696,8 +652,8 @@ void CartesianCommunicator::StencilSendToRecvFromPollDtoH(std::vector<CommsReque
// std::cout << " DtoH is complete for index "<<idx<<" calling MPI_Isend "<<std::endl; // std::cout << " DtoH is complete for index "<<idx<<" calling MPI_Isend "<<std::endl;
MPI_Request xrq; MPI_Request xrq;
int ierr =MPI_Isend(host_xmit, (int)(xbytes/sizeof(int32_t)), MPI_INT32_T,dest,tag,communicator_halo[commdir],&xrq); int ierr =MPI_Isend(host_xmit, xbytes, MPI_CHAR,dest,tag,communicator_halo[commdir],&xrq);
GRID_ASSERT(ierr==0); assert(ierr==0);
list[idx].req = xrq; // Update the MPI request in the list list[idx].req = xrq; // Update the MPI request in the list
@@ -713,11 +669,11 @@ void CartesianCommunicator::StencilSendToRecvFromPollDtoH(std::vector<CommsReque
} }
double CartesianCommunicator::StencilSendToRecvFromBegin(std::vector<CommsRequest_t> &list, double CartesianCommunicator::StencilSendToRecvFromBegin(std::vector<CommsRequest_t> &list,
void *xmit,void *xmit_comp, void *xmit,
int dest,int dox, int dest,int dox,
void *recv,void *recv_comp, void *recv,
int from,int dor, int from,int dor,
uint64_t xbytes,uint64_t rbytes,int dir) int xbytes,int rbytes,int dir)
{ {
int ncomm =communicator_halo.size(); int ncomm =communicator_halo.size();
int commdir=dir%ncomm; int commdir=dir%ncomm;
@@ -730,9 +686,9 @@ double CartesianCommunicator::StencilSendToRecvFromBegin(std::vector<CommsReques
int gfrom = ShmRanks[from]; int gfrom = ShmRanks[from];
int gme = ShmRanks[_processor]; int gme = ShmRanks[_processor];
GRID_ASSERT(dest != _processor); assert(dest != _processor);
GRID_ASSERT(from != _processor); assert(from != _processor);
GRID_ASSERT(gme == ShmRank); assert(gme == ShmRank);
double off_node_bytes=0.0; double off_node_bytes=0.0;
int tag; int tag;
@@ -753,7 +709,7 @@ double CartesianCommunicator::StencilSendToRecvFromBegin(std::vector<CommsReques
if ( ! ( (gfrom ==MPI_UNDEFINED) || Stencil_force_mpi ) ) { if ( ! ( (gfrom ==MPI_UNDEFINED) || Stencil_force_mpi ) ) {
// Intranode // Intranode
void *shm = (void *) this->ShmBufferTranslate(from,xmit); void *shm = (void *) this->ShmBufferTranslate(from,xmit);
GRID_ASSERT(shm!=NULL); assert(shm!=NULL);
CommsRequest_t srq; CommsRequest_t srq;
@@ -776,7 +732,7 @@ double CartesianCommunicator::StencilSendToRecvFromBegin(std::vector<CommsReques
if ( !( (gdest == MPI_UNDEFINED) || Stencil_force_mpi ) ) { if ( !( (gdest == MPI_UNDEFINED) || Stencil_force_mpi ) ) {
// Intranode // Intranode
void *shm = (void *) this->ShmBufferTranslate(dest,recv); void *shm = (void *) this->ShmBufferTranslate(dest,recv);
GRID_ASSERT(shm!=NULL); assert(shm!=NULL);
CommsRequest_t srq; CommsRequest_t srq;
@@ -815,7 +771,7 @@ void CartesianCommunicator::StencilSendToRecvFromComplete(std::vector<CommsReque
if (nreq>0) { if (nreq>0) {
status.resize(MpiRequests.size()); status.resize(MpiRequests.size());
int ierr = MPI_Waitall(MpiRequests.size(),&MpiRequests[0],&status[0]); // Sends are guaranteed in order. No harm in not completing. int ierr = MPI_Waitall(MpiRequests.size(),&MpiRequests[0],&status[0]); // Sends are guaranteed in order. No harm in not completing.
GRID_ASSERT(ierr==0); assert(ierr==0);
} }
// for(int r=0;r<nreq;r++){ // for(int r=0;r<nreq;r++){
@@ -823,40 +779,7 @@ void CartesianCommunicator::StencilSendToRecvFromComplete(std::vector<CommsReque
// acceleratorCopyToDeviceAsynch(list[r].host_buf,list[r].device_buf,list[r].bytes); // acceleratorCopyToDeviceAsynch(list[r].host_buf,list[r].device_buf,list[r].bytes);
// } // }
// } // }
#ifdef GRID_CHECKSUM_COMMS
for(int r=0;r<list.size();r++){
if ( list[r].PacketType == InterNodeReceiveHtoD ) {
uint64_t rbytes_data = list[r].bytes - 8;
uint64_t expected_cs = *(uint64_t*)(((char*)list[r].host_buf) + rbytes_data);
uint64_t computed_cs = checksum_gpu((uint64_t*)list[r].device_buf, rbytes_data / 8) ^ (checksum_index + 1 + 1000 * list[r].tag); //
if (expected_cs != computed_cs) {
// TODO: error message, backtrace, quit
fprintf(stderr, "GRID_CHECKSUM_COMMS error:\n");
fprintf(stderr, " processor = %d\n", (int)_processor);
for(int d=0;d<_processors.size();d++)
fprintf(stderr, " processor_coord[%d] = %d\n", d, _processor_coor[d]);
fprintf(stderr, " hostname: %s\n", GridHostname());
fprintf(stderr, " expected_cs: %ld\n", expected_cs);
fprintf(stderr, " computed_cs: %ld\n", computed_cs);
fprintf(stderr, " dest: %d\n", list[r].dest);
fprintf(stderr, " tag: %d\n", list[r].tag);
fprintf(stderr, " commdir: %d\n", list[r].commdir);
fprintf(stderr, " bytes: %ld\n", (uint64_t)list[r].bytes);
fflush(stderr);
// backtrace
int symbols = backtrace(Grid_backtrace_buffer,_NBACKTRACE);
backtrace_symbols_fd(Grid_backtrace_buffer,symbols, 2);
exit(1);
}
}
}
checksum_index += 1;
#endif
list.resize(0); // Delete the list list.resize(0); // Delete the list
this->HostBufferFreeAll(); // Clean up the buffer allocs this->HostBufferFreeAll(); // Clean up the buffer allocs
@@ -871,7 +794,6 @@ void CartesianCommunicator::StencilSendToRecvFromComplete(std::vector<CommsReque
void CartesianCommunicator::StencilBarrier(void) void CartesianCommunicator::StencilBarrier(void)
{ {
FlightRecorder::StepLog("NodeBarrier");
MPI_Barrier (ShmComm); MPI_Barrier (ShmComm);
} }
//void CartesianCommunicator::SendToRecvFromComplete(std::vector<CommsRequest_t> &list) //void CartesianCommunicator::SendToRecvFromComplete(std::vector<CommsRequest_t> &list)
@@ -879,19 +801,17 @@ void CartesianCommunicator::StencilBarrier(void)
//} //}
void CartesianCommunicator::Barrier(void) void CartesianCommunicator::Barrier(void)
{ {
FlightRecorder::StepLog("GridBarrier");
int ierr = MPI_Barrier(communicator); int ierr = MPI_Barrier(communicator);
GRID_ASSERT(ierr==0); assert(ierr==0);
} }
void CartesianCommunicator::Broadcast(int root,void* data,uint64_t bytes) void CartesianCommunicator::Broadcast(int root,void* data, int bytes)
{ {
FlightRecorder::StepLog("Broadcast");
int ierr=MPI_Bcast(data, int ierr=MPI_Bcast(data,
(int)bytes, bytes,
MPI_BYTE, MPI_BYTE,
root, root,
communicator); communicator);
GRID_ASSERT(ierr==0); assert(ierr==0);
} }
int CartesianCommunicator::RankWorld(void){ int CartesianCommunicator::RankWorld(void){
int r; int r;
@@ -899,25 +819,23 @@ int CartesianCommunicator::RankWorld(void){
return r; return r;
} }
void CartesianCommunicator::BarrierWorld(void){ void CartesianCommunicator::BarrierWorld(void){
FlightRecorder::StepLog("BarrierWorld");
int ierr = MPI_Barrier(communicator_world); int ierr = MPI_Barrier(communicator_world);
GRID_ASSERT(ierr==0); assert(ierr==0);
} }
void CartesianCommunicator::BroadcastWorld(int root,void* data, uint64_t bytes) void CartesianCommunicator::BroadcastWorld(int root,void* data, int bytes)
{ {
FlightRecorder::StepLog("BroadcastWorld");
int ierr= MPI_Bcast(data, int ierr= MPI_Bcast(data,
(int)bytes, bytes,
MPI_BYTE, MPI_BYTE,
root, root,
communicator_world); communicator_world);
GRID_ASSERT(ierr==0); assert(ierr==0);
} }
void CartesianCommunicator::AllToAll(int dim,void *in,void *out,uint64_t words,uint64_t bytes) void CartesianCommunicator::AllToAll(int dim,void *in,void *out,uint64_t words,uint64_t bytes)
{ {
Coordinate row(_ndimension,1); Coordinate row(_ndimension,1);
GRID_ASSERT(dim>=0 && dim<_ndimension); assert(dim>=0 && dim<_ndimension);
// Split the communicator // Split the communicator
row[dim] = _processors[dim]; row[dim] = _processors[dim];
@@ -928,7 +846,6 @@ void CartesianCommunicator::AllToAll(int dim,void *in,void *out,uint64_t words,
} }
void CartesianCommunicator::AllToAll(void *in,void *out,uint64_t words,uint64_t bytes) void CartesianCommunicator::AllToAll(void *in,void *out,uint64_t words,uint64_t bytes)
{ {
FlightRecorder::StepLog("AllToAll");
// MPI is a pain and uses "int" arguments // MPI is a pain and uses "int" arguments
// 64*64*64*128*16 == 500Million elements of data. // 64*64*64*128*16 == 500Million elements of data.
// When 24*4 bytes multiples get 50x 10^9 >>> 2x10^9 Y2K bug. // When 24*4 bytes multiples get 50x 10^9 >>> 2x10^9 Y2K bug.
@@ -938,67 +855,12 @@ void CartesianCommunicator::AllToAll(void *in,void *out,uint64_t words,uint64_t
int ibytes; int ibytes;
iwords = words; iwords = words;
ibytes = bytes; ibytes = bytes;
GRID_ASSERT(words == iwords); // safe to cast to int ? assert(words == iwords); // safe to cast to int ?
GRID_ASSERT(bytes == ibytes); // safe to cast to int ? assert(bytes == ibytes); // safe to cast to int ?
MPI_Type_contiguous(ibytes,MPI_BYTE,&object); MPI_Type_contiguous(ibytes,MPI_BYTE,&object);
MPI_Type_commit(&object); MPI_Type_commit(&object);
MPI_Alltoall(in,iwords,object,out,iwords,object,communicator); MPI_Alltoall(in,iwords,object,out,iwords,object,communicator);
MPI_Type_free(&object); MPI_Type_free(&object);
} }
void CartesianCommunicator::AllToAllV(void *in ,const std::vector<int> &sendcounts,const std::vector<int> &senddispls,
void *out,const std::vector<int> &recvcounts,const std::vector<int> &recvdispls,
uint64_t bytes)
{
FlightRecorder::StepLog("AllToAllV");
GRID_ASSERT(sendcounts.size()==(size_t)_Nprocessors);
GRID_ASSERT(senddispls.size()==(size_t)_Nprocessors);
GRID_ASSERT(recvcounts.size()==(size_t)_Nprocessors);
GRID_ASSERT(recvdispls.size()==(size_t)_Nprocessors);
// MPI counts are "int"; the caller sizes the word to keep them in range
int ibytes = bytes;
GRID_ASSERT(bytes == (uint64_t)ibytes);
MPI_Datatype object;
MPI_Type_contiguous(ibytes,MPI_BYTE,&object);
MPI_Type_commit(&object);
int ierr = MPI_Alltoallv(in ,(int *)&sendcounts[0],(int *)&senddispls[0],object,
out,(int *)&recvcounts[0],(int *)&recvdispls[0],object,communicator);
GRID_ASSERT(ierr==0);
MPI_Type_free(&object);
}
void CartesianCommunicator::AllGatherV(void *in ,int sendcount,
void *out,const std::vector<int> &recvcounts,const std::vector<int> &recvdispls,
uint64_t bytes)
{
FlightRecorder::StepLog("AllGatherV");
GRID_ASSERT(recvcounts.size()==(size_t)_Nprocessors);
GRID_ASSERT(recvdispls.size()==(size_t)_Nprocessors);
GRID_ASSERT(recvcounts[_processor]==sendcount);
// MPI counts are "int"; the caller sizes the word to keep them in range
int ibytes = bytes;
GRID_ASSERT(bytes == (uint64_t)ibytes);
MPI_Datatype object;
MPI_Type_contiguous(ibytes,MPI_BYTE,&object);
MPI_Type_commit(&object);
int ierr = MPI_Allgatherv(in ,sendcount,object,
out,(int *)&recvcounts[0],(int *)&recvdispls[0],object,communicator);
GRID_ASSERT(ierr==0);
MPI_Type_free(&object);
}
void CartesianCommunicator::AllGather(void *in,void *out,uint64_t words,uint64_t bytes)
{
FlightRecorder::StepLog("AllGather");
int iwords = words;
int ibytes = bytes;
GRID_ASSERT(words == (uint64_t)iwords);
GRID_ASSERT(bytes == (uint64_t)ibytes);
MPI_Datatype object;
MPI_Type_contiguous(ibytes,MPI_BYTE,&object);
MPI_Type_commit(&object);
int ierr = MPI_Allgather(in,iwords,object,out,iwords,object,communicator);
GRID_ASSERT(ierr==0);
MPI_Type_free(&object);
}
NAMESPACE_END(Grid); NAMESPACE_END(Grid);
+14 -47
View File
@@ -27,8 +27,6 @@ Author: Peter Boyle <paboyle@ph.ed.ac.uk>
/* END LEGAL */ /* END LEGAL */
#include <Grid/GridCore.h> #include <Grid/GridCore.h>
void GridAbort(void) { abort(); }
NAMESPACE_BEGIN(Grid); NAMESPACE_BEGIN(Grid);
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -36,7 +34,6 @@ NAMESPACE_BEGIN(Grid);
/////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////
Grid_MPI_Comm CartesianCommunicator::communicator_world; Grid_MPI_Comm CartesianCommunicator::communicator_world;
void CartesianCommunicator::Init(int *argc, char *** arv) void CartesianCommunicator::Init(int *argc, char *** arv)
{ {
GlobalSharedMemory::Init(communicator_world); GlobalSharedMemory::Init(communicator_world);
@@ -57,14 +54,14 @@ CartesianCommunicator::CartesianCommunicator(const Coordinate &processors)
{ {
_shm_processors = Coordinate(processors.size(),1); _shm_processors = Coordinate(processors.size(),1);
_processors = processors; _processors = processors;
_ndimension = processors.size(); GRID_ASSERT(_ndimension>=1); _ndimension = processors.size(); assert(_ndimension>=1);
_processor_coor.resize(_ndimension); _processor_coor.resize(_ndimension);
// Require 1^N processor grid for fake // Require 1^N processor grid for fake
_Nprocessors=1; _Nprocessors=1;
_processor = 0; _processor = 0;
for(int d=0;d<_ndimension;d++) { for(int d=0;d<_ndimension;d++) {
GRID_ASSERT(_processors[d]==1); assert(_processors[d]==1);
_processor_coor[d] = 0; _processor_coor[d] = 0;
} }
SetCommunicator(communicator_world); SetCommunicator(communicator_world);
@@ -90,19 +87,19 @@ void CartesianCommunicator::SendToRecvFrom(void *xmit,
int dest, int dest,
void *recv, void *recv,
int from, int from,
uint64_t bytes) int bytes)
{ {
GRID_ASSERT(0); assert(0);
} }
void CartesianCommunicator::CommsComplete(std::vector<CommsRequest_t> &list){ GRID_ASSERT(list.size()==0);} void CartesianCommunicator::CommsComplete(std::vector<CommsRequest_t> &list){ assert(list.size()==0);}
void CartesianCommunicator::SendToRecvFromBegin(std::vector<CommsRequest_t> &list, void CartesianCommunicator::SendToRecvFromBegin(std::vector<CommsRequest_t> &list,
void *xmit, void *xmit,
int dest, int dest,
void *recv, void *recv,
int from, int from,
uint64_t bytes,int dir) int bytes,int dir)
{ {
GRID_ASSERT(0); assert(0);
} }
void CartesianCommunicator::AllToAll(int dim,void *in,void *out,uint64_t words,uint64_t bytes) void CartesianCommunicator::AllToAll(int dim,void *in,void *out,uint64_t words,uint64_t bytes)
@@ -113,39 +110,11 @@ void CartesianCommunicator::AllToAll(void *in,void *out,uint64_t words,uint64_t
{ {
bcopy(in,out,bytes*words); bcopy(in,out,bytes*words);
} }
void CartesianCommunicator::AllToAllV(void *in ,const std::vector<int> &sendcounts,const std::vector<int> &senddispls,
void *out,const std::vector<int> &recvcounts,const std::vector<int> &recvdispls,
uint64_t bytes)
{
// Single rank: the exchange degenerates to a copy of our own segment
GRID_ASSERT(sendcounts.size()==1);
GRID_ASSERT(recvcounts.size()==1);
GRID_ASSERT(sendcounts[0]==recvcounts[0]);
bcopy((char *)in +(uint64_t)senddispls[0]*bytes,
(char *)out+(uint64_t)recvdispls[0]*bytes,bytes*(uint64_t)sendcounts[0]);
}
void CartesianCommunicator::AllGatherV(void *in ,int sendcount,
void *out,const std::vector<int> &recvcounts,const std::vector<int> &recvdispls,
uint64_t bytes)
{
// Single rank: the gather degenerates to a copy of our own contribution
GRID_ASSERT(recvcounts.size()==1);
GRID_ASSERT(recvdispls.size()==1);
GRID_ASSERT(recvcounts[0]==sendcount);
bcopy((char *)in,
(char *)out+(uint64_t)recvdispls[0]*bytes,bytes*(uint64_t)sendcount);
}
void CartesianCommunicator::AllGather(void *in,void *out,uint64_t words,uint64_t bytes)
{
bcopy((char *)in,(char *)out,bytes*words);
}
int CartesianCommunicator::RankWorld(void){return 0;} int CartesianCommunicator::RankWorld(void){return 0;}
void CartesianCommunicator::Barrier(void){} void CartesianCommunicator::Barrier(void){}
void CartesianCommunicator::Broadcast(int root,void* data, uint64_t bytes) {} void CartesianCommunicator::Broadcast(int root,void* data, int bytes) {}
void CartesianCommunicator::BroadcastWorld(int root,void* data, uint64_t bytes) { } void CartesianCommunicator::BroadcastWorld(int root,void* data, int bytes) { }
void CartesianCommunicator::BarrierWorld(void) { } void CartesianCommunicator::BarrierWorld(void) { }
int CartesianCommunicator::RankFromProcessorCoor(Coordinate &coor) { return 0;} int CartesianCommunicator::RankFromProcessorCoor(Coordinate &coor) { return 0;}
void CartesianCommunicator::ProcessorCoorFromRank(int rank, Coordinate &coor){ coor = _processor_coor; } void CartesianCommunicator::ProcessorCoorFromRank(int rank, Coordinate &coor){ coor = _processor_coor; }
@@ -155,13 +124,11 @@ void CartesianCommunicator::ShiftedRanks(int dim,int shift,int &source,int &dest
dest=0; dest=0;
} }
int CartesianCommunicator::IsOffNode(int rank) { return false; }
double CartesianCommunicator::StencilSendToRecvFrom( void *xmit, double CartesianCommunicator::StencilSendToRecvFrom( void *xmit,
int xmit_to_rank,int dox, int xmit_to_rank,int dox,
void *recv, void *recv,
int recv_from_rank,int dor, int recv_from_rank,int dor,
uint64_t bytes, int dir) int bytes, int dir)
{ {
return 2.0*bytes; return 2.0*bytes;
} }
@@ -172,16 +139,16 @@ double CartesianCommunicator::StencilSendToRecvFromPrepare(std::vector<CommsRequ
int xmit_to_rank,int dox, int xmit_to_rank,int dox,
void *recv, void *recv,
int recv_from_rank,int dor, int recv_from_rank,int dor,
uint64_t xbytes,uint64_t rbytes, int dir) int xbytes,int rbytes, int dir)
{ {
return 0.0; return 0.0;
} }
double CartesianCommunicator::StencilSendToRecvFromBegin(std::vector<CommsRequest_t> &list, double CartesianCommunicator::StencilSendToRecvFromBegin(std::vector<CommsRequest_t> &list,
void *xmit, void *xmit_comp, void *xmit,
int xmit_to_rank,int dox, int xmit_to_rank,int dox,
void *recv, void *recv_comp, void *recv,
int recv_from_rank,int dor, int recv_from_rank,int dor,
uint64_t xbytes,uint64_t rbytes, int dir) int xbytes,int rbytes, int dir)
{ {
return xbytes+rbytes; return xbytes+rbytes;
} }
-119
View File
@@ -1,119 +0,0 @@
/*************************************************************************************
Grid physics library, www.github.com/paboyle/Grid
Source file: ./Grid/communicator/RingAllReduce.h
Copyright (C) 2026
Author: Peter Boyle <pboyle@bnl.gov>
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 */
#pragma once
NAMESPACE_BEGIN(Grid);
/////////////////////////////////////////////////////////////////////////////
// Vector all-reduce on point-to-point only (SendToRecvFrom), no MPI
// collectives: reduce-scatter ring followed by all-gather ring. A
// communicator-level primitive: needs only CartesianCommunicator.
//
// RingAllReduce(comm, buf, n) flat ring over all P ranks
// CartesianRingAllReduce(comm, buf, n) ring along each processor dimension
// in turn (P_d ranks per ring)
//
// Why: Cray MPICH device-buffer MPI_Allreduce aborts above ~8 MB (MPI_FLOAT,
// measured 4.4 MB pass / 13.3 MB fail) and delivers 5.8 GB/s where P2P rings
// deliver ~17 GB/s. A ring has no size cliff -- every step is one symmetric
// SendToRecvFrom of one chunk.
//
// Cost model, N bytes per rank:
// flat : 2(P-1) steps, 2N(P-1)/P bytes per rank -- bandwidth-optimal,
// latency 2(P-1) x step (P=288: 574 steps)
// cartesian : sum_d 2(P_d-1) steps, ~2N per dimension -- few steps, each on
// a neighbour link (3.6.4.4: 26 steps, ~8N bytes)
// Rule of thumb: cartesian for tens of MB, flat for hundreds of MB.
//
// Buffer memory space: whatever SendToRecvFrom accepts on this build (device
// under ACCELERATOR_AWARE_MPI); the reduction runs as accelerator_for on a
// deviceVector working copy, so the caller's buffer is only ever memcpy'd.
// Deterministic: summation order is fixed by rank and chunk index, so the
// result is bitwise reproducible run to run (MPI_Allreduce need not be),
// though it differs from MPI's order at rounding level.
//
// n elements of T; T must support + on the accelerator (RealF/RealD,
// ComplexF/ComplexD). sizeof(T)*chunk must be a multiple of 4 bytes
// (SendToRecvFrom counts int32 words); true for all supported T.
/////////////////////////////////////////////////////////////////////////////
// Ring all-reduce among P ranks with given next/prev neighbours; `me` is this
// rank's position in the ring. work has P*c elements, scratch has c.
template<class T>
void RingAllReduceCore(CartesianCommunicator *comm,
T *work, T *scratch, uint64_t c, int P, int me, int next, int prev)
{
if ( P==1 ) return;
uint64_t bytes = c*sizeof(T);
// reduce-scatter: after P-1 steps rank me owns fully reduced chunk (me+1)%P
for(int s=0;s<P-1;s++){
int sendc = (me - s + 2*P) % P;
int recvc = (me - s - 1 + 2*P) % P;
comm->SendToRecvFrom((void *)&work[sendc*c], next, (void *)scratch, prev, bytes);
T *dst = &work[recvc*c];
accelerator_for(i, c, 1, { dst[i] = dst[i] + scratch[i]; });
}
// all-gather: circulate the reduced chunks
for(int s=0;s<P-1;s++){
int sendc = (me - s + 1 + 2*P) % P;
int recvc = (me - s + 2*P) % P;
comm->SendToRecvFrom((void *)&work[sendc*c], next, (void *)&work[recvc*c], prev, bytes);
}
}
template<class T>
void RingAllReduce(CartesianCommunicator *comm, T *buf, uint64_t n)
{
int P = comm->ProcessorCount();
int me = comm->ThisRank();
if ( P==1 || n==0 ) return;
uint64_t c = (n + P - 1)/P;
deviceVector<T> work(c*P);
deviceVector<T> scratch(c);
T *w = &work[0];
accelerator_for(i, c*P, 1, { w[i] = T(0.0); });
acceleratorCopyDeviceToDevice((void *)buf, (void *)w, n*sizeof(T));
RingAllReduceCore(comm, w, &scratch[0], c, P, me, (me+1)%P, (me+P-1)%P);
acceleratorCopyDeviceToDevice((void *)w, (void *)buf, n*sizeof(T));
}
template<class T>
void CartesianRingAllReduce(CartesianCommunicator *comm, T *buf, uint64_t n)
{
if ( comm->ProcessorCount()==1 || n==0 ) return;
int Nd = comm->_ndimension;
for(int d=0;d<Nd;d++){
int P = comm->_processors[d];
if ( P==1 ) continue;
int me = comm->_processor_coor[d];
int next, prev;
comm->ShiftedRanks(d, 1, prev, next); // (dim, shift, source, dest)
uint64_t c = (n + P - 1)/P;
deviceVector<T> work(c*P);
deviceVector<T> scratch(c);
T *w = &work[0];
accelerator_for(i, c*P, 1, { w[i] = T(0.0); });
acceleratorCopyDeviceToDevice((void *)buf, (void *)w, n*sizeof(T));
RingAllReduceCore(comm, w, &scratch[0], c, P, me, next, prev);
acceleratorCopyDeviceToDevice((void *)w, (void *)buf, n*sizeof(T));
}
}
NAMESPACE_END(Grid);
+7 -7
View File
@@ -58,8 +58,8 @@ int GlobalSharedMemory::WorldNode;
void GlobalSharedMemory::SharedMemoryFree(void) void GlobalSharedMemory::SharedMemoryFree(void)
{ {
GRID_ASSERT(_ShmAlloc); assert(_ShmAlloc);
GRID_ASSERT(_ShmAllocBytes>0); assert(_ShmAllocBytes>0);
for(int r=0;r<WorldShmSize;r++){ for(int r=0;r<WorldShmSize;r++){
munmap(WorldShmCommBufs[r],_ShmAllocBytes); munmap(WorldShmCommBufs[r],_ShmAllocBytes);
} }
@@ -80,7 +80,7 @@ void *SharedMemory::HostBufferMalloc(size_t bytes){
std::cout<< " Current alloc is " << (bytes/(1024*1024)) <<"MB"<<std::endl; std::cout<< " Current alloc is " << (bytes/(1024*1024)) <<"MB"<<std::endl;
std::cout<< " Current bytes is " << (host_heap_bytes/(1024*1024)) <<"MB"<<std::endl; std::cout<< " Current bytes is " << (host_heap_bytes/(1024*1024)) <<"MB"<<std::endl;
std::cout<< " Current heap is " << (host_heap_size/(1024*1024)) <<"MB"<<std::endl; std::cout<< " Current heap is " << (host_heap_size/(1024*1024)) <<"MB"<<std::endl;
GRID_ASSERT(host_heap_bytes<host_heap_size); assert(host_heap_bytes<host_heap_size);
} }
return ptr; return ptr;
} }
@@ -100,7 +100,7 @@ void *SharedMemory::ShmBufferMalloc(size_t bytes){
std::cout<< " Current alloc is " << (bytes/(1024*1024)) <<"MB"<<std::endl; std::cout<< " Current alloc is " << (bytes/(1024*1024)) <<"MB"<<std::endl;
std::cout<< " Current bytes is " << (heap_bytes/(1024*1024)) <<"MB"<<std::endl; std::cout<< " Current bytes is " << (heap_bytes/(1024*1024)) <<"MB"<<std::endl;
std::cout<< " Current heap is " << (heap_size/(1024*1024)) <<"MB"<<std::endl; std::cout<< " Current heap is " << (heap_size/(1024*1024)) <<"MB"<<std::endl;
GRID_ASSERT(heap_bytes<heap_size); assert(heap_bytes<heap_size);
} }
//std::cerr << "ShmBufferMalloc "<<std::hex<< ptr<<" - "<<((uint64_t)ptr+bytes)<<std::dec<<std::endl; //std::cerr << "ShmBufferMalloc "<<std::hex<< ptr<<" - "<<((uint64_t)ptr+bytes)<<std::dec<<std::endl;
return ptr; return ptr;
@@ -127,13 +127,13 @@ void GlobalSharedMemory::GetShmDims(const Coordinate &WorldDims,Coordinate &ShmD
if ( str ) { if ( str ) {
std::vector<int> IntShmDims; std::vector<int> IntShmDims;
GridCmdOptionIntVector(std::string(str),IntShmDims); GridCmdOptionIntVector(std::string(str),IntShmDims);
GRID_ASSERT(IntShmDims.size() == WorldDims.size()); assert(IntShmDims.size() == WorldDims.size());
long ShmSize = 1; long ShmSize = 1;
for (int dim=0;dim<WorldDims.size();dim++) { for (int dim=0;dim<WorldDims.size();dim++) {
ShmSize *= (ShmDims[dim] = IntShmDims[dim]); ShmSize *= (ShmDims[dim] = IntShmDims[dim]);
GRID_ASSERT(divides(ShmDims[dim],WorldDims[dim])); assert(divides(ShmDims[dim],WorldDims[dim]));
} }
GRID_ASSERT(ShmSize == WorldShmSize); assert(ShmSize == WorldShmSize);
return; return;
} }
+87 -54
View File
@@ -43,6 +43,10 @@ Author: Christoph Lehner <christoph@lhnr.de>
#define GRID_SYCL_LEVEL_ZERO_IPC #define GRID_SYCL_LEVEL_ZERO_IPC
#define SHM_SOCKETS #define SHM_SOCKETS
#else #else
#ifdef HAVE_NUMAIF_H
#warning " Using NUMAIF "
#include <numaif.h>
#endif
#endif #endif
#include <syscall.h> #include <syscall.h>
#endif #endif
@@ -67,7 +71,7 @@ public:
{ {
int errnum; int errnum;
sock = socket(AF_UNIX, SOCK_DGRAM, 0); GRID_ASSERT(sock>0); sock = socket(AF_UNIX, SOCK_DGRAM, 0); assert(sock>0);
struct sockaddr_un sa_un = { 0 }; struct sockaddr_un sa_un = { 0 };
sa_un.sun_family = AF_UNIX; sa_un.sun_family = AF_UNIX;
@@ -158,7 +162,7 @@ public:
/*Construct from an MPI communicator*/ /*Construct from an MPI communicator*/
void GlobalSharedMemory::Init(Grid_MPI_Comm comm) void GlobalSharedMemory::Init(Grid_MPI_Comm comm)
{ {
GRID_ASSERT(_ShmSetup==0); assert(_ShmSetup==0);
WorldComm = comm; WorldComm = comm;
MPI_Comm_rank(WorldComm,&WorldRank); MPI_Comm_rank(WorldComm,&WorldRank);
MPI_Comm_size(WorldComm,&WorldSize); MPI_Comm_size(WorldComm,&WorldSize);
@@ -184,7 +188,7 @@ void GlobalSharedMemory::Init(Grid_MPI_Comm comm)
// WorldNodes // WorldNodes
WorldNodes = WorldSize/WorldShmSize; WorldNodes = WorldSize/WorldShmSize;
GRID_ASSERT( (WorldNodes * WorldShmSize) == WorldSize ); assert( (WorldNodes * WorldShmSize) == WorldSize );
// FIXME: Check all WorldShmSize are the same ? // FIXME: Check all WorldShmSize are the same ?
@@ -209,7 +213,7 @@ void GlobalSharedMemory::Init(Grid_MPI_Comm comm)
MyGroup.resize(WorldShmSize); MyGroup.resize(WorldShmSize);
for(int rank=0;rank<WorldSize;rank++){ for(int rank=0;rank<WorldSize;rank++){
if(WorldShmRanks[rank]!=MPI_UNDEFINED){ if(WorldShmRanks[rank]!=MPI_UNDEFINED){
GRID_ASSERT(g<WorldShmSize); assert(g<WorldShmSize);
MyGroup[g++] = rank; MyGroup[g++] = rank;
} }
} }
@@ -225,7 +229,7 @@ void GlobalSharedMemory::Init(Grid_MPI_Comm comm)
// global sum leaders over comm world // global sum leaders over comm world
/////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////
int ierr=MPI_Allreduce(MPI_IN_PLACE,&leaders_1hot[0],WorldSize,MPI_INT,MPI_SUM,WorldComm); int ierr=MPI_Allreduce(MPI_IN_PLACE,&leaders_1hot[0],WorldSize,MPI_INT,MPI_SUM,WorldComm);
GRID_ASSERT(ierr==0); assert(ierr==0);
/////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////
// find the group leaders world rank // find the group leaders world rank
@@ -246,7 +250,7 @@ void GlobalSharedMemory::Init(Grid_MPI_Comm comm)
WorldNode=g; WorldNode=g;
} }
} }
GRID_ASSERT(WorldNode!=-1); assert(WorldNode!=-1);
_ShmSetup=1; _ShmSetup=1;
} }
// Gray encode support // Gray encode support
@@ -288,7 +292,7 @@ void GlobalSharedMemory::OptimalCommunicatorHypercube(const Coordinate &processo
// Assert power of two shm_size. // Assert power of two shm_size.
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
int log2size = Log2Size(WorldShmSize,MAXLOG2RANKSPERNODE); int log2size = Log2Size(WorldShmSize,MAXLOG2RANKSPERNODE);
GRID_ASSERT(log2size != -1); assert(log2size != -1);
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
// Identify the hypercube coordinate of this node using hostname // Identify the hypercube coordinate of this node using hostname
@@ -309,7 +313,7 @@ void GlobalSharedMemory::OptimalCommunicatorHypercube(const Coordinate &processo
// Parse ICE-XA hostname to get hypercube location // Parse ICE-XA hostname to get hypercube location
gethostname(name,namelen); gethostname(name,namelen);
int nscan = sscanf(name,"r%di%dn%d",&R,&I,&N) ; int nscan = sscanf(name,"r%di%dn%d",&R,&I,&N) ;
GRID_ASSERT(nscan==3); assert(nscan==3);
int nlo = N%9; int nlo = N%9;
int nhi = N/9; int nhi = N/9;
@@ -333,8 +337,8 @@ void GlobalSharedMemory::OptimalCommunicatorHypercube(const Coordinate &processo
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
MPI_Bcast(&rootcoor, sizeof(rootcoor), MPI_BYTE, 0, WorldComm); MPI_Bcast(&rootcoor, sizeof(rootcoor), MPI_BYTE, 0, WorldComm);
hypercoor=hypercoor-rootcoor; hypercoor=hypercoor-rootcoor;
GRID_ASSERT(hypercoor<WorldSize); assert(hypercoor<WorldSize);
GRID_ASSERT(hypercoor>=0); assert(hypercoor>=0);
////////////////////////////////////// //////////////////////////////////////
// Printing // Printing
@@ -382,7 +386,7 @@ void GlobalSharedMemory::OptimalCommunicatorHypercube(const Coordinate &processo
for(int i=0;i<ndimension;i++){ for(int i=0;i<ndimension;i++){
Nprocessors*=processors[i]; Nprocessors*=processors[i];
} }
GRID_ASSERT(WorldSize==Nprocessors); assert(WorldSize==Nprocessors);
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
// Establish mapping between lexico physics coord and WorldRank // Establish mapping between lexico physics coord and WorldRank
@@ -401,7 +405,7 @@ void GlobalSharedMemory::OptimalCommunicatorHypercube(const Coordinate &processo
// Build the new communicator // Build the new communicator
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
int ierr= MPI_Comm_split(WorldComm,0,rank,&optimal_comm); int ierr= MPI_Comm_split(WorldComm,0,rank,&optimal_comm);
GRID_ASSERT(ierr==0); assert(ierr==0);
} }
void GlobalSharedMemory::OptimalCommunicatorSharedMemory(const Coordinate &processors,Grid_MPI_Comm & optimal_comm,Coordinate &SHM) void GlobalSharedMemory::OptimalCommunicatorSharedMemory(const Coordinate &processors,Grid_MPI_Comm & optimal_comm,Coordinate &SHM)
{ {
@@ -431,8 +435,7 @@ void GlobalSharedMemory::OptimalCommunicatorSharedMemory(const Coordinate &proce
for(int i=0;i<ndimension;i++){ for(int i=0;i<ndimension;i++){
Nprocessors*=processors[i]; Nprocessors*=processors[i];
} }
// std::cerr << " WorldSize "<<WorldSize << " Nprocessors "<<Nprocessors<<" "<<processors<<std::endl; assert(WorldSize==Nprocessors);
GRID_ASSERT(WorldSize==Nprocessors);
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
// Establish mapping between lexico physics coord and WorldRank // Establish mapping between lexico physics coord and WorldRank
@@ -448,7 +451,7 @@ void GlobalSharedMemory::OptimalCommunicatorSharedMemory(const Coordinate &proce
// Build the new communicator // Build the new communicator
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
int ierr= MPI_Comm_split(WorldComm,0,rank,&optimal_comm); int ierr= MPI_Comm_split(WorldComm,0,rank,&optimal_comm);
GRID_ASSERT(ierr==0); assert(ierr==0);
} }
//////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////
// SHMGET // SHMGET
@@ -457,8 +460,8 @@ void GlobalSharedMemory::OptimalCommunicatorSharedMemory(const Coordinate &proce
void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags)
{ {
std::cout << Mheader "SharedMemoryAllocate "<< bytes<< " shmget implementation "<<std::endl; std::cout << Mheader "SharedMemoryAllocate "<< bytes<< " shmget implementation "<<std::endl;
GRID_ASSERT(_ShmSetup==1); assert(_ShmSetup==1);
GRID_ASSERT(_ShmAlloc==0); assert(_ShmAlloc==0);
////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////
// allocate the shared windows for our group // allocate the shared windows for our group
@@ -519,8 +522,8 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags)
void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags)
{ {
void * ShmCommBuf ; void * ShmCommBuf ;
GRID_ASSERT(_ShmSetup==1); assert(_ShmSetup==1);
GRID_ASSERT(_ShmAlloc==0); assert(_ShmAlloc==0);
////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////
// allocate the pointer array for shared windows for our group // allocate the pointer array for shared windows for our group
@@ -540,21 +543,49 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags)
/////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef ACCELERATOR_AWARE_MPI #ifndef ACCELERATOR_AWARE_MPI
// printf("Host buffer allocate for GPU non-aware MPI\n"); // printf("Host buffer allocate for GPU non-aware MPI\n");
#if 0
HostCommBuf= acceleratorAllocHost(bytes);
#else
HostCommBuf= malloc(bytes); /// CHANGE THIS TO malloc_host HostCommBuf= malloc(bytes); /// CHANGE THIS TO malloc_host
#if 0
#warning "Moving host buffers to specific NUMA domain"
int numa;
char *numa_name=(char *)getenv("MPI_BUF_NUMA");
if(numa_name) {
unsigned long page_size = sysconf(_SC_PAGESIZE);
numa = atoi(numa_name);
unsigned long page_count = bytes/page_size;
std::vector<void *> pages(page_count);
std::vector<int> nodes(page_count,numa);
std::vector<int> status(page_count,-1);
for(unsigned long p=0;p<page_count;p++){
pages[p] =(void *) ((uint64_t) HostCommBuf + p*page_size);
}
int ret = move_pages(0,
page_count,
&pages[0],
&nodes[0],
&status[0],
MPOL_MF_MOVE);
printf("Host buffer move to numa domain %d : move_pages returned %d\n",numa,ret);
if (ret) perror(" move_pages failed for reason:");
}
#endif
acceleratorPin(HostCommBuf,bytes);
#endif
#endif #endif
ShmCommBuf = acceleratorAllocDevice(bytes); ShmCommBuf = acceleratorAllocDevice(bytes);
if (ShmCommBuf == (void *)NULL ) { if (ShmCommBuf == (void *)NULL ) {
std::cerr << "SharedMemoryMPI.cc acceleratorAllocDevice failed NULL pointer for " << bytes<<" bytes " << std::endl; std::cerr << " SharedMemoryMPI.cc acceleratorAllocDevice failed NULL pointer for " << bytes<<" bytes " << std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if ( WorldRank == 0 ){ if ( WorldRank == 0 ){
std::cout << Mheader " acceleratorAllocDevice "<< bytes std::cout << WorldRank << Mheader " SharedMemoryMPI.cc acceleratorAllocDevice "<< bytes
<< "bytes at "<< std::hex<< ShmCommBuf << " - "<<(bytes-1+(uint64_t)ShmCommBuf) <<std::dec<<" for comms buffers " <<std::endl; << "bytes at "<< std::hex<< ShmCommBuf << " - "<<(bytes-1+(uint64_t)ShmCommBuf) <<std::dec<<" for comms buffers " <<std::endl;
} }
SharedMemoryZero(ShmCommBuf,bytes); SharedMemoryZero(ShmCommBuf,bytes);
if ( WorldRank == 0 ){ std::cout<< "Setting up IPC"<<std::endl;
std::cout<< Mheader "Setting up IPC"<<std::endl;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////
// Loop over ranks/gpu's on our node // Loop over ranks/gpu's on our node
/////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -585,6 +616,8 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags)
if ( err != ZE_RESULT_SUCCESS ) { if ( err != ZE_RESULT_SUCCESS ) {
std::cerr << "SharedMemoryMPI.cc zeMemGetIpcHandle failed for rank "<<r<<" "<<std::hex<<err<<std::dec<<std::endl; std::cerr << "SharedMemoryMPI.cc zeMemGetIpcHandle failed for rank "<<r<<" "<<std::hex<<err<<std::dec<<std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} else {
std::cout << "SharedMemoryMPI.cc zeMemGetIpcHandle succeeded for rank "<<r<<" "<<std::hex<<err<<std::dec<<std::endl;
} }
memcpy((void *)&handle.fd,(void *)&ihandle,sizeof(int)); memcpy((void *)&handle.fd,(void *)&ihandle,sizeof(int));
handle.pid = getpid(); handle.pid = getpid();
@@ -629,7 +662,7 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags)
MPI_BYTE, MPI_BYTE,
r, r,
WorldShmComm); WorldShmComm);
GRID_ASSERT(ierr==0); assert(ierr==0);
} }
/////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////
@@ -643,12 +676,12 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags)
#ifdef SHM_SOCKETS #ifdef SHM_SOCKETS
myfd=UnixSockets::RecvFileDescriptor(); myfd=UnixSockets::RecvFileDescriptor();
#else #else
// std::cout<<"mapping seeking remote pid/fd " std::cout<<"mapping seeking remote pid/fd "
// <<handle.pid<<"/" <<handle.pid<<"/"
// <<handle.fd<<std::endl; <<handle.fd<<std::endl;
int pidfd = syscall(SYS_pidfd_open,handle.pid,0); int pidfd = syscall(SYS_pidfd_open,handle.pid,0);
// std::cout<<"Using IpcHandle pidfd "<<pidfd<<"\n"; std::cout<<"Using IpcHandle pidfd "<<pidfd<<"\n";
// int myfd = syscall(SYS_pidfd_getfd,pidfd,handle.fd,0); // int myfd = syscall(SYS_pidfd_getfd,pidfd,handle.fd,0);
myfd = syscall(438,pidfd,handle.fd,0); myfd = syscall(438,pidfd,handle.fd,0);
int err_t = errno; int err_t = errno;
@@ -658,7 +691,7 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags)
assert(0); assert(0);
} }
#endif #endif
// std::cout<<"Using IpcHandle mapped remote pid "<<handle.pid <<" FD "<<handle.fd <<" to myfd "<<myfd<<"\n"; std::cout<<"Using IpcHandle mapped remote pid "<<handle.pid <<" FD "<<handle.fd <<" to myfd "<<myfd<<"\n";
memcpy((void *)&ihandle,(void *)&handle.ze,sizeof(ihandle)); memcpy((void *)&ihandle,(void *)&handle.ze,sizeof(ihandle));
memcpy((void *)&ihandle,(void *)&myfd,sizeof(int)); memcpy((void *)&ihandle,(void *)&myfd,sizeof(int));
@@ -667,8 +700,11 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags)
std::cerr << "SharedMemoryMPI.cc "<<zeContext<<" "<<zeDevice<<std::endl; std::cerr << "SharedMemoryMPI.cc "<<zeContext<<" "<<zeDevice<<std::endl;
std::cerr << "SharedMemoryMPI.cc zeMemOpenIpcHandle failed for rank "<<r<<" "<<std::hex<<err<<std::dec<<std::endl; std::cerr << "SharedMemoryMPI.cc zeMemOpenIpcHandle failed for rank "<<r<<" "<<std::hex<<err<<std::dec<<std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} else {
std::cout << "SharedMemoryMPI.cc zeMemOpenIpcHandle succeeded for rank "<<r<<std::endl;
std::cout << "SharedMemoryMPI.cc zeMemOpenIpcHandle pointer is "<<std::hex<<thisBuf<<std::dec<<std::endl;
} }
GRID_ASSERT(thisBuf!=nullptr); assert(thisBuf!=nullptr);
} }
#endif #endif
#ifdef GRID_CUDA #ifdef GRID_CUDA
@@ -709,8 +745,8 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags)
void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags)
{ {
std::cout << Mheader "SharedMemoryAllocate "<< bytes<< " MMAP implementation "<< GRID_SHM_PATH <<std::endl; std::cout << Mheader "SharedMemoryAllocate "<< bytes<< " MMAP implementation "<< GRID_SHM_PATH <<std::endl;
GRID_ASSERT(_ShmSetup==1); assert(_ShmSetup==1);
GRID_ASSERT(_ShmAlloc==0); assert(_ShmAlloc==0);
////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////
// allocate the shared windows for our group // allocate the shared windows for our group
////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -740,14 +776,13 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags)
void *ptr = (void *) mmap(NULL, bytes, PROT_READ | PROT_WRITE, mmap_flag,fd, 0); void *ptr = (void *) mmap(NULL, bytes, PROT_READ | PROT_WRITE, mmap_flag,fd, 0);
if ( ptr == (void *)MAP_FAILED ) { if ( ptr == (void *)MAP_FAILED ) {
printf("mmap %s failed\n",shm_name); printf("mmap %s failed\n",shm_name);
perror("failed mmap"); GRID_ASSERT(0); perror("failed mmap"); assert(0);
} }
GRID_ASSERT(((uint64_t)ptr&0x3F)==0); assert(((uint64_t)ptr&0x3F)==0);
close(fd); close(fd);
WorldShmCommBufs[r] =ptr; WorldShmCommBufs[r] =ptr;
// std::cout << Mheader "Set WorldShmCommBufs["<<r<<"]="<<ptr<< "("<< bytes<< "bytes)"<<std::endl; // std::cout << Mheader "Set WorldShmCommBufs["<<r<<"]="<<ptr<< "("<< bytes<< "bytes)"<<std::endl;
} }
std::cout<< Mheader " Intra-node IPC setup is complete "<<std::endl;
_ShmAlloc=1; _ShmAlloc=1;
_ShmAllocBytes = bytes; _ShmAllocBytes = bytes;
}; };
@@ -757,8 +792,8 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags)
void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags)
{ {
std::cout << Mheader "SharedMemoryAllocate "<< bytes<< " MMAP anonymous implementation "<<std::endl; std::cout << Mheader "SharedMemoryAllocate "<< bytes<< " MMAP anonymous implementation "<<std::endl;
GRID_ASSERT(_ShmSetup==1); assert(_ShmSetup==1);
GRID_ASSERT(_ShmAlloc==0); assert(_ShmAlloc==0);
////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////
// allocate the shared windows for our group // allocate the shared windows for our group
////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -769,7 +804,7 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags)
// Hugetlbf and others map filesystems as mappable huge pages // Hugetlbf and others map filesystems as mappable huge pages
//////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////
char shm_name [NAME_MAX]; char shm_name [NAME_MAX];
GRID_ASSERT(WorldShmSize == 1); assert(WorldShmSize == 1);
for(int r=0;r<WorldShmSize;r++){ for(int r=0;r<WorldShmSize;r++){
int fd=-1; int fd=-1;
@@ -783,9 +818,9 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags)
void *ptr = (void *) mmap(NULL, bytes, PROT_READ | PROT_WRITE, mmap_flag,fd, 0); void *ptr = (void *) mmap(NULL, bytes, PROT_READ | PROT_WRITE, mmap_flag,fd, 0);
if ( ptr == (void *)MAP_FAILED ) { if ( ptr == (void *)MAP_FAILED ) {
printf("mmap %s failed\n",shm_name); printf("mmap %s failed\n",shm_name);
perror("failed mmap"); GRID_ASSERT(0); perror("failed mmap"); assert(0);
} }
GRID_ASSERT(((uint64_t)ptr&0x3F)==0); assert(((uint64_t)ptr&0x3F)==0);
close(fd); close(fd);
WorldShmCommBufs[r] =ptr; WorldShmCommBufs[r] =ptr;
// std::cout << "Set WorldShmCommBufs["<<r<<"]="<<ptr<< "("<< bytes<< "bytes)"<<std::endl; // std::cout << "Set WorldShmCommBufs["<<r<<"]="<<ptr<< "("<< bytes<< "bytes)"<<std::endl;
@@ -804,8 +839,8 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags)
void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags)
{ {
std::cout << Mheader "SharedMemoryAllocate "<< bytes<< " SHMOPEN implementation "<<std::endl; std::cout << Mheader "SharedMemoryAllocate "<< bytes<< " SHMOPEN implementation "<<std::endl;
GRID_ASSERT(_ShmSetup==1); assert(_ShmSetup==1);
GRID_ASSERT(_ShmAlloc==0); assert(_ShmAlloc==0);
MPI_Barrier(WorldShmComm); MPI_Barrier(WorldShmComm);
WorldShmCommBufs.resize(WorldShmSize); WorldShmCommBufs.resize(WorldShmSize);
@@ -836,7 +871,7 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags)
perror("failed mmap"); perror("failed mmap");
assert(0); assert(0);
} }
GRID_ASSERT(((uint64_t)ptr&0x3F)==0); assert(((uint64_t)ptr&0x3F)==0);
WorldShmCommBufs[r] =ptr; WorldShmCommBufs[r] =ptr;
close(fd); close(fd);
@@ -857,8 +892,8 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags)
if ( fd<0 ) { perror("failed shm_open"); assert(0); } if ( fd<0 ) { perror("failed shm_open"); assert(0); }
void * ptr = mmap(NULL,size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); void * ptr = mmap(NULL,size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if ( ptr == MAP_FAILED ) { perror("failed mmap"); GRID_ASSERT(0); } if ( ptr == MAP_FAILED ) { perror("failed mmap"); assert(0); }
GRID_ASSERT(((uint64_t)ptr&0x3F)==0); assert(((uint64_t)ptr&0x3F)==0);
WorldShmCommBufs[r] =ptr; WorldShmCommBufs[r] =ptr;
close(fd); close(fd);
@@ -915,7 +950,7 @@ void SharedMemory::SetCommunicator(Grid_MPI_Comm comm)
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Map ShmRank to WorldShmRank and use the right buffer // Map ShmRank to WorldShmRank and use the right buffer
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
GRID_ASSERT (GlobalSharedMemory::ShmAlloc()==1); assert (GlobalSharedMemory::ShmAlloc()==1);
heap_size = GlobalSharedMemory::ShmAllocBytes(); heap_size = GlobalSharedMemory::ShmAllocBytes();
for(int r=0;r<ShmSize;r++){ for(int r=0;r<ShmSize;r++){
@@ -955,7 +990,7 @@ void SharedMemory::SetCommunicator(Grid_MPI_Comm comm)
} }
#endif #endif
// SharedMemoryTest(); SharedMemoryTest();
} }
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
// On node barrier // On node barrier
@@ -983,9 +1018,9 @@ void SharedMemory::SharedMemoryTest(void)
ShmBarrier(); ShmBarrier();
for(uint64_t r=0;r<ShmSize;r++){ for(uint64_t r=0;r<ShmSize;r++){
acceleratorCopyFromDevice(ShmCommBufs[r],check,3*sizeof(uint64_t)); acceleratorCopyFromDevice(ShmCommBufs[r],check,3*sizeof(uint64_t));
GRID_ASSERT(check[0]==GlobalSharedMemory::WorldNode); assert(check[0]==GlobalSharedMemory::WorldNode);
GRID_ASSERT(check[1]==r); assert(check[1]==r);
GRID_ASSERT(check[2]==magic); assert(check[2]==magic);
} }
ShmBarrier(); ShmBarrier();
std::cout << GridLogDebug << " SharedMemoryTest has passed "<<std::endl; std::cout << GridLogDebug << " SharedMemoryTest has passed "<<std::endl;
@@ -1003,14 +1038,12 @@ void *SharedMemory::ShmBuffer(int rank)
void *SharedMemory::ShmBufferTranslate(int rank,void * local_p) void *SharedMemory::ShmBufferTranslate(int rank,void * local_p)
{ {
int gpeer = ShmRanks[rank]; int gpeer = ShmRanks[rank];
GRID_ASSERT(gpeer!=ShmRank); // never send to self assert(gpeer!=ShmRank); // never send to self
// std::cout << "ShmBufferTranslate for rank " << rank<<" peer "<<gpeer<<std::endl;
if (gpeer == MPI_UNDEFINED){ if (gpeer == MPI_UNDEFINED){
return NULL; return NULL;
} else { } else {
uint64_t offset = (uint64_t)local_p - (uint64_t)ShmCommBufs[ShmRank]; uint64_t offset = (uint64_t)local_p - (uint64_t)ShmCommBufs[ShmRank];
uint64_t remote = (uint64_t)ShmCommBufs[gpeer]+offset; uint64_t remote = (uint64_t)ShmCommBufs[gpeer]+offset;
// std::cout << "ShmBufferTranslate : local,offset,remote "<<std::hex<<local_p<<" "<<offset<<" "<<remote<<std::dec<<std::endl;
return (void *) remote; return (void *) remote;
} }
} }
+10 -10
View File
@@ -34,7 +34,7 @@ NAMESPACE_BEGIN(Grid);
/*Construct from an MPI communicator*/ /*Construct from an MPI communicator*/
void GlobalSharedMemory::Init(Grid_MPI_Comm comm) void GlobalSharedMemory::Init(Grid_MPI_Comm comm)
{ {
GRID_ASSERT(_ShmSetup==0); assert(_ShmSetup==0);
WorldComm = 0; WorldComm = 0;
WorldRank = 0; WorldRank = 0;
WorldSize = 1; WorldSize = 1;
@@ -62,8 +62,8 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags)
{ {
std::cout << header "SharedMemoryAllocate "<< bytes<< " GPU implementation "<<std::endl; std::cout << header "SharedMemoryAllocate "<< bytes<< " GPU implementation "<<std::endl;
void * ShmCommBuf ; void * ShmCommBuf ;
GRID_ASSERT(_ShmSetup==1); assert(_ShmSetup==1);
GRID_ASSERT(_ShmAlloc==0); assert(_ShmAlloc==0);
/////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////
// Each MPI rank should allocate our own buffer // Each MPI rank should allocate our own buffer
@@ -92,8 +92,8 @@ void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags)
void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags) void GlobalSharedMemory::SharedMemoryAllocate(uint64_t bytes, int flags)
{ {
void * ShmCommBuf ; void * ShmCommBuf ;
GRID_ASSERT(_ShmSetup==1); assert(_ShmSetup==1);
GRID_ASSERT(_ShmAlloc==0); assert(_ShmAlloc==0);
int mmap_flag =0; int mmap_flag =0;
#ifdef MAP_ANONYMOUS #ifdef MAP_ANONYMOUS
mmap_flag = mmap_flag| MAP_SHARED | MAP_ANONYMOUS; mmap_flag = mmap_flag| MAP_SHARED | MAP_ANONYMOUS;
@@ -122,17 +122,17 @@ void GlobalSharedMemory::SharedMemoryZero(void *dest,size_t bytes)
{ {
acceleratorMemSet(dest,0,bytes); acceleratorMemSet(dest,0,bytes);
} }
//void GlobalSharedMemory::SharedMemoryCopy(void *dest,void *src,size_t bytes) void GlobalSharedMemory::SharedMemoryCopy(void *dest,void *src,size_t bytes)
//{ {
// acceleratorCopyToDevice(src,dest,bytes); acceleratorCopyToDevice(src,dest,bytes);
//} }
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
// Global shared functionality finished // Global shared functionality finished
// Now move to per communicator functionality // Now move to per communicator functionality
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
void SharedMemory::SetCommunicator(Grid_MPI_Comm comm) void SharedMemory::SetCommunicator(Grid_MPI_Comm comm)
{ {
GRID_ASSERT(GlobalSharedMemory::ShmAlloc()==1); assert(GlobalSharedMemory::ShmAlloc()==1);
ShmRanks.resize(1); ShmRanks.resize(1);
ShmCommBufs.resize(1); ShmCommBufs.resize(1);
ShmRanks[0] = 0; ShmRanks[0] = 0;
+5 -5
View File
@@ -202,7 +202,7 @@ template<class vobj> void Scatter_plane_simple (Lattice<vobj> &rhs,deviceVector<
{ {
auto buffer_p = & buffer[0]; auto buffer_p = & buffer[0];
auto table = MapCshiftTable(); auto table = MapCshiftTable();
autoView( rhs_v, rhs, AcceleratorWriteDiscard); autoView( rhs_v, rhs, AcceleratorWrite);
accelerator_for(i,ent,vobj::Nsimd(),{ accelerator_for(i,ent,vobj::Nsimd(),{
coalescedWrite(rhs_v[table[i].first],coalescedRead(buffer_p[table[i].second])); coalescedWrite(rhs_v[table[i].first],coalescedRead(buffer_p[table[i].second]));
}); });
@@ -228,7 +228,7 @@ template<class vobj> void Scatter_plane_merge(Lattice<vobj> &rhs,ExtractPointerA
if(cbmask ==0x3 ) { if(cbmask ==0x3 ) {
int _slice_stride = rhs.Grid()->_slice_stride[dimension]; int _slice_stride = rhs.Grid()->_slice_stride[dimension];
int _slice_block = rhs.Grid()->_slice_block[dimension]; int _slice_block = rhs.Grid()->_slice_block[dimension];
autoView( rhs_v , rhs, AcceleratorWriteDiscard); autoView( rhs_v , rhs, AcceleratorWrite);
accelerator_for(nn,e1*e2,1,{ accelerator_for(nn,e1*e2,1,{
int n = nn%e1; int n = nn%e1;
int b = nn/e1; int b = nn/e1;
@@ -240,9 +240,9 @@ template<class vobj> void Scatter_plane_merge(Lattice<vobj> &rhs,ExtractPointerA
// Case of SIMD split AND checker dim cannot currently be hit, except in // Case of SIMD split AND checker dim cannot currently be hit, except in
// Test_cshift_red_black code. // Test_cshift_red_black code.
std::cout << "Scatter_plane merge GRID_ASSERT(0); think this is buggy FIXME "<< std::endl;// think this is buggy FIXME std::cout << "Scatter_plane merge assert(0); think this is buggy FIXME "<< std::endl;// think this is buggy FIXME
std::cout<<" Unthreaded warning -- buffer is not densely packed ??"<<std::endl; std::cout<<" Unthreaded warning -- buffer is not densely packed ??"<<std::endl;
GRID_ASSERT(0); // This will fail if hit on GPU assert(0); // This will fail if hit on GPU
autoView( rhs_v, rhs, CpuWrite); autoView( rhs_v, rhs, CpuWrite);
for(int n=0;n<e1;n++){ for(int n=0;n<e1;n++){
for(int b=0;b<e2;b++){ for(int b=0;b<e2;b++){
@@ -302,7 +302,7 @@ template<class vobj> void Copy_plane(Lattice<vobj>& lhs,const Lattice<vobj> &rhs
{ {
auto table = MapCshiftTable(); auto table = MapCshiftTable();
autoView(rhs_v , rhs, AcceleratorRead); autoView(rhs_v , rhs, AcceleratorRead);
autoView(lhs_v , lhs, AcceleratorWriteDiscard); autoView(lhs_v , lhs, AcceleratorWrite);
accelerator_for(i,ent,vobj::Nsimd(),{ accelerator_for(i,ent,vobj::Nsimd(),{
coalescedWrite(lhs_v[table[i].first],coalescedRead(rhs_v[table[i].second])); coalescedWrite(lhs_v[table[i].first],coalescedRead(rhs_v[table[i].second]));
}); });
+14 -149
View File
@@ -29,12 +29,8 @@ Author: paboyle <paboyle@ph.ed.ac.uk>
#ifndef _GRID_CSHIFT_MPI_H_ #ifndef _GRID_CSHIFT_MPI_H_
#define _GRID_CSHIFT_MPI_H_ #define _GRID_CSHIFT_MPI_H_
NAMESPACE_BEGIN(Grid); NAMESPACE_BEGIN(Grid);
#ifdef GRID_CHECKSUM_COMMS
extern uint64_t checksum_index;
#endif
const int Cshift_verbose=0; const int Cshift_verbose=0;
template<class vobj> Lattice<vobj> Cshift(const Lattice<vobj> &rhs,int dimension,int shift) template<class vobj> Lattice<vobj> Cshift(const Lattice<vobj> &rhs,int dimension,int shift)
{ {
@@ -49,20 +45,6 @@ template<class vobj> Lattice<vobj> Cshift(const Lattice<vobj> &rhs,int dimension
// Map to always positive shift modulo global full dimension. // Map to always positive shift modulo global full dimension.
shift = (shift+fd)%fd; shift = (shift+fd)%fd;
if( shift ==0 ) {
ret = rhs;
return ret;
}
//
// Potential easy fast cases:
// Shift is a multiple of the local lattice extent.
// Then need only to shift whole subvolumes
int L = rhs.Grid()->_ldimensions[dimension];
if ( (shift%L )==0 && !rhs.Grid()->CheckerBoarded(dimension) ) {
Cshift_simple(ret,rhs,dimension,shift);
return ret;
}
ret.Checkerboard() = rhs.Grid()->CheckerBoardDestination(rhs.Checkerboard(),shift,dimension); ret.Checkerboard() = rhs.Grid()->CheckerBoardDestination(rhs.Checkerboard(),shift,dimension);
// the permute type // the permute type
@@ -87,55 +69,6 @@ template<class vobj> Lattice<vobj> Cshift(const Lattice<vobj> &rhs,int dimension
return ret; return ret;
} }
template<class vobj> void Cshift_simple(Lattice<vobj>& ret,const Lattice<vobj> &rhs,int dimension,int shift)
{
GridBase *grid=rhs.Grid();
int comm_proc, xmit_to_rank, recv_from_rank;
int fd = rhs.Grid()->_fdimensions[dimension];
int rd = rhs.Grid()->_rdimensions[dimension];
int ld = rhs.Grid()->_ldimensions[dimension];
int pd = rhs.Grid()->_processors[dimension];
int simd_layout = rhs.Grid()->_simd_layout[dimension];
int comm_dim = rhs.Grid()->_processors[dimension] >1 ;
comm_proc = ((shift)/ld)%pd;
grid->ShiftedRanks(dimension,comm_proc,xmit_to_rank,recv_from_rank);
if(comm_dim) {
int64_t bytes = sizeof(vobj) * grid->oSites();
autoView(rhs_v , rhs, AcceleratorRead);
autoView(ret_v , ret, AcceleratorWrite);
void *send_buf = (void *)&rhs_v[0];
void *recv_buf = (void *)&ret_v[0];
#ifdef ACCELERATOR_AWARE_MPI
grid->SendToRecvFrom(send_buf,
xmit_to_rank,
recv_buf,
recv_from_rank,
bytes);
#else
static hostVector<vobj> hrhs; hrhs.resize(grid->oSites());
static hostVector<vobj> hret; hret.resize(grid->oSites());
void *hsend_buf = (void *)&hrhs[0];
void *hrecv_buf = (void *)&hret[0];
acceleratorCopyFromDevice(send_buf,hsend_buf,bytes);
grid->SendToRecvFrom(hsend_buf,
xmit_to_rank,
hrecv_buf,
recv_from_rank,
bytes);
acceleratorCopyToDevice(hrecv_buf,recv_buf,bytes);
#endif
}
}
template<class vobj> void Cshift_comms(Lattice<vobj>& ret,const Lattice<vobj> &rhs,int dimension,int shift) template<class vobj> void Cshift_comms(Lattice<vobj>& ret,const Lattice<vobj> &rhs,int dimension,int shift)
{ {
int sshift[2]; int sshift[2];
@@ -184,18 +117,17 @@ template<class vobj> void Cshift_comms(Lattice<vobj> &ret,const Lattice<vobj> &r
int pd = rhs.Grid()->_processors[dimension]; int pd = rhs.Grid()->_processors[dimension];
int simd_layout = rhs.Grid()->_simd_layout[dimension]; int simd_layout = rhs.Grid()->_simd_layout[dimension];
int comm_dim = rhs.Grid()->_processors[dimension] >1 ; int comm_dim = rhs.Grid()->_processors[dimension] >1 ;
GRID_ASSERT(simd_layout==1); assert(simd_layout==1);
GRID_ASSERT(comm_dim==1); assert(comm_dim==1);
GRID_ASSERT(shift>=0); assert(shift>=0);
GRID_ASSERT(shift<fd); assert(shift<fd);
int buffer_size = rhs.Grid()->_slice_nblock[dimension]*rhs.Grid()->_slice_block[dimension]; int buffer_size = rhs.Grid()->_slice_nblock[dimension]*rhs.Grid()->_slice_block[dimension];
static deviceVector<vobj> send_buf; send_buf.resize(buffer_size); static deviceVector<vobj> send_buf; send_buf.resize(buffer_size);
static deviceVector<vobj> recv_buf; recv_buf.resize(buffer_size); static deviceVector<vobj> recv_buf; recv_buf.resize(buffer_size);
#ifndef ACCELERATOR_AWARE_MPI #ifndef ACCELERATOR_AWARE_MPI
int pad = (8 + sizeof(vobj) - 1) / sizeof(vobj); static hostVector<vobj> hsend_buf; hsend_buf.resize(buffer_size);
static hostVector<vobj> hsend_buf; hsend_buf.resize(buffer_size+pad); static hostVector<vobj> hrecv_buf; hrecv_buf.resize(buffer_size);
static hostVector<vobj> hrecv_buf; hrecv_buf.resize(buffer_size+pad);
#endif #endif
int cb= (cbmask==0x2)? Odd : Even; int cb= (cbmask==0x2)? Odd : Even;
@@ -211,11 +143,9 @@ template<class vobj> void Cshift_comms(Lattice<vobj> &ret,const Lattice<vobj> &r
int comm_proc = ((x+sshift)/rd)%pd; int comm_proc = ((x+sshift)/rd)%pd;
if (comm_proc==0) { if (comm_proc==0) {
FlightRecorder::StepLog("Cshift_Copy_plane");
tcopy-=usecond(); tcopy-=usecond();
Copy_plane(ret,rhs,dimension,x,sx,cbmask); Copy_plane(ret,rhs,dimension,x,sx,cbmask);
tcopy+=usecond(); tcopy+=usecond();
FlightRecorder::StepLog("Cshift_Copy_plane_complete");
} else { } else {
int words = buffer_size; int words = buffer_size;
@@ -223,11 +153,9 @@ template<class vobj> void Cshift_comms(Lattice<vobj> &ret,const Lattice<vobj> &r
int bytes = words * sizeof(vobj); int bytes = words * sizeof(vobj);
FlightRecorder::StepLog("Cshift_Gather_plane");
tgather-=usecond(); tgather-=usecond();
Gather_plane_simple (rhs,send_buf,dimension,sx,cbmask); Gather_plane_simple (rhs,send_buf,dimension,sx,cbmask);
tgather+=usecond(); tgather+=usecond();
FlightRecorder::StepLog("Cshift_Gather_plane_complete");
// int rank = grid->_processor; // int rank = grid->_processor;
int recv_from_rank; int recv_from_rank;
@@ -238,7 +166,6 @@ template<class vobj> void Cshift_comms(Lattice<vobj> &ret,const Lattice<vobj> &r
tcomms-=usecond(); tcomms-=usecond();
grid->Barrier(); grid->Barrier();
FlightRecorder::StepLog("Cshift_SendRecv");
#ifdef ACCELERATOR_AWARE_MPI #ifdef ACCELERATOR_AWARE_MPI
grid->SendToRecvFrom((void *)&send_buf[0], grid->SendToRecvFrom((void *)&send_buf[0],
xmit_to_rank, xmit_to_rank,
@@ -248,46 +175,17 @@ template<class vobj> void Cshift_comms(Lattice<vobj> &ret,const Lattice<vobj> &r
#else #else
// bouncy bouncy // bouncy bouncy
acceleratorCopyFromDevice(&send_buf[0],&hsend_buf[0],bytes); acceleratorCopyFromDevice(&send_buf[0],&hsend_buf[0],bytes);
#ifdef GRID_CHECKSUM_COMMS
GRID_ASSERT(bytes % 8 == 0);
checksum_index++;
uint64_t xsum = checksum_gpu((uint64_t*)&send_buf[0], bytes / 8) ^ (1 + checksum_index);
*(uint64_t*)(((char*)&hsend_buf[0]) + bytes) = xsum;
bytes += 8;
#endif
grid->SendToRecvFrom((void *)&hsend_buf[0], grid->SendToRecvFrom((void *)&hsend_buf[0],
xmit_to_rank, xmit_to_rank,
(void *)&hrecv_buf[0], (void *)&hrecv_buf[0],
recv_from_rank, recv_from_rank,
bytes); bytes);
#ifdef GRID_CHECKSUM_COMMS
bytes -= 8;
acceleratorCopyToDevice(&hrecv_buf[0],&recv_buf[0],bytes);
uint64_t expected_cs = *(uint64_t*)(((char*)&hrecv_buf[0]) + bytes);
uint64_t computed_cs = checksum_gpu((uint64_t*)&recv_buf[0], bytes / 8) ^ (1 + checksum_index);
std::cout << GridLogComms<< " Cshift: "
<<" dim"<<dimension
<<" shift "<<shift
<< " rank "<< grid->ThisRank()
<<" Coor "<<grid->ThisProcessorCoor()
<<" send "<<xsum<<" to "<<xmit_to_rank
<<" recv "<<computed_cs<<" from "<<recv_from_rank
<<std::endl;
GRID_ASSERT(expected_cs == computed_cs);
#else
acceleratorCopyToDevice(&hrecv_buf[0],&recv_buf[0],bytes); acceleratorCopyToDevice(&hrecv_buf[0],&recv_buf[0],bytes);
#endif #endif
#endif
FlightRecorder::StepLog("Cshift_SendRecv_complete");
xbytes+=bytes; xbytes+=bytes;
grid->Barrier(); grid->Barrier();
tcomms+=usecond(); tcomms+=usecond();
FlightRecorder::StepLog("Cshift_barrier_complete");
tscatter-=usecond(); tscatter-=usecond();
Scatter_plane_simple (ret,recv_buf,dimension,x,cbmask); Scatter_plane_simple (ret,recv_buf,dimension,x,cbmask);
@@ -322,10 +220,10 @@ template<class vobj> void Cshift_comms_simd(Lattice<vobj> &ret,const Lattice<vo
// << " ld "<<ld<<" pd " << pd<<" simd_layout "<<simd_layout // << " ld "<<ld<<" pd " << pd<<" simd_layout "<<simd_layout
// << " comm_dim " << comm_dim << " cbmask " << cbmask <<std::endl; // << " comm_dim " << comm_dim << " cbmask " << cbmask <<std::endl;
GRID_ASSERT(comm_dim==1); assert(comm_dim==1);
GRID_ASSERT(simd_layout==2); assert(simd_layout==2);
GRID_ASSERT(shift>=0); assert(shift>=0);
GRID_ASSERT(shift<fd); assert(shift<fd);
RealD tcopy=0.0; RealD tcopy=0.0;
RealD tgather=0.0; RealD tgather=0.0;
@@ -351,16 +249,8 @@ template<class vobj> void Cshift_comms_simd(Lattice<vobj> &ret,const Lattice<vo
recv_buf_extract[s].resize(buffer_size); recv_buf_extract[s].resize(buffer_size);
} }
#ifndef ACCELERATOR_AWARE_MPI #ifndef ACCELERATOR_AWARE_MPI
#ifdef GRID_CHECKSUM_COMMS hostVector<scalar_object> hsend_buf; hsend_buf.resize(buffer_size);
buffer_size += (8 + sizeof(vobj) - 1) / sizeof(vobj); hostVector<scalar_object> hrecv_buf; hrecv_buf.resize(buffer_size);
#endif
static hostVector<vobj> hsend_buf; hsend_buf.resize(buffer_size);
static hostVector<vobj> hrecv_buf; hrecv_buf.resize(buffer_size);
#ifdef GRID_CHECKSUM_COMMS
buffer_size -= (8 + sizeof(vobj) - 1) / sizeof(vobj);
#endif
#endif #endif
int bytes = buffer_size*sizeof(scalar_object); int bytes = buffer_size*sizeof(scalar_object);
@@ -404,7 +294,7 @@ template<class vobj> void Cshift_comms_simd(Lattice<vobj> &ret,const Lattice<vo
if (nbr_ic) nbr_lane|=inner_bit; if (nbr_ic) nbr_lane|=inner_bit;
GRID_ASSERT (sx == nbr_ox); assert (sx == nbr_ox);
if(nbr_proc){ if(nbr_proc){
grid->ShiftedRanks(dimension,nbr_proc,xmit_to_rank,recv_from_rank); grid->ShiftedRanks(dimension,nbr_proc,xmit_to_rank,recv_from_rank);
@@ -423,37 +313,12 @@ template<class vobj> void Cshift_comms_simd(Lattice<vobj> &ret,const Lattice<vo
#else #else
// bouncy bouncy // bouncy bouncy
acceleratorCopyFromDevice((void *)send_buf_extract_mpi,(void *)&hsend_buf[0],bytes); acceleratorCopyFromDevice((void *)send_buf_extract_mpi,(void *)&hsend_buf[0],bytes);
#ifdef GRID_CHECKSUM_COMMS
assert(bytes % 8 == 0);
checksum_index++;
uint64_t xsum = checksum_gpu((uint64_t*)send_buf_extract_mpi, bytes / 8) ^ (1 + checksum_index);
*(uint64_t*)(((char*)&hsend_buf[0]) + bytes) = xsum;
bytes += 8;
#endif
grid->SendToRecvFrom((void *)&hsend_buf[0], grid->SendToRecvFrom((void *)&hsend_buf[0],
xmit_to_rank, xmit_to_rank,
(void *)&hrecv_buf[0], (void *)&hrecv_buf[0],
recv_from_rank, recv_from_rank,
bytes); bytes);
#ifdef GRID_CHECKSUM_COMMS
bytes -= 8;
acceleratorCopyToDevice((void *)&hrecv_buf[0],(void *)recv_buf_extract_mpi,bytes); acceleratorCopyToDevice((void *)&hrecv_buf[0],(void *)recv_buf_extract_mpi,bytes);
uint64_t expected_cs = *(uint64_t*)(((char*)&hrecv_buf[0]) + bytes);
uint64_t computed_cs = checksum_gpu((uint64_t*)recv_buf_extract_mpi, bytes / 8) ^ (1 + checksum_index);
std::cout << GridLogComms<< " Cshift_comms_simd: "
<<" dim"<<dimension
<<" shift "<<shift
<< " rank "<< grid->ThisRank()
<<" Coor "<<grid->ThisProcessorCoor()
<<" send "<<xsum<<" to "<<xmit_to_rank
<<" recv "<<computed_cs<<" from "<<recv_from_rank
<<std::endl;
assert(expected_cs == computed_cs);
#else
acceleratorCopyToDevice((void *)&hrecv_buf[0],(void *)recv_buf_extract_mpi,bytes);
#endif
#endif #endif
xbytes+=bytes; xbytes+=bytes;
+2 -2
View File
@@ -21895,7 +21895,7 @@ inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC
/// @brief user-defined string literal for JSON values /// @brief user-defined string literal for JSON values
/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json/ /// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json/
JSON_HEDLEY_NON_NULL(1) JSON_HEDLEY_NON_NULL(1)
inline nlohmann::json operator ""_json(const char* s, std::size_t n) inline nlohmann::json operator "" _json(const char* s, std::size_t n)
{ {
return nlohmann::json::parse(s, s + n); return nlohmann::json::parse(s, s + n);
} }
@@ -21903,7 +21903,7 @@ inline nlohmann::json operator ""_json(const char* s, std::size_t n)
/// @brief user-defined string literal for JSON pointer /// @brief user-defined string literal for JSON pointer
/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json_pointer/ /// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json_pointer/
JSON_HEDLEY_NON_NULL(1) JSON_HEDLEY_NON_NULL(1)
inline nlohmann::json::json_pointer operator ""_json_pointer(const char* s, std::size_t n) inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n)
{ {
return nlohmann::json::json_pointer(std::string(s, n)); return nlohmann::json::json_pointer(std::string(s, n));
} }
+2 -2
View File
@@ -74,7 +74,7 @@ accelerator_inline vobj predicatedWhere(const iobj &predicate,
extract(iftrue, truevals); extract(iftrue, truevals);
extract(iffalse, falsevals); extract(iffalse, falsevals);
extract(TensorRemove(predicate), mask); extract<vInteger, Integer>(TensorRemove(predicate), mask);
for (int s = 0; s < Nsimd; s++) { for (int s = 0; s < Nsimd; s++) {
if (mask[s]) falsevals[s] = truevals[s]; if (mask[s]) falsevals[s] = truevals[s];
@@ -245,7 +245,7 @@ template <class T1,typename std::enable_if<is_lattice<T1>::value, T1>::type * =
inline void CBFromExpression(int &cb, const T1 &lat) // Lattice leaf inline void CBFromExpression(int &cb, const T1 &lat) // Lattice leaf
{ {
if ((cb == Odd) || (cb == Even)) { if ((cb == Odd) || (cb == Even)) {
GRID_ASSERT(cb == lat.Checkerboard()); assert(cb == lat.Checkerboard());
} }
cb = lat.Checkerboard(); cb = lat.Checkerboard();
} }
+16 -16
View File
@@ -120,12 +120,12 @@ public:
GRID_TRACE("ExpressionTemplateEval"); GRID_TRACE("ExpressionTemplateEval");
GridBase *egrid(nullptr); GridBase *egrid(nullptr);
GridFromExpression(egrid,expr); GridFromExpression(egrid,expr);
GRID_ASSERT(egrid!=nullptr); assert(egrid!=nullptr);
conformable(this->_grid,egrid); conformable(this->_grid,egrid);
int cb=-1; int cb=-1;
CBFromExpression(cb,expr); CBFromExpression(cb,expr);
GRID_ASSERT( (cb==Odd) || (cb==Even)); assert( (cb==Odd) || (cb==Even));
this->checkerboard=cb; this->checkerboard=cb;
auto exprCopy = expr; auto exprCopy = expr;
@@ -144,12 +144,12 @@ public:
GRID_TRACE("ExpressionTemplateEval"); GRID_TRACE("ExpressionTemplateEval");
GridBase *egrid(nullptr); GridBase *egrid(nullptr);
GridFromExpression(egrid,expr); GridFromExpression(egrid,expr);
GRID_ASSERT(egrid!=nullptr); assert(egrid!=nullptr);
conformable(this->_grid,egrid); conformable(this->_grid,egrid);
int cb=-1; int cb=-1;
CBFromExpression(cb,expr); CBFromExpression(cb,expr);
GRID_ASSERT( (cb==Odd) || (cb==Even)); assert( (cb==Odd) || (cb==Even));
this->checkerboard=cb; this->checkerboard=cb;
auto exprCopy = expr; auto exprCopy = expr;
@@ -168,12 +168,12 @@ public:
GRID_TRACE("ExpressionTemplateEval"); GRID_TRACE("ExpressionTemplateEval");
GridBase *egrid(nullptr); GridBase *egrid(nullptr);
GridFromExpression(egrid,expr); GridFromExpression(egrid,expr);
GRID_ASSERT(egrid!=nullptr); assert(egrid!=nullptr);
conformable(this->_grid,egrid); conformable(this->_grid,egrid);
int cb=-1; int cb=-1;
CBFromExpression(cb,expr); CBFromExpression(cb,expr);
GRID_ASSERT( (cb==Odd) || (cb==Even)); assert( (cb==Odd) || (cb==Even));
this->checkerboard=cb; this->checkerboard=cb;
auto exprCopy = expr; auto exprCopy = expr;
ExpressionViewOpen(exprCopy); ExpressionViewOpen(exprCopy);
@@ -191,11 +191,11 @@ public:
Lattice(const LatticeUnaryExpression<Op,T1> & expr) { Lattice(const LatticeUnaryExpression<Op,T1> & expr) {
this->_grid = nullptr; this->_grid = nullptr;
GridFromExpression(this->_grid,expr); GridFromExpression(this->_grid,expr);
GRID_ASSERT(this->_grid!=nullptr); assert(this->_grid!=nullptr);
int cb=-1; int cb=-1;
CBFromExpression(cb,expr); CBFromExpression(cb,expr);
GRID_ASSERT( (cb==Odd) || (cb==Even)); assert( (cb==Odd) || (cb==Even));
this->checkerboard=cb; this->checkerboard=cb;
resize(this->_grid->oSites()); resize(this->_grid->oSites());
@@ -206,11 +206,11 @@ public:
Lattice(const LatticeBinaryExpression<Op,T1,T2> & expr) { Lattice(const LatticeBinaryExpression<Op,T1,T2> & expr) {
this->_grid = nullptr; this->_grid = nullptr;
GridFromExpression(this->_grid,expr); GridFromExpression(this->_grid,expr);
GRID_ASSERT(this->_grid!=nullptr); assert(this->_grid!=nullptr);
int cb=-1; int cb=-1;
CBFromExpression(cb,expr); CBFromExpression(cb,expr);
GRID_ASSERT( (cb==Odd) || (cb==Even)); assert( (cb==Odd) || (cb==Even));
this->checkerboard=cb; this->checkerboard=cb;
resize(this->_grid->oSites()); resize(this->_grid->oSites());
@@ -221,11 +221,11 @@ public:
Lattice(const LatticeTrinaryExpression<Op,T1,T2,T3> & expr) { Lattice(const LatticeTrinaryExpression<Op,T1,T2,T3> & expr) {
this->_grid = nullptr; this->_grid = nullptr;
GridFromExpression(this->_grid,expr); GridFromExpression(this->_grid,expr);
GRID_ASSERT(this->_grid!=nullptr); assert(this->_grid!=nullptr);
int cb=-1; int cb=-1;
CBFromExpression(cb,expr); CBFromExpression(cb,expr);
GRID_ASSERT( (cb==Odd) || (cb==Even)); assert( (cb==Odd) || (cb==Even));
this->checkerboard=cb; this->checkerboard=cb;
resize(this->_grid->oSites()); resize(this->_grid->oSites());
@@ -236,7 +236,7 @@ public:
template<class sobj> inline Lattice<vobj> & operator = (const sobj & r){ template<class sobj> inline Lattice<vobj> & operator = (const sobj & r){
vobj vtmp; vobj vtmp;
vtmp = r; vtmp = r;
#if 1 #if 0
deviceVector<vobj> vvtmp(1); deviceVector<vobj> vvtmp(1);
acceleratorPut(vvtmp[0],vtmp); acceleratorPut(vvtmp[0],vtmp);
vobj *vvtmp_p = & vvtmp[0]; vobj *vvtmp_p = & vvtmp[0];
@@ -264,7 +264,7 @@ public:
Lattice(GridBase *grid,ViewMode mode=AcceleratorWriteDiscard) { Lattice(GridBase *grid,ViewMode mode=AcceleratorWriteDiscard) {
this->_grid = grid; this->_grid = grid;
resize(this->_grid->oSites()); resize(this->_grid->oSites());
GRID_ASSERT((((uint64_t)&this->_odata[0])&0xF) ==0); assert((((uint64_t)&this->_odata[0])&0xF) ==0);
this->checkerboard=0; this->checkerboard=0;
SetViewMode(mode); SetViewMode(mode);
} }
@@ -289,7 +289,7 @@ public:
/////////////////////////////////////////// ///////////////////////////////////////////
// move constructor // move constructor
/////////////////////////////////////////// ///////////////////////////////////////////
Lattice(Lattice && r) noexcept { Lattice(Lattice && r){
this->_grid = r.Grid(); this->_grid = r.Grid();
this->_odata = r._odata; this->_odata = r._odata;
this->_odata_size = r._odata_size; this->_odata_size = r._odata_size;
@@ -330,7 +330,7 @@ public:
/////////////////////////////////////////// ///////////////////////////////////////////
// Move assignment possible if same type // Move assignment possible if same type
/////////////////////////////////////////// ///////////////////////////////////////////
inline Lattice<vobj> & operator = (Lattice<vobj> && r) noexcept { inline Lattice<vobj> & operator = (Lattice<vobj> && r){
resize(0); // deletes if appropriate resize(0); // deletes if appropriate
this->_grid = r.Grid(); this->_grid = r.Grid();
+5 -5
View File
@@ -166,9 +166,9 @@ void basisReorderInPlace(std::vector<Field> &_v,std::vector<RealD>& sort_vals, s
{ {
int vlen = idx.size(); int vlen = idx.size();
GRID_ASSERT(vlen>=1); assert(vlen>=1);
GRID_ASSERT(vlen<=sort_vals.size()); assert(vlen<=sort_vals.size());
GRID_ASSERT(vlen<=_v.size()); assert(vlen<=_v.size());
for (size_t i=0;i<vlen;i++) { for (size_t i=0;i<vlen;i++) {
@@ -186,7 +186,7 @@ void basisReorderInPlace(std::vector<Field> &_v,std::vector<RealD>& sort_vals, s
if (idx[j]==i) if (idx[j]==i)
break; break;
GRID_ASSERT(idx[i] > i); GRID_ASSERT(j!=idx.size()); GRID_ASSERT(idx[j]==i); assert(idx[i] > i); assert(j!=idx.size()); assert(idx[j]==i);
swap(_v[i],_v[idx[i]]); // should use vector move constructor, no data copy swap(_v[i],_v[idx[i]]); // should use vector move constructor, no data copy
std::swap(sort_vals[i],sort_vals[idx[i]]); std::swap(sort_vals[i],sort_vals[idx[i]]);
@@ -224,7 +224,7 @@ void basisSortInPlace(std::vector<Field> & _v,std::vector<RealD>& sort_vals, boo
template<class Field> template<class Field>
void basisDeflate(const std::vector<Field> &_v,const std::vector<RealD>& eval,const Field& src_orig,Field& result) { void basisDeflate(const std::vector<Field> &_v,const std::vector<RealD>& eval,const Field& src_orig,Field& result) {
result = Zero(); result = Zero();
GRID_ASSERT(_v.size()==eval.size()); assert(_v.size()==eval.size());
int N = (int)_v.size(); int N = (int)_v.size();
for (int i=0;i<N;i++) { for (int i=0;i<N;i++) {
Field& tmp = _v[i]; Field& tmp = _v[i];
+25 -25
View File
@@ -40,16 +40,16 @@ NAMESPACE_BEGIN(Grid);
//Query supporting logical &&, ||, //Query supporting logical &&, ||,
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
template<class vobj> using vPredicate = iScalar<IntegerPredicate<vobj> > ; typedef iScalar<vInteger> vPredicate ;
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// compare lattice to lattice // compare lattice to lattice
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
template<class vfunctor,class lobj,class robj> template<class vfunctor,class lobj,class robj>
inline Lattice<vPredicate<lobj> > LLComparison(vfunctor op,const Lattice<lobj> &lhs,const Lattice<robj> &rhs) inline Lattice<vPredicate> LLComparison(vfunctor op,const Lattice<lobj> &lhs,const Lattice<robj> &rhs)
{ {
Lattice<vPredicate<lobj> > ret(rhs.Grid()); Lattice<vPredicate> ret(rhs.Grid());
autoView( lhs_v, lhs, CpuRead); autoView( lhs_v, lhs, CpuRead);
autoView( rhs_v, rhs, CpuRead); autoView( rhs_v, rhs, CpuRead);
autoView( ret_v, ret, CpuWrite); autoView( ret_v, ret, CpuWrite);
@@ -62,9 +62,9 @@ inline Lattice<vPredicate<lobj> > LLComparison(vfunctor op,const Lattice<lobj> &
// compare lattice to scalar // compare lattice to scalar
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
template<class vfunctor,class lobj,class robj> template<class vfunctor,class lobj,class robj>
inline Lattice<vPredicate<lobj> > LSComparison(vfunctor op,const Lattice<lobj> &lhs,const robj &rhs) inline Lattice<vPredicate> LSComparison(vfunctor op,const Lattice<lobj> &lhs,const robj &rhs)
{ {
Lattice<vPredicate<lobj> > ret(lhs.Grid()); Lattice<vPredicate> ret(lhs.Grid());
autoView( lhs_v, lhs, CpuRead); autoView( lhs_v, lhs, CpuRead);
autoView( ret_v, ret, CpuWrite); autoView( ret_v, ret, CpuWrite);
thread_for( ss, lhs_v.size(), { thread_for( ss, lhs_v.size(), {
@@ -76,9 +76,9 @@ inline Lattice<vPredicate<lobj> > LSComparison(vfunctor op,const Lattice<lobj> &
// compare scalar to lattice // compare scalar to lattice
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
template<class vfunctor,class lobj,class robj> template<class vfunctor,class lobj,class robj>
inline Lattice<vPredicate<robj> > SLComparison(vfunctor op,const lobj &lhs,const Lattice<robj> &rhs) inline Lattice<vPredicate> SLComparison(vfunctor op,const lobj &lhs,const Lattice<robj> &rhs)
{ {
Lattice<vPredicate<robj> > ret(rhs.Grid()); Lattice<vPredicate> ret(rhs.Grid());
autoView( rhs_v, rhs, CpuRead); autoView( rhs_v, rhs, CpuRead);
autoView( ret_v, ret, CpuWrite); autoView( ret_v, ret, CpuWrite);
thread_for( ss, rhs_v.size(), { thread_for( ss, rhs_v.size(), {
@@ -92,87 +92,87 @@ inline Lattice<vPredicate<robj> > SLComparison(vfunctor op,const lobj &lhs,const
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// Less than // Less than
template<class lobj,class robj> template<class lobj,class robj>
inline Lattice<vPredicate<lobj> > operator < (const Lattice<lobj> & lhs, const Lattice<robj> & rhs) { inline Lattice<vPredicate> operator < (const Lattice<lobj> & lhs, const Lattice<robj> & rhs) {
return LLComparison(vlt<lobj,robj>(),lhs,rhs); return LLComparison(vlt<lobj,robj>(),lhs,rhs);
} }
template<class lobj,class robj> template<class lobj,class robj>
inline Lattice<vPredicate<lobj> > operator < (const Lattice<lobj> & lhs, const robj & rhs) { inline Lattice<vPredicate> operator < (const Lattice<lobj> & lhs, const robj & rhs) {
return LSComparison(vlt<lobj,robj>(),lhs,rhs); return LSComparison(vlt<lobj,robj>(),lhs,rhs);
} }
template<class lobj,class robj> template<class lobj,class robj>
inline Lattice<vPredicate<robj> > operator < (const lobj & lhs, const Lattice<robj> & rhs) { inline Lattice<vPredicate> operator < (const lobj & lhs, const Lattice<robj> & rhs) {
return SLComparison(vlt<lobj,robj>(),lhs,rhs); return SLComparison(vlt<lobj,robj>(),lhs,rhs);
} }
// Less than equal // Less than equal
template<class lobj,class robj> template<class lobj,class robj>
inline Lattice<vPredicate<lobj> > operator <= (const Lattice<lobj> & lhs, const Lattice<robj> & rhs) { inline Lattice<vPredicate> operator <= (const Lattice<lobj> & lhs, const Lattice<robj> & rhs) {
return LLComparison(vle<lobj,robj>(),lhs,rhs); return LLComparison(vle<lobj,robj>(),lhs,rhs);
} }
template<class lobj,class robj> template<class lobj,class robj>
inline Lattice<vPredicate<lobj> > operator <= (const Lattice<lobj> & lhs, const robj & rhs) { inline Lattice<vPredicate> operator <= (const Lattice<lobj> & lhs, const robj & rhs) {
return LSComparison(vle<lobj,robj>(),lhs,rhs); return LSComparison(vle<lobj,robj>(),lhs,rhs);
} }
template<class lobj,class robj> template<class lobj,class robj>
inline Lattice<vPredicate<robj> > operator <= (const lobj & lhs, const Lattice<robj> & rhs) { inline Lattice<vPredicate> operator <= (const lobj & lhs, const Lattice<robj> & rhs) {
return SLComparison(vle<lobj,robj>(),lhs,rhs); return SLComparison(vle<lobj,robj>(),lhs,rhs);
} }
// Greater than // Greater than
template<class lobj,class robj> template<class lobj,class robj>
inline Lattice<vPredicate<lobj> > operator > (const Lattice<lobj> & lhs, const Lattice<robj> & rhs) { inline Lattice<vPredicate> operator > (const Lattice<lobj> & lhs, const Lattice<robj> & rhs) {
return LLComparison(vgt<lobj,robj>(),lhs,rhs); return LLComparison(vgt<lobj,robj>(),lhs,rhs);
} }
template<class lobj,class robj> template<class lobj,class robj>
inline Lattice<vPredicate<lobj> > operator > (const Lattice<lobj> & lhs, const robj & rhs) { inline Lattice<vPredicate> operator > (const Lattice<lobj> & lhs, const robj & rhs) {
return LSComparison(vgt<lobj,robj>(),lhs,rhs); return LSComparison(vgt<lobj,robj>(),lhs,rhs);
} }
template<class lobj,class robj> template<class lobj,class robj>
inline Lattice<vPredicate<robj> > operator > (const lobj & lhs, const Lattice<robj> & rhs) { inline Lattice<vPredicate> operator > (const lobj & lhs, const Lattice<robj> & rhs) {
return SLComparison(vgt<lobj,robj>(),lhs,rhs); return SLComparison(vgt<lobj,robj>(),lhs,rhs);
} }
// Greater than equal // Greater than equal
template<class lobj,class robj> template<class lobj,class robj>
inline Lattice<vPredicate<lobj> > operator >= (const Lattice<lobj> & lhs, const Lattice<robj> & rhs) { inline Lattice<vPredicate> operator >= (const Lattice<lobj> & lhs, const Lattice<robj> & rhs) {
return LLComparison(vge<lobj,robj>(),lhs,rhs); return LLComparison(vge<lobj,robj>(),lhs,rhs);
} }
template<class lobj,class robj> template<class lobj,class robj>
inline Lattice<vPredicate<lobj> > operator >= (const Lattice<lobj> & lhs, const robj & rhs) { inline Lattice<vPredicate> operator >= (const Lattice<lobj> & lhs, const robj & rhs) {
return LSComparison(vge<lobj,robj>(),lhs,rhs); return LSComparison(vge<lobj,robj>(),lhs,rhs);
} }
template<class lobj,class robj> template<class lobj,class robj>
inline Lattice<vPredicate<robj> > operator >= (const lobj & lhs, const Lattice<robj> & rhs) { inline Lattice<vPredicate> operator >= (const lobj & lhs, const Lattice<robj> & rhs) {
return SLComparison(vge<lobj,robj>(),lhs,rhs); return SLComparison(vge<lobj,robj>(),lhs,rhs);
} }
// equal // equal
template<class lobj,class robj> template<class lobj,class robj>
inline Lattice<vPredicate<lobj> > operator == (const Lattice<lobj> & lhs, const Lattice<robj> & rhs) { inline Lattice<vPredicate> operator == (const Lattice<lobj> & lhs, const Lattice<robj> & rhs) {
return LLComparison(veq<lobj,robj>(),lhs,rhs); return LLComparison(veq<lobj,robj>(),lhs,rhs);
} }
template<class lobj,class robj> template<class lobj,class robj>
inline Lattice<vPredicate<lobj> > operator == (const Lattice<lobj> & lhs, const robj & rhs) { inline Lattice<vPredicate> operator == (const Lattice<lobj> & lhs, const robj & rhs) {
return LSComparison(veq<lobj,robj>(),lhs,rhs); return LSComparison(veq<lobj,robj>(),lhs,rhs);
} }
template<class lobj,class robj> template<class lobj,class robj>
inline Lattice<vPredicate<robj> > operator == (const lobj & lhs, const Lattice<robj> & rhs) { inline Lattice<vPredicate> operator == (const lobj & lhs, const Lattice<robj> & rhs) {
return SLComparison(veq<lobj,robj>(),lhs,rhs); return SLComparison(veq<lobj,robj>(),lhs,rhs);
} }
// not equal // not equal
template<class lobj,class robj> template<class lobj,class robj>
inline Lattice<vPredicate<lobj> > operator != (const Lattice<lobj> & lhs, const Lattice<robj> & rhs) { inline Lattice<vPredicate> operator != (const Lattice<lobj> & lhs, const Lattice<robj> & rhs) {
return LLComparison(vne<lobj,robj>(),lhs,rhs); return LLComparison(vne<lobj,robj>(),lhs,rhs);
} }
template<class lobj,class robj> template<class lobj,class robj>
inline Lattice<vPredicate<lobj> > operator != (const Lattice<lobj> & lhs, const robj & rhs) { inline Lattice<vPredicate> operator != (const Lattice<lobj> & lhs, const robj & rhs) {
return LSComparison(vne<lobj,robj>(),lhs,rhs); return LSComparison(vne<lobj,robj>(),lhs,rhs);
} }
template<class lobj,class robj> template<class lobj,class robj>
inline Lattice<vPredicate<robj> > operator != (const lobj & lhs, const Lattice<robj> & rhs) { inline Lattice<vPredicate> operator != (const lobj & lhs, const Lattice<robj> & rhs) {
return SLComparison(vne<lobj,robj>(),lhs,rhs); return SLComparison(vne<lobj,robj>(),lhs,rhs);
} }
NAMESPACE_END(Grid); NAMESPACE_END(Grid);
+21 -27
View File
@@ -39,53 +39,47 @@ NAMESPACE_BEGIN(Grid);
// //
// Should guard this with isGridTensor<> enable if? // Should guard this with isGridTensor<> enable if?
///////////////////////////////////////// /////////////////////////////////////////
//
// Predicate type matching the operand's vectorisation
//
template<class obj> using IntegerPredicate =
typename GridTypeMapper<typename GridTypeMapper<obj>::vector_type>::Integerified;
// //
// Generic list of functors // Generic list of functors
// //
template<class lobj,class robj> class veq { template<class lobj,class robj> class veq {
public: public:
accelerator IntegerPredicate<lobj> operator()(const lobj &lhs, const robj &rhs) accelerator vInteger operator()(const lobj &lhs, const robj &rhs)
{ {
return (lhs) == (rhs); return (lhs) == (rhs);
} }
}; };
template<class lobj,class robj> class vne { template<class lobj,class robj> class vne {
public: public:
accelerator IntegerPredicate<lobj> operator()(const lobj &lhs, const robj &rhs) accelerator vInteger operator()(const lobj &lhs, const robj &rhs)
{ {
return (lhs) != (rhs); return (lhs) != (rhs);
} }
}; };
template<class lobj,class robj> class vlt { template<class lobj,class robj> class vlt {
public: public:
accelerator IntegerPredicate<lobj> operator()(const lobj &lhs, const robj &rhs) accelerator vInteger operator()(const lobj &lhs, const robj &rhs)
{ {
return (lhs) < (rhs); return (lhs) < (rhs);
} }
}; };
template<class lobj,class robj> class vle { template<class lobj,class robj> class vle {
public: public:
accelerator IntegerPredicate<lobj> operator()(const lobj &lhs, const robj &rhs) accelerator vInteger operator()(const lobj &lhs, const robj &rhs)
{ {
return (lhs) <= (rhs); return (lhs) <= (rhs);
} }
}; };
template<class lobj,class robj> class vgt { template<class lobj,class robj> class vgt {
public: public:
accelerator IntegerPredicate<lobj> operator()(const lobj &lhs, const robj &rhs) accelerator vInteger operator()(const lobj &lhs, const robj &rhs)
{ {
return (lhs) > (rhs); return (lhs) > (rhs);
} }
}; };
template<class lobj,class robj> class vge { template<class lobj,class robj> class vge {
public: public:
accelerator IntegerPredicate<lobj> operator()(const lobj &lhs, const robj &rhs) accelerator vInteger operator()(const lobj &lhs, const robj &rhs)
{ {
return (lhs) >= (rhs); return (lhs) >= (rhs);
} }
@@ -139,78 +133,78 @@ NAMESPACE_BEGIN(Grid);
// Integer and real get extra relational functions. // Integer and real get extra relational functions.
////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////
template<class sfunctor, class vsimd,IfNotComplex<vsimd> = 0> template<class sfunctor, class vsimd,IfNotComplex<vsimd> = 0>
accelerator_inline IntegerPredicate<vsimd> Comparison(sfunctor sop,const vsimd & lhs, const vsimd & rhs) accelerator_inline vInteger Comparison(sfunctor sop,const vsimd & lhs, const vsimd & rhs)
{ {
typedef typename vsimd::scalar_type scalar; typedef typename vsimd::scalar_type scalar;
ExtractBuffer<scalar> vlhs(vsimd::Nsimd()); // Use functors to reduce this to single implementation ExtractBuffer<scalar> vlhs(vsimd::Nsimd()); // Use functors to reduce this to single implementation
ExtractBuffer<scalar> vrhs(vsimd::Nsimd()); ExtractBuffer<scalar> vrhs(vsimd::Nsimd());
ExtractBuffer<Integer> vpred(vsimd::Nsimd()); ExtractBuffer<Integer> vpred(vsimd::Nsimd());
IntegerPredicate<vsimd> ret; vInteger ret;
extract<vsimd,scalar>(lhs,vlhs); extract<vsimd,scalar>(lhs,vlhs);
extract<vsimd,scalar>(rhs,vrhs); extract<vsimd,scalar>(rhs,vrhs);
for(int s=0;s<vsimd::Nsimd();s++){ for(int s=0;s<vsimd::Nsimd();s++){
vpred[s] = sop(vlhs[s],vrhs[s]); vpred[s] = sop(vlhs[s],vrhs[s]);
} }
merge<IntegerPredicate<vsimd>,Integer>(ret,vpred); merge<vInteger,Integer>(ret,vpred);
return ret; return ret;
} }
template<class sfunctor, class vsimd,IfNotComplex<vsimd> = 0> template<class sfunctor, class vsimd,IfNotComplex<vsimd> = 0>
accelerator_inline IntegerPredicate<vsimd> Comparison(sfunctor sop,const vsimd & lhs, const typename vsimd::scalar_type & rhs) accelerator_inline vInteger Comparison(sfunctor sop,const vsimd & lhs, const typename vsimd::scalar_type & rhs)
{ {
typedef typename vsimd::scalar_type scalar; typedef typename vsimd::scalar_type scalar;
ExtractBuffer<scalar> vlhs(vsimd::Nsimd()); // Use functors to reduce this to single implementation ExtractBuffer<scalar> vlhs(vsimd::Nsimd()); // Use functors to reduce this to single implementation
ExtractBuffer<Integer> vpred(vsimd::Nsimd()); ExtractBuffer<Integer> vpred(vsimd::Nsimd());
IntegerPredicate<vsimd> ret; vInteger ret;
extract<vsimd,scalar>(lhs,vlhs); extract<vsimd,scalar>(lhs,vlhs);
for(int s=0;s<vsimd::Nsimd();s++){ for(int s=0;s<vsimd::Nsimd();s++){
vpred[s] = sop(vlhs[s],rhs); vpred[s] = sop(vlhs[s],rhs);
} }
merge<IntegerPredicate<vsimd>,Integer>(ret,vpred); merge<vInteger,Integer>(ret,vpred);
return ret; return ret;
} }
template<class sfunctor, class vsimd,IfNotComplex<vsimd> = 0> template<class sfunctor, class vsimd,IfNotComplex<vsimd> = 0>
accelerator_inline IntegerPredicate<vsimd> Comparison(sfunctor sop,const typename vsimd::scalar_type & lhs, const vsimd & rhs) accelerator_inline vInteger Comparison(sfunctor sop,const typename vsimd::scalar_type & lhs, const vsimd & rhs)
{ {
typedef typename vsimd::scalar_type scalar; typedef typename vsimd::scalar_type scalar;
ExtractBuffer<scalar> vrhs(vsimd::Nsimd()); // Use functors to reduce this to single implementation ExtractBuffer<scalar> vrhs(vsimd::Nsimd()); // Use functors to reduce this to single implementation
ExtractBuffer<Integer> vpred(vsimd::Nsimd()); ExtractBuffer<Integer> vpred(vsimd::Nsimd());
IntegerPredicate<vsimd> ret; vInteger ret;
extract<vsimd,scalar>(rhs,vrhs); extract<vsimd,scalar>(rhs,vrhs);
for(int s=0;s<vsimd::Nsimd();s++){ for(int s=0;s<vsimd::Nsimd();s++){
vpred[s] = sop(lhs,vrhs[s]); vpred[s] = sop(lhs,vrhs[s]);
} }
merge<IntegerPredicate<vsimd>,Integer>(ret,vpred); merge<vInteger,Integer>(ret,vpred);
return ret; return ret;
} }
#define DECLARE_RELATIONAL_EQ(op,functor) \ #define DECLARE_RELATIONAL_EQ(op,functor) \
template<class vsimd,IfSimd<vsimd> = 0>\ template<class vsimd,IfSimd<vsimd> = 0>\
accelerator_inline IntegerPredicate<vsimd> operator op (const vsimd & lhs, const vsimd & rhs)\ accelerator_inline vInteger operator op (const vsimd & lhs, const vsimd & rhs)\
{\ {\
typedef typename vsimd::scalar_type scalar;\ typedef typename vsimd::scalar_type scalar;\
return Comparison(functor<scalar,scalar>(),lhs,rhs);\ return Comparison(functor<scalar,scalar>(),lhs,rhs);\
}\ }\
template<class vsimd,IfSimd<vsimd> = 0>\ template<class vsimd,IfSimd<vsimd> = 0>\
accelerator_inline IntegerPredicate<vsimd> operator op (const vsimd & lhs, const typename vsimd::scalar_type & rhs) \ accelerator_inline vInteger operator op (const vsimd & lhs, const typename vsimd::scalar_type & rhs) \
{\ {\
typedef typename vsimd::scalar_type scalar;\ typedef typename vsimd::scalar_type scalar;\
return Comparison(functor<scalar,scalar>(),lhs,rhs);\ return Comparison(functor<scalar,scalar>(),lhs,rhs);\
}\ }\
template<class vsimd,IfSimd<vsimd> = 0>\ template<class vsimd,IfSimd<vsimd> = 0>\
accelerator_inline IntegerPredicate<vsimd> operator op (const typename vsimd::scalar_type & lhs, const vsimd & rhs) \ accelerator_inline vInteger operator op (const typename vsimd::scalar_type & lhs, const vsimd & rhs) \
{\ {\
typedef typename vsimd::scalar_type scalar;\ typedef typename vsimd::scalar_type scalar;\
return Comparison(functor<scalar,scalar>(),lhs,rhs);\ return Comparison(functor<scalar,scalar>(),lhs,rhs);\
}\ }\
template<class vsimd>\ template<class vsimd>\
accelerator_inline IntegerPredicate<vsimd> operator op(const iScalar<vsimd> &lhs,const typename vsimd::scalar_type &rhs) \ accelerator_inline vInteger operator op(const iScalar<vsimd> &lhs,const typename vsimd::scalar_type &rhs) \
{ \ { \
return lhs._internal op rhs; \ return lhs._internal op rhs; \
} \ } \
template<class vsimd>\ template<class vsimd>\
accelerator_inline IntegerPredicate<vsimd> operator op(const typename vsimd::scalar_type &lhs,const iScalar<vsimd> &rhs) \ accelerator_inline vInteger operator op(const typename vsimd::scalar_type &lhs,const iScalar<vsimd> &rhs) \
{ \ { \
return lhs op rhs._internal; \ return lhs op rhs._internal; \
} \ } \
@@ -218,7 +212,7 @@ NAMESPACE_BEGIN(Grid);
#define DECLARE_RELATIONAL(op,functor) \ #define DECLARE_RELATIONAL(op,functor) \
DECLARE_RELATIONAL_EQ(op,functor) \ DECLARE_RELATIONAL_EQ(op,functor) \
template<class vsimd>\ template<class vsimd>\
accelerator_inline IntegerPredicate<vsimd> operator op(const iScalar<vsimd> &lhs,const iScalar<vsimd> &rhs)\ accelerator_inline vInteger operator op(const iScalar<vsimd> &lhs,const iScalar<vsimd> &rhs)\
{ \ { \
return lhs._internal op rhs._internal; \ return lhs._internal op rhs._internal; \
} }
+2 -2
View File
@@ -32,8 +32,8 @@ NAMESPACE_BEGIN(Grid);
template<class obj1,class obj2> void conformable(const Lattice<obj1> &lhs,const Lattice<obj2> &rhs) template<class obj1,class obj2> void conformable(const Lattice<obj1> &lhs,const Lattice<obj2> &rhs)
{ {
GRID_ASSERT(lhs.Grid() == rhs.Grid()); assert(lhs.Grid() == rhs.Grid());
GRID_ASSERT(lhs.Checkerboard() == rhs.Checkerboard()); assert(lhs.Checkerboard() == rhs.Checkerboard());
} }
NAMESPACE_END(Grid); NAMESPACE_END(Grid);
+3 -3
View File
@@ -42,7 +42,7 @@ static void sliceMaddMatrix (Lattice<vobj> &R,Eigen::MatrixXcd &aa,const Lattice
// Lattice<vobj> Xslice(SliceGrid); // Lattice<vobj> Xslice(SliceGrid);
// Lattice<vobj> Rslice(SliceGrid); // Lattice<vobj> Rslice(SliceGrid);
GRID_ASSERT( FullGrid->_simd_layout[Orthog]==1); assert( FullGrid->_simd_layout[Orthog]==1);
//FIXME package in a convenient iterator //FIXME package in a convenient iterator
//Should loop over a plane orthogonal to direction "Orthog" //Should loop over a plane orthogonal to direction "Orthog"
@@ -86,7 +86,7 @@ static void sliceMulMatrix (Lattice<vobj> &R,Eigen::MatrixXcd &aa,const Lattice<
int Nblock = X.Grid()->GlobalDimensions()[Orthog]; int Nblock = X.Grid()->GlobalDimensions()[Orthog];
GridBase *FullGrid = X.Grid(); GridBase *FullGrid = X.Grid();
GRID_ASSERT( FullGrid->_simd_layout[Orthog]==1); assert( FullGrid->_simd_layout[Orthog]==1);
//FIXME package in a convenient iterator //FIXME package in a convenient iterator
//Should loop over a plane orthogonal to direction "Orthog" //Should loop over a plane orthogonal to direction "Orthog"
@@ -140,7 +140,7 @@ static void sliceInnerProductMatrix( Eigen::MatrixXcd &mat, const Lattice<vobj>
mat = Eigen::MatrixXcd::Zero(Nblock,Nblock); mat = Eigen::MatrixXcd::Zero(Nblock,Nblock);
GRID_ASSERT( FullGrid->_simd_layout[Orthog]==1); assert( FullGrid->_simd_layout[Orthog]==1);
// int nh = FullGrid->_ndimension; // int nh = FullGrid->_ndimension;
// int nl = SliceGrid->_ndimension; // int nl = SliceGrid->_ndimension;
// int nl = nh-1; // int nl = nh-1;
+9 -9
View File
@@ -98,8 +98,8 @@ void pokeSite(const sobj &s,Lattice<vobj> &l,const Coordinate &site){
int Nsimd = grid->Nsimd(); int Nsimd = grid->Nsimd();
GRID_ASSERT( l.Checkerboard()== l.Grid()->CheckerBoard(site)); assert( l.Checkerboard()== l.Grid()->CheckerBoard(site));
GRID_ASSERT( sizeof(sobj)*Nsimd == sizeof(vobj)); assert( sizeof(sobj)*Nsimd == sizeof(vobj));
int rank,odx,idx; int rank,odx,idx;
// Optional to broadcast from node 0. // Optional to broadcast from node 0.
@@ -135,7 +135,7 @@ void peekSite(sobj &s,const Lattice<vobj> &l,const Coordinate &site){
int Nsimd = grid->Nsimd(); int Nsimd = grid->Nsimd();
GRID_ASSERT( l.Checkerboard() == l.Grid()->CheckerBoard(site)); assert( l.Checkerboard() == l.Grid()->CheckerBoard(site));
int rank,odx,idx; int rank,odx,idx;
grid->GlobalCoorToRankIndex(rank,odx,idx,site); grid->GlobalCoorToRankIndex(rank,odx,idx,site);
@@ -159,14 +159,14 @@ template<class vobj,class sobj>
inline void peekLocalSite(sobj &s,const LatticeView<vobj> &l,Coordinate &site) inline void peekLocalSite(sobj &s,const LatticeView<vobj> &l,Coordinate &site)
{ {
GridBase *grid = l.getGrid(); GridBase *grid = l.getGrid();
GRID_ASSERT(l.mode==CpuRead); assert(l.mode==CpuRead);
typedef typename vobj::scalar_type scalar_type; typedef typename vobj::scalar_type scalar_type;
typedef typename vobj::vector_type vector_type; typedef typename vobj::vector_type vector_type;
int Nsimd = grid->Nsimd(); int Nsimd = grid->Nsimd();
// GRID_ASSERT( l.Checkerboard()== grid->CheckerBoard(site)); // assert( l.Checkerboard()== grid->CheckerBoard(site));
GRID_ASSERT( sizeof(sobj)*Nsimd == sizeof(vobj)); assert( sizeof(sobj)*Nsimd == sizeof(vobj));
static const int words=sizeof(vobj)/sizeof(vector_type); static const int words=sizeof(vobj)/sizeof(vector_type);
int odx,idx; int odx,idx;
@@ -195,15 +195,15 @@ template<class vobj,class sobj>
inline void pokeLocalSite(const sobj &s,LatticeView<vobj> &l,Coordinate &site) inline void pokeLocalSite(const sobj &s,LatticeView<vobj> &l,Coordinate &site)
{ {
GridBase *grid=l.getGrid(); GridBase *grid=l.getGrid();
GRID_ASSERT(l.mode==CpuWrite); assert(l.mode==CpuWrite);
typedef typename vobj::scalar_type scalar_type; typedef typename vobj::scalar_type scalar_type;
typedef typename vobj::vector_type vector_type; typedef typename vobj::vector_type vector_type;
int Nsimd = grid->Nsimd(); int Nsimd = grid->Nsimd();
// GRID_ASSERT( l.Checkerboard()== grid->CheckerBoard(site)); // assert( l.Checkerboard()== grid->CheckerBoard(site));
GRID_ASSERT( sizeof(sobj)*Nsimd == sizeof(vobj)); assert( sizeof(sobj)*Nsimd == sizeof(vobj));
static const int words=sizeof(vobj)/sizeof(vector_type); static const int words=sizeof(vobj)/sizeof(vector_type);
int odx,idx; int odx,idx;
+61 -212
View File
@@ -292,26 +292,26 @@ inline ComplexD innerProduct(const Lattice<vobj> &left,const Lattice<vobj> &righ
bool ok; bool ok;
#ifdef GRID_SYCL #ifdef GRID_SYCL
// uint64_t csum=0; uint64_t csum=0;
// uint64_t csum2=0; uint64_t csum2=0;
// if ( FlightRecorder::LoggingMode != FlightRecorder::LoggingModeNone) if ( FlightRecorder::LoggingMode != FlightRecorder::LoggingModeNone)
// { {
// Hack // Hack
// Fast integer xor checksum. Can also be used in comms now. // Fast integer xor checksum. Can also be used in comms now.
// autoView(l_v,left,AcceleratorRead); autoView(l_v,left,AcceleratorRead);
// Integer words = left.Grid()->oSites()*sizeof(vobj)/sizeof(uint64_t); Integer words = left.Grid()->oSites()*sizeof(vobj)/sizeof(uint64_t);
// uint64_t *base= (uint64_t *)&l_v[0]; uint64_t *base= (uint64_t *)&l_v[0];
// csum=svm_xor(base,words); csum=svm_xor(base,words);
// ok = FlightRecorder::CsumLog(csum); ok = FlightRecorder::CsumLog(csum);
// if ( !ok ) { if ( !ok ) {
// csum2=svm_xor(base,words); csum2=svm_xor(base,words);
// std::cerr<< " Bad CSUM " << std::hex<< csum << " recomputed as "<<csum2<<std::dec<<std::endl; std::cerr<< " Bad CSUM " << std::hex<< csum << " recomputed as "<<csum2<<std::dec<<std::endl;
// } else { } else {
// csum2=svm_xor(base,words); // csum2=svm_xor(base,words);
// std::cerr<< " ok CSUM " << std::hex<< csum << " recomputed as "<<csum2<<std::dec<<std::endl; // std::cerr<< " ok CSUM " << std::hex<< csum << " recomputed as "<<csum2<<std::dec<<std::endl;
// } }
// GRID_ASSERT(ok); assert(ok);
// } }
#endif #endif
FlightRecorder::StepLog("rank inner product"); FlightRecorder::StepLog("rank inner product");
ComplexD nrm = rankInnerProduct(left,right); ComplexD nrm = rankInnerProduct(left,right);
@@ -322,11 +322,11 @@ inline ComplexD innerProduct(const Lattice<vobj> &left,const Lattice<vobj> &righ
ComplexD nrm2 = rankInnerProduct(left,right); ComplexD nrm2 = rankInnerProduct(left,right);
RealD local2 = real(nrm2); RealD local2 = real(nrm2);
std::cerr<< " Bad NORM " << local << " recomputed as "<<local2<<std::endl; std::cerr<< " Bad NORM " << local << " recomputed as "<<local2<<std::endl;
GRID_ASSERT(ok); assert(ok);
} }
FlightRecorder::StepLog("Start global sum"); FlightRecorder::StepLog("Start global sum");
grid->GlobalSumP2P(nrm); // grid->GlobalSumP2P(nrm);
// grid->GlobalSum(nrm); grid->GlobalSum(nrm);
FlightRecorder::StepLog("Finished global sum"); FlightRecorder::StepLog("Finished global sum");
// std::cout << " norm "<< nrm << " p2p norm "<<nrmck<<std::endl; // std::cout << " norm "<< nrm << " p2p norm "<<nrmck<<std::endl;
FlightRecorder::ReductionLog(local,real(nrm)); FlightRecorder::ReductionLog(local,real(nrm));
@@ -376,9 +376,40 @@ axpby_norm_fast(Lattice<vobj> &z,sobj a,sobj b,const Lattice<vobj> &x,const Latt
coalescedWrite(z_v[ss],tmp); coalescedWrite(z_v[ss],tmp);
}); });
bool ok; bool ok;
#ifdef GRID_SYCL
uint64_t csum=0;
uint64_t csum2=0;
if ( FlightRecorder::LoggingMode != FlightRecorder::LoggingModeNone)
{
// z_v
{
Integer words = sites*sizeof(vobj)/sizeof(uint64_t);
uint64_t *base= (uint64_t *)&z_v[0];
csum=svm_xor(base,words);
ok = FlightRecorder::CsumLog(csum);
if ( !ok ) {
csum2=svm_xor(base,words);
std::cerr<< " Bad z_v CSUM " << std::hex<< csum << " recomputed as "<<csum2<<std::dec<<std::endl;
}
assert(ok);
}
// inner_v
{
Integer words = sites*sizeof(inner_t)/sizeof(uint64_t);
uint64_t *base= (uint64_t *)&inner_tmp_v[0];
csum=svm_xor(base,words);
ok = FlightRecorder::CsumLog(csum);
if ( !ok ) {
csum2=svm_xor(base,words);
std::cerr<< " Bad inner_tmp_v CSUM " << std::hex<< csum << " recomputed as "<<csum2<<std::dec<<std::endl;
}
assert(ok);
}
}
#endif
nrm = real(TensorRemove(sumD(inner_tmp_v,sites))); nrm = real(TensorRemove(sumD(inner_tmp_v,sites)));
ok = FlightRecorder::NormLog(real(nrm)); ok = FlightRecorder::NormLog(real(nrm));
GRID_ASSERT(ok); assert(ok);
RealD local = real(nrm); RealD local = real(nrm);
grid->GlobalSum(nrm); grid->GlobalSum(nrm);
FlightRecorder::ReductionLog(local,real(nrm)); FlightRecorder::ReductionLog(local,real(nrm));
@@ -464,13 +495,13 @@ template<class vobj> inline void sliceSum(const Lattice<vobj> &Data,
typedef typename vobj::scalar_object sobj; typedef typename vobj::scalar_object sobj;
typedef typename vobj::scalar_object::scalar_type scalar_type; typedef typename vobj::scalar_object::scalar_type scalar_type;
GridBase *grid = Data.Grid(); GridBase *grid = Data.Grid();
GRID_ASSERT(grid!=NULL); assert(grid!=NULL);
const int Nd = grid->_ndimension; const int Nd = grid->_ndimension;
const int Nsimd = grid->Nsimd(); const int Nsimd = grid->Nsimd();
GRID_ASSERT(orthogdim >= 0); assert(orthogdim >= 0);
GRID_ASSERT(orthogdim < Nd); assert(orthogdim < Nd);
int fd=grid->_fdimensions[orthogdim]; int fd=grid->_fdimensions[orthogdim];
int ld=grid->_ldimensions[orthogdim]; int ld=grid->_ldimensions[orthogdim];
@@ -557,14 +588,14 @@ static void sliceInnerProductVector( std::vector<ComplexD> & result, const Latti
typedef typename vobj::vector_type vector_type; typedef typename vobj::vector_type vector_type;
typedef typename vobj::scalar_type scalar_type; typedef typename vobj::scalar_type scalar_type;
GridBase *grid = lhs.Grid(); GridBase *grid = lhs.Grid();
GRID_ASSERT(grid!=NULL); assert(grid!=NULL);
conformable(grid,rhs.Grid()); conformable(grid,rhs.Grid());
const int Nd = grid->_ndimension; const int Nd = grid->_ndimension;
const int Nsimd = grid->Nsimd(); const int Nsimd = grid->Nsimd();
GRID_ASSERT(orthogdim >= 0); assert(orthogdim >= 0);
GRID_ASSERT(orthogdim < Nd); assert(orthogdim < Nd);
int fd=grid->_fdimensions[orthogdim]; int fd=grid->_fdimensions[orthogdim];
int ld=grid->_ldimensions[orthogdim]; int ld=grid->_ldimensions[orthogdim];
@@ -793,188 +824,6 @@ static void sliceInnerProductMatrix( Eigen::MatrixXcd &mat, const Lattice<vobj>
delete SliceGrid; delete SliceGrid;
} }
/////////////////////////////////////////////////////////////////////////////
// Batched linear algebra for Krylov orthogonalisation (GCR history windows).
//
// innerProductMulti(out,left,right): out[j] = <left[j],right> for all j in
// ONE kernel (right read once), ONE device reduction / sync (the per-site
// partials are an iVector over the batch) and ONE GlobalSumVector.
// axpyMulti(z,b,x): z = z + sum_j b[j] x[j] in ONE pass.
// axpyMultiNorm(z,b,x): same, returning global |z|^2 from the
// same pass (one reduction, one GlobalSum).
//
// The batch width is a template parameter chosen at runtime from {2,4,8,16}
// so a short window (mmax=2 smoother) does not pay for 16 partial lanes;
// windows longer than 16 are processed in chunks of 16, one reduction each.
// Same code path for every Lattice<vobj>: fine fermion fields and coarse
// multi-RHS fields (nrhs folded into the grid) alike.
/////////////////////////////////////////////////////////////////////////////
// The batch of views and coefficients is passed to the kernel BY VALUE as
// lambda-captured kernel arguments (a ViewPack), so there is no per-call
// deviceVector allocation and no synchronous host->device memcpy of pointer
// tables: the only sync points are the reductions themselves.
// LatticeView has no default constructor, so the pack holds raw aligned
// storage and views are copied in bytewise (as basisRotateJ does through
// acceleratorPut); the struct is trivially copyable as a kernel argument.
template<class View,int B>
struct ViewPack {
alignas(View) unsigned char raw[B*sizeof(View)];
ComplexD b[B];
accelerator_inline const View & v(int j) const { return reinterpret_cast<const View *>(raw)[j]; }
void set(int j,const View &view){ memcpy(raw+j*sizeof(View),&view,sizeof(View)); }
};
template<int B,class vobj>
void rankInnerProductMultiChunk(ComplexD *out,int m,
const std::vector<const Lattice<vobj>*> &left,
const Lattice<vobj> &right)
{
typedef decltype(innerProductD(vobj(),vobj())) inner_t;
typedef iVector<inner_t,B> batch_t;
typedef decltype(right.View(AcceleratorRead)) View;
GRID_ASSERT(m>=1 && m<=B);
GridBase *grid = right.Grid();
const uint64_t sites = grid->oSites();
std::vector<View> h_v; h_v.reserve(m);
ViewPack<View,B> pack;
for(int j=0;j<m;j++){
conformable(*left[j],right);
h_v.push_back(left[j]->View(AcceleratorRead));
pack.set(j,h_v[j]);
}
for(int j=m;j<B;j++) pack.set(j,h_v[0]); // valid but unused lanes
deviceVector<batch_t> partial(sites);
batch_t *partial_v = &partial[0];
{
autoView(right_v,right,AcceleratorRead);
accelerator_for(ss,sites,1,{
auto r = right_v[ss];
batch_t acc;
for(int j=0;j<B;j++){
if ( j<m ) acc._internal[j] = innerProductD(pack.v(j)[ss],r);
else zeroit(acc._internal[j]);
}
partial_v[ss] = acc;
});
}
for(int j=0;j<m;j++) h_v[j].ViewClose();
auto res = sum(partial_v,sites); // one reduction for the whole batch
for(int j=0;j<m;j++) out[j] = TensorRemove(res._internal[j]);
}
template<class vobj>
void rankInnerProductMulti(std::vector<ComplexD> &out,
const std::vector<const Lattice<vobj>*> &left,
const Lattice<vobj> &right)
{
int m = left.size();
out.resize(m);
for(int j0=0;j0<m;j0+=16){
int mm = std::min(16,m-j0);
std::vector<const Lattice<vobj>*> sub(left.begin()+j0,left.begin()+j0+mm);
if ( mm<=2 ) rankInnerProductMultiChunk<2> (&out[j0],mm,sub,right);
else if ( mm<=4 ) rankInnerProductMultiChunk<4> (&out[j0],mm,sub,right);
else if ( mm<=8 ) rankInnerProductMultiChunk<8> (&out[j0],mm,sub,right);
else rankInnerProductMultiChunk<16>(&out[j0],mm,sub,right);
}
}
template<class vobj>
void innerProductMulti(std::vector<ComplexD> &out,
const std::vector<const Lattice<vobj>*> &left,
const Lattice<vobj> &right)
{
rankInnerProductMulti(out,left,right);
if ( out.size() ) right.Grid()->GlobalSumVector(&out[0],(int)out.size());
}
// z = z + sum_{j<m} b[j] x[j] for one chunk of at most B vectors; if do_norm,
// also writes per-site |z|^2 into inner_tmp_v.
template<int B,class vobj>
void axpyMultiChunk(Lattice<vobj> &z,const ComplexD *b,
const std::vector<const Lattice<vobj>*> &x,int m,
int do_norm,
decltype(innerProduct(vobj(),vobj())) *inner_tmp_v)
{
typedef decltype(z.View(AcceleratorRead)) View;
GRID_ASSERT(m>=1 && m<=B);
GridBase *grid = z.Grid();
const uint64_t nsimd = grid->Nsimd();
const uint64_t sites = grid->oSites();
std::vector<View> h_v; h_v.reserve(m);
ViewPack<View,B> pack;
for(int j=0;j<m;j++){
conformable(*x[j],z);
GRID_ASSERT(x[j]!=&z); // window must not alias the accumulator
h_v.push_back(x[j]->View(AcceleratorRead));
pack.set(j,h_v[j]);
pack.b[j] = b[j];
}
for(int j=m;j<B;j++){ pack.set(j,h_v[0]); pack.b[j] = ComplexD(0.0); }
autoView(z_v,z,AcceleratorWrite);
accelerator_for(ss,sites,nsimd,{
auto acc = coalescedRead(z_v[ss]);
for(int j=0;j<B;j++) if ( j<m ) acc = acc + pack.b[j]*coalescedRead(pack.v(j)[ss]);
coalescedWrite(z_v[ss],acc);
if ( do_norm ) coalescedWrite(inner_tmp_v[ss],innerProduct(acc,acc));
});
for(int j=0;j<m;j++) h_v[j].ViewClose();
}
// z = z + sum_j b[j] x[j]; if do_norm, returns global |z|^2 from the same
// (last) pass. Chunks of 16 for windows longer than 16.
template<class vobj>
RealD axpyMultiNormImpl(Lattice<vobj> &z,const std::vector<ComplexD> &b,
const std::vector<const Lattice<vobj>*> &x,int do_norm)
{
typedef decltype(innerProduct(vobj(),vobj())) inner_t;
int m = x.size();
GRID_ASSERT((int)b.size()>=m);
GridBase *grid = z.Grid();
const uint64_t sites = grid->oSites();
deviceVector<inner_t> inner_tmp(do_norm ? sites : 1);
inner_t *inner_tmp_v = &inner_tmp[0];
if ( m==0 ) {
if ( do_norm ) return norm2(z);
return 0.0;
}
for(int j0=0;j0<m;j0+=16){
int mm = std::min(16,m-j0);
int last = (j0+mm>=m);
std::vector<const Lattice<vobj>*> sub(x.begin()+j0,x.begin()+j0+mm);
int dn = do_norm && last;
if ( mm<=2 ) axpyMultiChunk<2> (z,&b[j0],sub,mm,dn,inner_tmp_v);
else if ( mm<=4 ) axpyMultiChunk<4> (z,&b[j0],sub,mm,dn,inner_tmp_v);
else if ( mm<=8 ) axpyMultiChunk<8> (z,&b[j0],sub,mm,dn,inner_tmp_v);
else axpyMultiChunk<16>(z,&b[j0],sub,mm,dn,inner_tmp_v);
}
RealD nrm = 0.0;
if ( do_norm ) {
nrm = real(TensorRemove(sumD(inner_tmp_v,sites)));
grid->GlobalSum(nrm);
}
return nrm;
}
template<class vobj>
void axpyMulti(Lattice<vobj> &z,const std::vector<ComplexD> &b,const std::vector<const Lattice<vobj>*> &x)
{
axpyMultiNormImpl(z,b,x,0);
}
template<class vobj>
RealD axpyMultiNorm(Lattice<vobj> &z,const std::vector<ComplexD> &b,const std::vector<const Lattice<vobj>*> &x)
{
return axpyMultiNormImpl(z,b,x,1);
}
NAMESPACE_END(Grid); NAMESPACE_END(Grid);
+23 -179
View File
@@ -197,205 +197,55 @@ __global__ void reduceKernel(const vobj *lat, sobj *buffer, Iterator n) {
///////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////
// Possibly promote to double and sum // Possibly promote to double and sum
///////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////
#undef GRID_REDUCTION_TIMING
template <class vobj> template <class vobj>
inline typename vobj::scalar_objectD sumD_gpu_small(const vobj *lat, Integer osites) inline typename vobj::scalar_objectD sumD_gpu_small(const vobj *lat, Integer osites)
{ {
typedef typename vobj::scalar_objectD sobj; typedef typename vobj::scalar_objectD sobj;
typedef decltype(lat) Iterator; typedef decltype(lat) Iterator;
Integer nsimd= vobj::Nsimd(); Integer nsimd= vobj::Nsimd();
Integer size = osites*nsimd; Integer size = osites*nsimd;
Integer numThreads, numBlocks; Integer numThreads, numBlocks;
int ok = getNumBlocksAndThreads(size, sizeof(sobj), numThreads, numBlocks); int ok = getNumBlocksAndThreads(size, sizeof(sobj), numThreads, numBlocks);
GRID_ASSERT(ok); assert(ok);
Integer smemSize = numThreads * sizeof(sobj); Integer smemSize = numThreads * sizeof(sobj);
// Move out of UVM
// Turns out I had messed up the synchronise after move to compute stream
// as running this on the default stream fools the synchronise
deviceVector<sobj> buffer(numBlocks); deviceVector<sobj> buffer(numBlocks);
sobj *buffer_v = &buffer[0]; sobj *buffer_v = &buffer[0];
sobj result; sobj result;
#ifdef GRID_REDUCTION_TIMING
RealD t_kernel = -usecond();
#endif
reduceKernel<<< numBlocks, numThreads, smemSize, computeStream >>>(lat, buffer_v, size); reduceKernel<<< numBlocks, numThreads, smemSize, computeStream >>>(lat, buffer_v, size);
accelerator_barrier(); accelerator_barrier();
#ifdef GRID_REDUCTION_TIMING
t_kernel += usecond();
RealD t_d2h = -usecond();
#endif
acceleratorCopyFromDevice(buffer_v,&result,sizeof(result)); acceleratorCopyFromDevice(buffer_v,&result,sizeof(result));
#ifdef GRID_REDUCTION_TIMING
t_d2h += usecond();
std::cout << GridLogDebug << " sumD_gpu_small"
<< " sizeof(sobj)=" << sizeof(sobj)
<< " blocks=" << numBlocks << " threads=" << numThreads
<< " kernel+barrier=" << t_kernel << " us"
<< " D2H=" << t_d2h << " us" << std::endl;
#endif
return result; return result;
} }
// Fused pack+reduce: reads R words of each vobj at word offset 'base',
// accumulates directly into iVector<iScalar<scalarD>,R> without staging
// through an intermediate bundle buffer. One HBM pass instead of three.
template <int R, class vobj, class sobj, class Iterator>
__device__ void packReduceBlocks(
const iScalar<typename vobj::vector_type> *idat,
sobj *g_odata, Iterator osites, int base, int words)
{
constexpr Iterator nsimd = vobj::Nsimd();
Iterator blockSize = blockDim.x;
extern __shared__ __align__(COALESCE_GRANULARITY) unsigned char shmem_pointer[];
sobj *sdata = (sobj *)shmem_pointer;
Iterator tid = threadIdx.x;
Iterator i = blockIdx.x * (blockSize * 2) + threadIdx.x;
Iterator gridSize = blockSize * 2 * gridDim.x;
sobj mySum = Zero();
while (i < osites * nsimd) {
Iterator lane = i % nsimd;
Iterator ss = i / nsimd;
sobj tmpD; zeroit(tmpD);
for (int k = 0; k < R; k++) {
auto w = extractLane(lane, idat[ss * words + base + k]);
iScalar<typename vobj::scalar_typeD> wd; wd = w;
tmpD._internal[k] = wd;
}
mySum += tmpD;
if (i + blockSize < osites * nsimd) {
lane = (i + blockSize) % nsimd;
ss = (i + blockSize) / nsimd;
sobj tmpD2; zeroit(tmpD2);
for (int k = 0; k < R; k++) {
auto w = extractLane(lane, idat[ss * words + base + k]);
iScalar<typename vobj::scalar_typeD> wd; wd = w;
tmpD2._internal[k] = wd;
}
mySum += tmpD2;
}
i += gridSize;
}
reduceBlock(sdata, mySum, tid);
if (tid == 0) g_odata[blockIdx.x] = sdata[0];
}
template <int R, class vobj, class sobj, class Iterator>
__global__ void packReduceKernel(
const iScalar<typename vobj::vector_type> *idat,
sobj *buffer, Iterator osites, int base, int words)
{
Iterator blockSize = blockDim.x;
packReduceBlocks<R, vobj, sobj>(idat, buffer, osites, base, words);
if (gridDim.x > 1) {
const Iterator tid = threadIdx.x;
__shared__ bool amLast;
extern __shared__ __align__(COALESCE_GRANULARITY) unsigned char shmem_pointer[];
sobj *smem = (sobj *)shmem_pointer;
acceleratorFence();
if (tid == 0) {
unsigned int ticket = atomicInc(&retirementCount, gridDim.x);
amLast = (ticket == gridDim.x - 1);
}
acceleratorSynchroniseAll();
if (amLast) {
Iterator i = tid;
sobj mySum = Zero();
while (i < (Iterator)gridDim.x) {
mySum += buffer[i];
i += blockSize;
}
reduceBlock(smem, mySum, tid);
if (tid == 0) {
buffer[0] = smem[0];
retirementCount = 0;
}
}
}
}
template<int R, class vobj>
inline void sumD_gpu_reduce_words(const vobj *lat, Integer osites,
typename vobj::scalar_typeD *ret_p, int base)
{
typedef typename vobj::vector_type vector;
typedef typename vobj::scalar_typeD scalarD;
using BundleScalarD = iVector<iScalar<scalarD>, R>;
constexpr int Nsimd = vobj::Nsimd();
const int words = sizeof(vobj) / sizeof(vector);
const iScalar<vector> *idat = (const iScalar<vector> *)lat;
Integer size = (Integer)osites * Nsimd;
Integer numThreads, numBlocks;
int ok = getNumBlocksAndThreads(size, sizeof(BundleScalarD), numThreads, numBlocks);
GRID_ASSERT(ok);
Integer smemSize = numThreads * sizeof(BundleScalarD);
deviceVector<BundleScalarD> buffer(numBlocks);
BundleScalarD *buffer_v = &buffer[0];
BundleScalarD result;
#ifdef GRID_REDUCTION_TIMING
RealD t_kernel = -usecond();
#endif
packReduceKernel<R, vobj, BundleScalarD, Integer>
<<<numBlocks, numThreads, smemSize, computeStream>>>
(idat, buffer_v, osites, base, words);
accelerator_barrier();
#ifdef GRID_REDUCTION_TIMING
t_kernel += usecond();
RealD t_d2h = -usecond();
#endif
acceleratorCopyFromDevice(buffer_v, &result, sizeof(result));
#ifdef GRID_REDUCTION_TIMING
t_d2h += usecond();
std::cout << GridLogDebug << " sumD_gpu_reduce_words R=" << R
<< " base=" << base
<< " kernel=" << t_kernel << " D2H=" << t_d2h << " us" << std::endl;
#endif
for (int k = 0; k < R; k++)
ret_p[base + k] = TensorRemove(result._internal[k]);
}
template <class vobj> template <class vobj>
inline typename vobj::scalar_objectD sumD_gpu_large(const vobj *lat, Integer osites) inline typename vobj::scalar_objectD sumD_gpu_large(const vobj *lat, Integer osites)
{ {
typedef typename vobj::vector_type vector; typedef typename vobj::vector_type vector;
typedef typename vobj::scalar_typeD scalarD; typedef typename vobj::scalar_typeD scalarD;
typedef typename vobj::scalar_objectD sobjD; typedef typename vobj::scalar_objectD sobj;
sobj ret;
const int words = sizeof(vobj) / sizeof(vector);
sobjD ret; zeroit(ret);
scalarD *ret_p = (scalarD *)&ret; scalarD *ret_p = (scalarD *)&ret;
const int words = sizeof(vobj)/sizeof(vector);
#ifdef GRID_REDUCTION_TIMING deviceVector<vector> buffer(osites);
RealD t_large = -usecond(); vector *dat = (vector *)lat;
#endif vector *buf = &buffer[0];
int w = 0; iScalar<vector> *tbuf =(iScalar<vector> *) &buffer[0];
while (w + 12 <= words) { sumD_gpu_reduce_words<12>(lat, osites, ret_p, w); w += 12; } for(int w=0;w<words;w++) {
while (w + 4 <= words) { sumD_gpu_reduce_words< 4>(lat, osites, ret_p, w); w += 4; }
while (w < words) { sumD_gpu_reduce_words< 1>(lat, osites, ret_p, w); w += 1; }
#ifdef GRID_REDUCTION_TIMING
t_large += usecond();
std::cout << GridLogDebug << "sumD_gpu_large"
<< " sizeof(sobjD)=" << sizeof(sobjD)
<< " words=" << words << " total=" << t_large << " us" << std::endl;
#endif
accelerator_for(ss,osites,1,{
buf[ss] = dat[ss*words+w];
});
ret_p[w] = sumD_gpu_small(tbuf,osites);
}
return ret; return ret;
} }
@@ -438,11 +288,5 @@ inline typename vobj::scalar_object sum_gpu_large(const vobj *lat, Integer osite
result = sumD_gpu_large(lat,osites); result = sumD_gpu_large(lat,osites);
return result; return result;
} }
template<class Word> Word checksum_gpu(Word *vec,uint64_t L)
{
Word w;
bzero(&w,sizeof(w));
return w;
}
NAMESPACE_END(Grid); NAMESPACE_END(Grid);
+16 -34
View File
@@ -6,27 +6,28 @@ NAMESPACE_BEGIN(Grid);
template <class vobj> template <class vobj>
inline typename vobj::scalar_objectD sumD_gpu_tensor(const vobj *lat, Integer osites) inline typename vobj::scalar_objectD sumD_gpu_tensor(const vobj *lat, Integer osites)
{ {
typedef typename vobj::scalar_object sobj; typedef typename vobj::scalar_object sobj;
typedef typename vobj::scalar_objectD sobjD; typedef typename vobj::scalar_objectD sobjD;
sobjD identity; zeroit(identity); sobj identity; zeroit(identity);
sobjD ret; zeroit(ret); sobj ret; zeroit(ret);
{ Integer nsimd= vobj::Nsimd();
sycl::buffer<sobjD, 1> abuff(&ret, {1}); {
sycl::buffer<sobj, 1> abuff(&ret, {1});
theGridAccelerator->submit([&](sycl::handler &cgh) { theGridAccelerator->submit([&](sycl::handler &cgh) {
auto Reduction = sycl::reduction(abuff, cgh, identity, std::plus<>()); auto Reduction = sycl::reduction(abuff,cgh,identity,std::plus<>());
cgh.parallel_for(sycl::range<1>{(size_t)osites}, cgh.parallel_for(sycl::range<1>{osites},
Reduction, Reduction,
[=](sycl::id<1> item, auto &sum) { [=] (sycl::id<1> item, auto &sum) {
sobj s = Reduce(lat[item[0]]); auto osite = item[0];
sobjD sd; sd = s; sum +=Reduce(lat[osite]);
sum += sd; });
});
}); });
} }
return ret; sobjD dret; convertType(dret,ret);
return dret;
} }
template <class vobj> template <class vobj>
@@ -86,25 +87,6 @@ template<class Word> Word svm_xor(Word *vec,uint64_t L)
theGridAccelerator->wait(); theGridAccelerator->wait();
return ret; return ret;
} }
template<class Word> Word checksum_gpu(Word *vec,uint64_t L)
{
Word identity; identity=0;
Word ret = 0;
{
sycl::buffer<Word, 1> abuff(&ret, {1});
theGridAccelerator->submit([&](sycl::handler &cgh) {
auto Reduction = sycl::reduction(abuff,cgh,identity,std::bit_xor<>());
cgh.parallel_for(sycl::range<1>{L},
Reduction,
[=] (sycl::id<1> index, auto &sum) {
auto l = index % 61;
sum ^= vec[index]<<l | vec[index]>>(64-l);
});
});
}
theGridAccelerator->wait();
return ret;
}
NAMESPACE_END(Grid); NAMESPACE_END(Grid);
+13 -13
View File
@@ -53,10 +53,10 @@ inline int RNGfillable(GridBase *coarse,GridBase *fine)
// trivially extended in higher dims, with locality guaranteeing RNG state is local to node // trivially extended in higher dims, with locality guaranteeing RNG state is local to node
int lowerdims = fine->_ndimension - coarse->_ndimension; int lowerdims = fine->_ndimension - coarse->_ndimension;
GRID_ASSERT(lowerdims >= 0); assert(lowerdims >= 0);
for(int d=0;d<lowerdims;d++){ for(int d=0;d<lowerdims;d++){
GRID_ASSERT(fine->_simd_layout[d]==1); assert(fine->_simd_layout[d]==1);
GRID_ASSERT(fine->_processors[d]==1); assert(fine->_processors[d]==1);
} }
int multiplicity=1; int multiplicity=1;
@@ -66,9 +66,9 @@ inline int RNGfillable(GridBase *coarse,GridBase *fine)
// local and global volumes subdivide cleanly after SIMDization // local and global volumes subdivide cleanly after SIMDization
for(int d=0;d<rngdims;d++){ for(int d=0;d<rngdims;d++){
int fd= d+lowerdims; int fd= d+lowerdims;
GRID_ASSERT(coarse->_processors[d] == fine->_processors[fd]); assert(coarse->_processors[d] == fine->_processors[fd]);
GRID_ASSERT(coarse->_simd_layout[d] == fine->_simd_layout[fd]); assert(coarse->_simd_layout[d] == fine->_simd_layout[fd]);
GRID_ASSERT(((fine->_rdimensions[fd] / coarse->_rdimensions[d])* coarse->_rdimensions[d])==fine->_rdimensions[fd]); assert(((fine->_rdimensions[fd] / coarse->_rdimensions[d])* coarse->_rdimensions[d])==fine->_rdimensions[fd]);
multiplicity = multiplicity *fine->_rdimensions[fd] / coarse->_rdimensions[d]; multiplicity = multiplicity *fine->_rdimensions[fd] / coarse->_rdimensions[d];
} }
@@ -83,18 +83,18 @@ inline int RNGfillable_general(GridBase *coarse,GridBase *fine)
int rngdims = coarse->_ndimension; int rngdims = coarse->_ndimension;
// trivially extended in higher dims, with locality guaranteeing RNG state is local to node // trivially extended in higher dims, with locality guaranteeing RNG state is local to node
int lowerdims = fine->_ndimension - coarse->_ndimension; GRID_ASSERT(lowerdims >= 0); int lowerdims = fine->_ndimension - coarse->_ndimension; assert(lowerdims >= 0);
// assumes that the higher dimensions are not using more processors // assumes that the higher dimensions are not using more processors
// all further divisions are local // all further divisions are local
for(int d=0;d<lowerdims;d++) GRID_ASSERT(fine->_processors[d]==1); for(int d=0;d<lowerdims;d++) assert(fine->_processors[d]==1);
for(int d=0;d<rngdims;d++) GRID_ASSERT(coarse->_processors[d] == fine->_processors[d+lowerdims]); for(int d=0;d<rngdims;d++) assert(coarse->_processors[d] == fine->_processors[d+lowerdims]);
// then divide the number of local sites // then divide the number of local sites
// check that the total number of sims agree, meanse the iSites are the same // check that the total number of sims agree, meanse the iSites are the same
GRID_ASSERT(fine->Nsimd() == coarse->Nsimd()); assert(fine->Nsimd() == coarse->Nsimd());
// check that the two grids divide cleanly // check that the two grids divide cleanly
GRID_ASSERT( (fine->lSites() / coarse->lSites() ) * coarse->lSites() == fine->lSites() ); assert( (fine->lSites() / coarse->lSites() ) * coarse->lSites() == fine->lSites() );
return fine->lSites() / coarse->lSites(); return fine->lSites() / coarse->lSites();
} }
@@ -177,7 +177,7 @@ public:
skip = skip<<shift; skip = skip<<shift;
GRID_ASSERT((skip >> shift)==site); // check for overflow assert((skip >> shift)==site); // check for overflow
eng.discard(skip); eng.discard(skip);
#else #else
@@ -218,7 +218,7 @@ public:
GetState(saved,_generators[gen]); GetState(saved,_generators[gen]);
} }
void SetState(std::vector<RngStateType> & saved,RngEngine &eng){ void SetState(std::vector<RngStateType> & saved,RngEngine &eng){
GRID_ASSERT(saved.size()==RngStateCount); assert(saved.size()==RngStateCount);
std::stringstream ss; std::stringstream ss;
for(int i=0;i<RngStateCount;i++){ for(int i=0;i<RngStateCount;i++){
ss<< saved[i]<<" "; ss<< saved[i]<<" ";
+3 -9
View File
@@ -1,6 +1,7 @@
#pragma once #pragma once
#if defined(GRID_CUDA) #if defined(GRID_CUDA)
#include <cub/cub.cuh> #include <cub/cub.cuh>
#define gpucub cub #define gpucub cub
#define gpuError_t cudaError_t #define gpuError_t cudaError_t
@@ -56,13 +57,8 @@ inline void sliceSumReduction_cub_small(const vobj *Data,
//copy offsets to device //copy offsets to device
acceleratorCopyToDeviceAsynch(&offsets[0],d_offsets,sizeof(int)*(rd+1),computeStream); acceleratorCopyToDeviceAsynch(&offsets[0],d_offsets,sizeof(int)*(rd+1),computeStream);
#if defined(__CUDACC__) && (__CUDACC_VER_MAJOR__ >= 13)
#define GRID_CUB_SUM_OP ::cuda::std::plus<>{}
#else
#define GRID_CUB_SUM_OP ::gpucub::Sum()
#endif
gpuError_t gpuErr = gpucub::DeviceSegmentedReduce::Reduce(temp_storage_array, temp_storage_bytes, rb_p,d_out, rd, d_offsets, d_offsets+1, GRID_CUB_SUM_OP, zero_init, computeStream); gpuError_t gpuErr = gpucub::DeviceSegmentedReduce::Reduce(temp_storage_array, temp_storage_bytes, rb_p,d_out, rd, d_offsets, d_offsets+1, ::gpucub::Sum(), zero_init, computeStream);
if (gpuErr!=gpuSuccess) { if (gpuErr!=gpuSuccess) {
std::cout << GridLogError << "Lattice_slicesum_gpu.h: Encountered error during gpucub::DeviceSegmentedReduce::Reduce (setup)! Error: " << gpuErr <<std::endl; std::cout << GridLogError << "Lattice_slicesum_gpu.h: Encountered error during gpucub::DeviceSegmentedReduce::Reduce (setup)! Error: " << gpuErr <<std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@@ -86,13 +82,11 @@ inline void sliceSumReduction_cub_small(const vobj *Data,
}); });
//issue segmented reductions in computeStream //issue segmented reductions in computeStream
gpuErr = gpucub::DeviceSegmentedReduce::Reduce(temp_storage_array, temp_storage_bytes, rb_p, d_out, rd, d_offsets, d_offsets+1, GRID_CUB_SUM_OP, zero_init, computeStream); gpuErr = gpucub::DeviceSegmentedReduce::Reduce(temp_storage_array, temp_storage_bytes, rb_p, d_out, rd, d_offsets, d_offsets+1,::gpucub::Sum(), zero_init, computeStream);
if (gpuErr!=gpuSuccess) { if (gpuErr!=gpuSuccess) {
std::cout << GridLogError << "Lattice_slicesum_gpu.h: Encountered error during gpucub::DeviceSegmentedReduce::Reduce! Error: " << gpuErr <<std::endl; std::cout << GridLogError << "Lattice_slicesum_gpu.h: Encountered error during gpucub::DeviceSegmentedReduce::Reduce! Error: " << gpuErr <<std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
#undef GRID_CUB_SUM_OP
acceleratorCopyFromDeviceAsynch(d_out,&lvSum[0],rd*sizeof(vobj),computeStream); acceleratorCopyFromDeviceAsynch(d_out,&lvSum[0],rd*sizeof(vobj),computeStream);
-62
View File
@@ -124,68 +124,6 @@ Lattice<iScalar<iScalar<iMatrix<vComplexD, N> > > > Inverse(const Lattice<iScala
return ret; return ret;
} }
template<int N>
Lattice<iMatrix<iScalar<iScalar<iScalar<vComplexD> > > , N> > Inverse(const Lattice<iMatrix<iScalar<iScalar<iScalar<vComplexD> > >, N> > &Umu)
{
GridBase *grid=Umu.Grid();
auto lvol = grid->lSites();
Lattice<iMatrix<iScalar<iScalar<iScalar<vComplexD> > >, N > > ret(grid);
autoView(Umu_v,Umu,CpuRead);
autoView(ret_v,ret,CpuWrite);
thread_for(site,lvol,{
Eigen::MatrixXcd EigenU = Eigen::MatrixXcd::Zero(N,N);
Coordinate lcoor;
grid->LocalIndexToLocalCoor(site, lcoor);
iMatrix<iScalar<iScalar<iScalar<ComplexD> > >, N > Us;
iMatrix<iScalar<iScalar<iScalar<ComplexD> > >, N > Ui;
peekLocalSite(Us, Umu_v, lcoor);
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
EigenU(i,j) = Us(i,j)()()();
}}
Eigen::MatrixXcd EigenUinv = EigenU.inverse();
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
Ui(i,j)()()() = EigenUinv(i,j);
}}
pokeLocalSite(Ui,ret_v,lcoor);
});
return ret;
}
template<int N>
Lattice<iMatrix<iScalar<iScalar<iScalar<iScalar<vComplexD> > > > , N> > Inverse(const Lattice<iMatrix<iScalar<iScalar<iScalar<iScalar<vComplexD> > > >, N> > &Umu)
{
GridBase *grid=Umu.Grid();
auto lvol = grid->lSites();
Lattice<iMatrix<iScalar<iScalar<iScalar<iScalar<vComplexD> > > >, N > > ret(grid);
autoView(Umu_v,Umu,CpuRead);
autoView(ret_v,ret,CpuWrite);
thread_for(site,lvol,{
Eigen::MatrixXcd EigenU = Eigen::MatrixXcd::Zero(N,N);
Coordinate lcoor;
grid->LocalIndexToLocalCoor(site, lcoor);
iMatrix<iScalar<iScalar<iScalar<iScalar<ComplexD> > > >, N > Us;
iMatrix<iScalar<iScalar<iScalar<iScalar<ComplexD> > > >, N > Ui;
peekLocalSite(Us, Umu_v, lcoor);
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
EigenU(i,j) = Us(i,j)()()()();
}}
Eigen::MatrixXcd EigenUinv = EigenU.inverse();
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
Ui(i,j)()()()() = EigenUinv(i,j);
}}
pokeLocalSite(Ui,ret_v,lcoor);
});
return ret;
}
NAMESPACE_END(Grid); NAMESPACE_END(Grid);
#endif #endif
+64 -73
View File
@@ -31,15 +31,15 @@ NAMESPACE_BEGIN(Grid);
inline void subdivides(GridBase *coarse,GridBase *fine) inline void subdivides(GridBase *coarse,GridBase *fine)
{ {
GRID_ASSERT(coarse->_ndimension == fine->_ndimension); assert(coarse->_ndimension == fine->_ndimension);
int _ndimension = coarse->_ndimension; int _ndimension = coarse->_ndimension;
// local and global volumes subdivide cleanly after SIMDization // local and global volumes subdivide cleanly after SIMDization
for(int d=0;d<_ndimension;d++){ for(int d=0;d<_ndimension;d++){
GRID_ASSERT(coarse->_processors[d] == fine->_processors[d]); assert(coarse->_processors[d] == fine->_processors[d]);
GRID_ASSERT(coarse->_simd_layout[d] == fine->_simd_layout[d]); assert(coarse->_simd_layout[d] == fine->_simd_layout[d]);
GRID_ASSERT((fine->_rdimensions[d] / coarse->_rdimensions[d])* coarse->_rdimensions[d]==fine->_rdimensions[d]); assert((fine->_rdimensions[d] / coarse->_rdimensions[d])* coarse->_rdimensions[d]==fine->_rdimensions[d]);
} }
} }
@@ -191,15 +191,6 @@ accelerator_inline void convertType(vComplexD & out, const ComplexD & in) {
accelerator_inline void convertType(vComplexD2 & out, const ComplexD & in) { accelerator_inline void convertType(vComplexD2 & out, const ComplexD & in) {
((ComplexD*)&out)[acceleratorSIMTlane(vComplexD::Nsimd()*2)] = in; ((ComplexD*)&out)[acceleratorSIMTlane(vComplexD::Nsimd()*2)] = in;
} }
// Lex chart: one lane, so there is no lane to select. Needed because the
// coalesced accessor hands the kernel a scalar object while the destination
// is still the vector type.
accelerator_inline void convertType(sComplexF & out, const ComplexF & in) {
out.v = in;
}
accelerator_inline void convertType(sComplexD & out, const ComplexD & in) {
out.v = in;
}
#endif #endif
accelerator_inline void convertType(vComplexF & out, const vComplexD2 & in) { accelerator_inline void convertType(vComplexF & out, const vComplexD2 & in) {
@@ -318,7 +309,7 @@ inline void batchBlockProject(std::vector<Lattice<iVector<CComplex,nbasis>>> &co
const VLattice &Basis) const VLattice &Basis)
{ {
int NBatch = fineData.size(); int NBatch = fineData.size();
GRID_ASSERT(coarseData.size() == NBatch); assert(coarseData.size() == NBatch);
GridBase * fine = fineData[0].Grid(); GridBase * fine = fineData[0].Grid();
GridBase * coarse= coarseData[0].Grid(); GridBase * coarse= coarseData[0].Grid();
@@ -353,7 +344,7 @@ template<class vobj,class vobj2,class CComplex>
GridBase * coarse= coarseA.Grid(); GridBase * coarse= coarseA.Grid();
fineZ.Checkerboard()=fineX.Checkerboard(); fineZ.Checkerboard()=fineX.Checkerboard();
GRID_ASSERT(fineX.Checkerboard()==fineY.Checkerboard()); assert(fineX.Checkerboard()==fineY.Checkerboard());
subdivides(coarse,fine); // require they map subdivides(coarse,fine); // require they map
conformable(fineX,fineY); conformable(fineX,fineY);
conformable(fineX,fineZ); conformable(fineX,fineZ);
@@ -365,7 +356,7 @@ template<class vobj,class vobj2,class CComplex>
// FIXME merge with subdivide checking routine as this is redundant // FIXME merge with subdivide checking routine as this is redundant
for(int d=0 ; d<_ndimension;d++){ for(int d=0 ; d<_ndimension;d++){
block_r[d] = fine->_rdimensions[d] / coarse->_rdimensions[d]; block_r[d] = fine->_rdimensions[d] / coarse->_rdimensions[d];
GRID_ASSERT(block_r[d]*coarse->_rdimensions[d]==fine->_rdimensions[d]); assert(block_r[d]*coarse->_rdimensions[d]==fine->_rdimensions[d]);
} }
autoView( fineZ_ , fineZ, AcceleratorWrite); autoView( fineZ_ , fineZ, AcceleratorWrite);
@@ -622,7 +613,7 @@ inline void blockPromote(const Lattice<iVector<CComplex,nbasis > > &coarseData,
int _ndimension = coarse->_ndimension; int _ndimension = coarse->_ndimension;
// checks // checks
GRID_ASSERT( nbasis == Basis.size() ); assert( nbasis == Basis.size() );
subdivides(coarse,fine); subdivides(coarse,fine);
for(int i=0;i<nbasis;i++){ for(int i=0;i<nbasis;i++){
conformable(Basis[i].Grid(),fine); conformable(Basis[i].Grid(),fine);
@@ -696,7 +687,7 @@ inline void batchBlockPromote(const std::vector<Lattice<iVector<CComplex,nbasis>
const VLattice &Basis) const VLattice &Basis)
{ {
int NBatch = coarseData.size(); int NBatch = coarseData.size();
GRID_ASSERT(fineData.size() == NBatch); assert(fineData.size() == NBatch);
GridBase * fine = fineData[0].Grid(); GridBase * fine = fineData[0].Grid();
GridBase * coarse = coarseData[0].Grid(); GridBase * coarse = coarseData[0].Grid();
@@ -724,12 +715,12 @@ void localConvert(const Lattice<vobj> &in,Lattice<vvobj> &out)
int ni = ig->_ndimension; int ni = ig->_ndimension;
int no = og->_ndimension; int no = og->_ndimension;
GRID_ASSERT(ni == no); assert(ni == no);
for(int d=0;d<no;d++){ for(int d=0;d<no;d++){
GRID_ASSERT(ig->_processors[d] == og->_processors[d]); assert(ig->_processors[d] == og->_processors[d]);
GRID_ASSERT(ig->_ldimensions[d] == og->_ldimensions[d]); assert(ig->_ldimensions[d] == og->_ldimensions[d]);
GRID_ASSERT(ig->lSites() == og->lSites()); assert(ig->lSites() == og->lSites());
} }
autoView(in_v,in,CpuRead); autoView(in_v,in,CpuRead);
@@ -761,16 +752,16 @@ void localCopyRegion(const Lattice<vobj> &From,Lattice<vobj> & To,Coordinate Fro
GridBase *Fg = From.Grid(); GridBase *Fg = From.Grid();
GridBase *Tg = To.Grid(); GridBase *Tg = To.Grid();
GRID_ASSERT(!Fg->_isCheckerBoarded); assert(!Fg->_isCheckerBoarded);
GRID_ASSERT(!Tg->_isCheckerBoarded); assert(!Tg->_isCheckerBoarded);
int Nsimd = Fg->Nsimd(); int Nsimd = Fg->Nsimd();
int nF = Fg->_ndimension; int nF = Fg->_ndimension;
int nT = Tg->_ndimension; int nT = Tg->_ndimension;
int nd = nF; int nd = nF;
GRID_ASSERT(nF == nT); assert(nF == nT);
for(int d=0;d<nd;d++){ for(int d=0;d<nd;d++){
GRID_ASSERT(Fg->_processors[d] == Tg->_processors[d]); assert(Fg->_processors[d] == Tg->_processors[d]);
} }
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
@@ -830,12 +821,12 @@ void InsertSliceFast(const Lattice<vobj> &From,Lattice<vobj> & To,int slice, int
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
GridBase *Fg = From.Grid(); GridBase *Fg = From.Grid();
GridBase *Tg = To.Grid(); GridBase *Tg = To.Grid();
GRID_ASSERT(!Fg->_isCheckerBoarded); assert(!Fg->_isCheckerBoarded);
GRID_ASSERT(!Tg->_isCheckerBoarded); assert(!Tg->_isCheckerBoarded);
int Nsimd = Fg->Nsimd(); int Nsimd = Fg->Nsimd();
int nF = Fg->_ndimension; int nF = Fg->_ndimension;
int nT = Tg->_ndimension; int nT = Tg->_ndimension;
GRID_ASSERT(nF+1 == nT); assert(nF+1 == nT);
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
// do the index calc on the GPU // do the index calc on the GPU
@@ -899,12 +890,12 @@ void ExtractSliceFast(Lattice<vobj> &To,const Lattice<vobj> & From,int slice, in
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
GridBase *Fg = From.Grid(); GridBase *Fg = From.Grid();
GridBase *Tg = To.Grid(); GridBase *Tg = To.Grid();
GRID_ASSERT(!Fg->_isCheckerBoarded); assert(!Fg->_isCheckerBoarded);
GRID_ASSERT(!Tg->_isCheckerBoarded); assert(!Tg->_isCheckerBoarded);
int Nsimd = Fg->Nsimd(); int Nsimd = Fg->Nsimd();
int nF = Fg->_ndimension; int nF = Fg->_ndimension;
int nT = Tg->_ndimension; int nT = Tg->_ndimension;
GRID_ASSERT(nT+1 == nF); assert(nT+1 == nF);
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
// do the index calc on the GPU // do the index calc on the GPU
@@ -964,16 +955,16 @@ void InsertSlice(const Lattice<vobj> &lowDim,Lattice<vobj> & higherDim,int slice
int nl = lg->_ndimension; int nl = lg->_ndimension;
int nh = hg->_ndimension; int nh = hg->_ndimension;
GRID_ASSERT(nl+1 == nh); assert(nl+1 == nh);
GRID_ASSERT(orthog<nh); assert(orthog<nh);
GRID_ASSERT(orthog>=0); assert(orthog>=0);
GRID_ASSERT(hg->_processors[orthog]==1); assert(hg->_processors[orthog]==1);
int dl; dl = 0; int dl; dl = 0;
for(int d=0;d<nh;d++){ for(int d=0;d<nh;d++){
if ( d != orthog) { if ( d != orthog) {
GRID_ASSERT(lg->_processors[dl] == hg->_processors[d]); assert(lg->_processors[dl] == hg->_processors[d]);
GRID_ASSERT(lg->_ldimensions[dl] == hg->_ldimensions[d]); assert(lg->_ldimensions[dl] == hg->_ldimensions[d]);
dl++; dl++;
} }
} }
@@ -1014,17 +1005,17 @@ void ExtractSlice(Lattice<vobj> &lowDim,const Lattice<vobj> & higherDim,int slic
int nl = lg->_ndimension; int nl = lg->_ndimension;
int nh = hg->_ndimension; int nh = hg->_ndimension;
GRID_ASSERT(nl+1 == nh); assert(nl+1 == nh);
GRID_ASSERT(orthog<nh); assert(orthog<nh);
GRID_ASSERT(orthog>=0); assert(orthog>=0);
GRID_ASSERT(hg->_processors[orthog]==1); assert(hg->_processors[orthog]==1);
lowDim.Checkerboard() = higherDim.Checkerboard(); lowDim.Checkerboard() = higherDim.Checkerboard();
int dl; dl = 0; int dl; dl = 0;
for(int d=0;d<nh;d++){ for(int d=0;d<nh;d++){
if ( d != orthog) { if ( d != orthog) {
GRID_ASSERT(lg->_processors[dl] == hg->_processors[d]); assert(lg->_processors[dl] == hg->_processors[d]);
GRID_ASSERT(lg->_ldimensions[dl] == hg->_ldimensions[d]); assert(lg->_ldimensions[dl] == hg->_ldimensions[d]);
dl++; dl++;
} }
} }
@@ -1065,14 +1056,14 @@ void InsertSliceLocal(const Lattice<vobj> &lowDim, Lattice<vobj> & higherDim,int
int nl = lg->_ndimension; int nl = lg->_ndimension;
int nh = hg->_ndimension; int nh = hg->_ndimension;
GRID_ASSERT(nl == nh); assert(nl == nh);
GRID_ASSERT(orthog<nh); assert(orthog<nh);
GRID_ASSERT(orthog>=0); assert(orthog>=0);
for(int d=0;d<nh;d++){ for(int d=0;d<nh;d++){
if ( d!=orthog ) { if ( d!=orthog ) {
GRID_ASSERT(lg->_processors[d] == hg->_processors[d]); assert(lg->_processors[d] == hg->_processors[d]);
GRID_ASSERT(lg->_ldimensions[d] == hg->_ldimensions[d]); assert(lg->_ldimensions[d] == hg->_ldimensions[d]);
} }
} }
Coordinate sz = lg->_ldimensions; Coordinate sz = lg->_ldimensions;
@@ -1102,7 +1093,7 @@ void Replicate(const Lattice<vobj> &coarse,Lattice<vobj> & fine)
subdivides(cg,fg); subdivides(cg,fg);
GRID_ASSERT(cg->_ndimension==fg->_ndimension); assert(cg->_ndimension==fg->_ndimension);
Coordinate ratio(cg->_ndimension); Coordinate ratio(cg->_ndimension);
@@ -1166,7 +1157,7 @@ unvectorizeToLexOrdArray(std::vector<sobj> &out, const Lattice<vobj> &in)
int lex; int lex;
Lexicographic::IndexFromCoor(lcoor, lex, in_grid->_ldimensions); Lexicographic::IndexFromCoor(lcoor, lex, in_grid->_ldimensions);
GRID_ASSERT(lex < out.size()); assert(lex < out.size());
out_ptrs[lane] = &out[lex]; out_ptrs[lane] = &out[lex];
} }
@@ -1230,7 +1221,7 @@ vectorizeFromLexOrdArray( std::vector<sobj> &in, Lattice<vobj> &out)
typedef typename vobj::vector_type vtype; typedef typename vobj::vector_type vtype;
GridBase* grid = out.Grid(); GridBase* grid = out.Grid();
GRID_ASSERT(in.size()==grid->lSites()); assert(in.size()==grid->lSites());
const int ndim = grid->Nd(); const int ndim = grid->Nd();
constexpr int nsimd = vtype::Nsimd(); constexpr int nsimd = vtype::Nsimd();
@@ -1277,7 +1268,7 @@ vectorizeFromRevLexOrdArray( std::vector<sobj> &in, Lattice<vobj> &out)
typedef typename vobj::vector_type vtype; typedef typename vobj::vector_type vtype;
GridBase* grid = out._grid; GridBase* grid = out._grid;
GRID_ASSERT(in.size()==grid->lSites()); assert(in.size()==grid->lSites());
int ndim = grid->Nd(); int ndim = grid->Nd();
int nsimd = vtype::Nsimd(); int nsimd = vtype::Nsimd();
@@ -1338,9 +1329,9 @@ void precisionChangeFast(Lattice<VobjOut> &out, const Lattice<VobjIn> &in)
template<class VobjOut, class VobjIn> template<class VobjOut, class VobjIn>
void precisionChangeOrig(Lattice<VobjOut> &out, const Lattice<VobjIn> &in) void precisionChangeOrig(Lattice<VobjOut> &out, const Lattice<VobjIn> &in)
{ {
GRID_ASSERT(out.Grid()->Nd() == in.Grid()->Nd()); assert(out.Grid()->Nd() == in.Grid()->Nd());
for(int d=0;d<out.Grid()->Nd();d++){ for(int d=0;d<out.Grid()->Nd();d++){
GRID_ASSERT(out.Grid()->FullDimensions()[d] == in.Grid()->FullDimensions()[d]); assert(out.Grid()->FullDimensions()[d] == in.Grid()->FullDimensions()[d]);
} }
out.Checkerboard() = in.Checkerboard(); out.Checkerboard() = in.Checkerboard();
GridBase *in_grid=in.Grid(); GridBase *in_grid=in.Grid();
@@ -1391,9 +1382,9 @@ class precisionChangeWorkspace{
public: public:
precisionChangeWorkspace(GridBase *out_grid, GridBase *in_grid): _out_grid(out_grid), _in_grid(in_grid){ precisionChangeWorkspace(GridBase *out_grid, GridBase *in_grid): _out_grid(out_grid), _in_grid(in_grid){
//Build a map between the sites and lanes of the output field and the input field as we cannot use the Grids on the device //Build a map between the sites and lanes of the output field and the input field as we cannot use the Grids on the device
GRID_ASSERT(out_grid->Nd() == in_grid->Nd()); assert(out_grid->Nd() == in_grid->Nd());
for(int d=0;d<out_grid->Nd();d++){ for(int d=0;d<out_grid->Nd();d++){
GRID_ASSERT(out_grid->FullDimensions()[d] == in_grid->FullDimensions()[d]); assert(out_grid->FullDimensions()[d] == in_grid->FullDimensions()[d]);
} }
int Nsimd_out = out_grid->Nsimd(); int Nsimd_out = out_grid->Nsimd();
@@ -1558,7 +1549,7 @@ void Grid_split(std::vector<Lattice<Vobj> > & full,Lattice<Vobj> & split)
int full_vecs = full.size(); int full_vecs = full.size();
GRID_ASSERT(full_vecs>=1); assert(full_vecs>=1);
GridBase * full_grid = full[0].Grid(); GridBase * full_grid = full[0].Grid();
GridBase *split_grid = split.Grid(); GridBase *split_grid = split.Grid();
@@ -1576,18 +1567,18 @@ void Grid_split(std::vector<Lattice<Vobj> > & full,Lattice<Vobj> & split)
////////////////////////////// //////////////////////////////
// Checks // Checks
////////////////////////////// //////////////////////////////
GRID_ASSERT(full_grid->_ndimension==split_grid->_ndimension); assert(full_grid->_ndimension==split_grid->_ndimension);
for(int n=0;n<full_vecs;n++){ for(int n=0;n<full_vecs;n++){
GRID_ASSERT(full[n].Checkerboard() == cb); assert(full[n].Checkerboard() == cb);
for(int d=0;d<ndim;d++){ for(int d=0;d<ndim;d++){
GRID_ASSERT(full[n].Grid()->_gdimensions[d]==split.Grid()->_gdimensions[d]); assert(full[n].Grid()->_gdimensions[d]==split.Grid()->_gdimensions[d]);
GRID_ASSERT(full[n].Grid()->_fdimensions[d]==split.Grid()->_fdimensions[d]); assert(full[n].Grid()->_fdimensions[d]==split.Grid()->_fdimensions[d]);
} }
} }
int nvector =full_nproc/split_nproc; int nvector =full_nproc/split_nproc;
GRID_ASSERT(nvector*split_nproc==full_nproc); assert(nvector*split_nproc==full_nproc);
GRID_ASSERT(nvector == full_vecs); assert(nvector == full_vecs);
Coordinate ratio(ndim); Coordinate ratio(ndim);
for(int d=0;d<ndim;d++){ for(int d=0;d<ndim;d++){
@@ -1631,7 +1622,7 @@ void Grid_split(std::vector<Lattice<Vobj> > & full,Lattice<Vobj> & split)
int fvol = lsites; int fvol = lsites;
int chunk = (nvec*fvol)/sP; GRID_ASSERT(chunk*sP == nvec*fvol); int chunk = (nvec*fvol)/sP; assert(chunk*sP == nvec*fvol);
// Loop over reordered data post A2A // Loop over reordered data post A2A
thread_for(c, chunk, { thread_for(c, chunk, {
@@ -1684,7 +1675,7 @@ void Grid_unsplit(std::vector<Lattice<Vobj> > & full,Lattice<Vobj> & split)
int full_vecs = full.size(); int full_vecs = full.size();
GRID_ASSERT(full_vecs>=1); assert(full_vecs>=1);
GridBase * full_grid = full[0].Grid(); GridBase * full_grid = full[0].Grid();
GridBase *split_grid = split.Grid(); GridBase *split_grid = split.Grid();
@@ -1702,18 +1693,18 @@ void Grid_unsplit(std::vector<Lattice<Vobj> > & full,Lattice<Vobj> & split)
////////////////////////////// //////////////////////////////
// Checks // Checks
////////////////////////////// //////////////////////////////
GRID_ASSERT(full_grid->_ndimension==split_grid->_ndimension); assert(full_grid->_ndimension==split_grid->_ndimension);
for(int n=0;n<full_vecs;n++){ for(int n=0;n<full_vecs;n++){
GRID_ASSERT(full[n].Checkerboard() == cb); assert(full[n].Checkerboard() == cb);
for(int d=0;d<ndim;d++){ for(int d=0;d<ndim;d++){
GRID_ASSERT(full[n].Grid()->_gdimensions[d]==split.Grid()->_gdimensions[d]); assert(full[n].Grid()->_gdimensions[d]==split.Grid()->_gdimensions[d]);
GRID_ASSERT(full[n].Grid()->_fdimensions[d]==split.Grid()->_fdimensions[d]); assert(full[n].Grid()->_fdimensions[d]==split.Grid()->_fdimensions[d]);
} }
} }
int nvector =full_nproc/split_nproc; int nvector =full_nproc/split_nproc;
GRID_ASSERT(nvector*split_nproc==full_nproc); assert(nvector*split_nproc==full_nproc);
GRID_ASSERT(nvector == full_vecs); assert(nvector == full_vecs);
Coordinate ratio(ndim); Coordinate ratio(ndim);
for(int d=0;d<ndim;d++){ for(int d=0;d<ndim;d++){
@@ -1749,7 +1740,7 @@ void Grid_unsplit(std::vector<Lattice<Vobj> > & full,Lattice<Vobj> & split)
auto lsites= rsites/M; // Decreases rsites by M auto lsites= rsites/M; // Decreases rsites by M
int fvol = lsites; int fvol = lsites;
int chunk = (nvec*fvol)/sP; GRID_ASSERT(chunk*sP == nvec*fvol); int chunk = (nvec*fvol)/sP; assert(chunk*sP == nvec*fvol);
{ {
// Loop over reordered data post A2A // Loop over reordered data post A2A
-42
View File
@@ -106,47 +106,6 @@ public:
} }
}; };
#ifdef GRID_LOG_VIEWS
// Little autoscope assister
template<class View>
class ViewCloser
{
View v; // Take a copy of view and call view close when I go out of scope automatically
const char* filename; int line, mode;
public:
ViewCloser(View &_v, const char* _filename, int _line, int _mode) :
v(_v), filename(_filename), line(_line), mode(_mode) {
switch (mode){
case AcceleratorRead:
case AcceleratorWrite:
case CpuRead:
case CpuWrite:
ViewLogger::LogOpen(filename, line, 1, mode, &v[0], v.size() * sizeof(v[0]));
break;
}
};
~ViewCloser() {
switch (mode) {
case AcceleratorWriteDiscard:
case AcceleratorWrite:
case CpuWrite:
ViewLogger::LogClose(filename, line, -1, mode, &v[0], v.size() * sizeof(v[0]));
break;
}
v.ViewClose();
}
};
#define autoView(l_v,l,mode) \
auto l_v = l.View(mode); \
ViewCloser<decltype(l_v)> _autoView##l_v(l_v,__FILE__,__LINE__,mode);
#else
// Little autoscope assister // Little autoscope assister
template<class View> template<class View>
class ViewCloser class ViewCloser
@@ -160,7 +119,6 @@ class ViewCloser
#define autoView(l_v,l,mode) \ #define autoView(l_v,l,mode) \
auto l_v = l.View(mode); \ auto l_v = l.View(mode); \
ViewCloser<decltype(l_v)> _autoView##l_v(l_v); ViewCloser<decltype(l_v)> _autoView##l_v(l_v);
#endif
///////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////
// Lattice expression types used by ET to assemble the AST // Lattice expression types used by ET to assemble the AST
Loaded 100 of 557 files, more files were not shown because too many files have changed in this diff. Show more