20#ifndef DEM_SOLVER_FUSED_HPP
21#define DEM_SOLVER_FUSED_HPP
27#include <Kokkos_Core.hpp>
38 Kokkos::View<unsigned*, CpMem>
bar;
60#ifdef KOKKOS_ENABLE_CUDA
61 static const int mode = [] {
62 if (std::getenv(
"PECLET_DEM_NO_FUSED"))
64 if (std::getenv(
"PECLET_DEM_FUSED"))
68 return mode < 0 ? !graphReplayAvailable : mode == 1;
70 (void)graphReplayAvailable;
75#ifdef KOKKOS_ENABLE_CUDA
87__device__
inline void demGridBarrier(
unsigned* count,
unsigned k) {
89 if (threadIdx.x == 0) {
92 const unsigned target = (k + 1u) * gridDim.x;
93 while (*
reinterpret_cast<volatile unsigned*
>(count) < target) {
102inline constexpr int kFusedBarWords = 32769;
108template <
class Sweep>
109__global__
void demFusedColorSweepK(Sweep f, Kokkos::View<const int*, CpMem> perm,
110 Kokkos::View<const int*, CpMem> offs,
int numColors,
112 const int stride = gridDim.x * blockDim.x;
113 const int tid = blockIdx.x * blockDim.x + threadIdx.x;
115 for (
int c = 0; c < numColors; ++c) {
116 const int b = offs(c), e = offs(c + 1);
119 for (
int i = b + tid; i < e; i += stride)
121 if (c + 1 < numColors)
122 demGridBarrier(bar, k++);
131template <
class Sweep>
132__global__
void demFusedSweepLoopK(Sweep f, Kokkos::View<const int*, CpMem> perm,
133 Kokkos::View<const int*, CpMem> offs,
int numColors,
134 int maxIters,
float tol,
bool strictLess,
float* res,
136 const int stride = gridDim.x * blockDim.x;
137 const int tid = blockIdx.x * blockDim.x + threadIdx.x;
139 for (
int it = 0; it < maxIters; ++it) {
142 demGridBarrier(bar, k++);
143 for (
int c = 0; c < numColors; ++c) {
144 const int b = offs(c), e = offs(c + 1);
147 for (
int i = b + tid; i < e; i += stride)
149 demGridBarrier(bar, k++);
151 const float r = *
reinterpret_cast<volatile float*
>(res);
152 if (strictLess ? (r < tol) : (r <= tol))
154 demGridBarrier(bar, k++);
158inline constexpr int kFusedBlock = 256;
164inline int demFusedGridCap() {
165 static const int cap = [] {
166 const char* e = std::getenv(
"PECLET_DEM_FUSED_GRID");
167 return e ? std::atoi(e) : 1 << 30;
175template <
class Kernel>
176inline int demFusedMaxGrid(Kernel kernel) {
177 static const int maxGrid = [kernel] {
179 if (cudaOccupancyMaxActiveBlocksPerMultiprocessor(&perSm, kernel, kFusedBlock, 0) !=
181 (void)cudaGetLastError();
185 if (cudaGetDevice(&dev) != cudaSuccess ||
186 cudaDeviceGetAttribute(&sm, cudaDevAttrMultiProcessorCount, dev) != cudaSuccess) {
187 (void)cudaGetLastError();
198template <
class Sweep>
199inline bool demLaunchFusedColorSweep(
CpExec& space,
const Sweep& f,
200 Kokkos::View<const int*, CpMem> perm,
201 const FusedSweepCtx& ctx,
int numColors) {
202 const int maxGrid = std::min(demFusedMaxGrid(demFusedColorSweepK<Sweep>),
203 (
static_cast<int>(ctx.bar.extent(0)) - 1) / 8);
204 if (maxGrid <= 0 || numColors <= 0 || ctx.maxBucket <= 0)
206 const int want = std::min((ctx.maxBucket + kFusedBlock - 1) / kFusedBlock,
207 std::max(1, demFusedGridCap()));
208 const int grid = want < maxGrid ? want : maxGrid;
209 cudaStream_t str = space.cuda_stream();
210 cudaMemsetAsync(ctx.bar.data(), 0, (
static_cast<std::size_t
>(grid) * 8 + 1) *
sizeof(
unsigned),
212 demFusedColorSweepK<Sweep>
213 <<<grid, kFusedBlock, 0, str>>>(f, perm, ctx.offsDev, numColors, ctx.bar.data());
220template <
class Sweep>
221inline bool demLaunchFusedSweepLoop(
CpExec& space,
const Sweep& f,
222 Kokkos::View<const int*, CpMem> perm,
223 const FusedSweepCtx& ctx,
int numColors,
224 const FusedLoopSpec& spec,
float* res) {
225 const int maxGrid = std::min(demFusedMaxGrid(demFusedSweepLoopK<Sweep>),
226 (
static_cast<int>(ctx.bar.extent(0)) - 1) / 8);
227 if (maxGrid <= 0 || numColors <= 0 || ctx.maxBucket <= 0 || res ==
nullptr)
229 const int want = std::min((ctx.maxBucket + kFusedBlock - 1) / kFusedBlock,
230 std::max(1, demFusedGridCap()));
231 const int grid = want < maxGrid ? want : maxGrid;
232 cudaStream_t str = space.cuda_stream();
233 cudaMemsetAsync(ctx.bar.data(), 0, (
static_cast<std::size_t
>(grid) * 8 + 1) *
sizeof(
unsigned),
235 demFusedSweepLoopK<Sweep><<<grid, kFusedBlock, 0, str>>>(f, perm, ctx.offsDev, numColors,
236 spec.maxIters, spec.tol,
237 spec.strictLess, res, ctx.bar.data());
247 Kokkos::View<int*, CpMem> offsDev,
248 Kokkos::View<unsigned*, CpMem> bar) {
250#ifdef KOKKOS_ENABLE_CUDA
251 if (offs.size() < 2 || offs.size() > offsDev.extent(0) +
static_cast<std::size_t
>(0))
254 for (std::size_t c = 0; c + 1 < offs.size(); ++c)
255 maxBucket = std::max(maxBucket, offs[c + 1] - offs[c]);
258 const Kokkos::View<const int*, Kokkos::HostSpace, Kokkos::MemoryUnmanaged> h(
259 offs.data(), offs.size());
260 auto d = Kokkos::subview(offsDev, Kokkos::pair<std::size_t, std::size_t>(0, offs.size()));
261 Kokkos::deep_copy(space, d, h);
262 ctx.
offsDev = Kokkos::subview(Kokkos::View<const int*, CpMem>(offsDev),
263 Kokkos::pair<std::size_t, std::size_t>(0, offs.size()));
bool demFusedWanted(bool graphReplayAvailable)
Fused-sweep policy (read once).
FusedSweepCtx demMakeFusedCtx(CpExec &space, const std::vector< int > &offs, Kokkos::View< int *, CpMem > offsDev, Kokkos::View< unsigned *, CpMem > bar)
Fill a FusedSweepCtx from buildColorBucketsKokkos's host offsets: async-upload them into the pooled d...
Kokkos::DefaultExecutionSpace CpExec
Device-side iteration loop: run up to maxIters sweeps of the colour classes inside ONE kernel,...
Device-side context for a fused colour sweep: the colour offsets (numColors+1, uploaded from buildCol...
Kokkos::View< const int *, CpMem > offsDev
Kokkos::View< unsigned *, CpMem > bar