flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
bench_scalar_cutcell.cpp
Go to the documentation of this file.
1// G-perf instrument for the cut-cell scalar (doc/scalar_ibm_design.md ยง11 G-perf; WO-9). An
2// instrument, not a gate (ctest label `bench`): it prints, it never fails on a number.
3//
4// stream Kokkos triad a = b + s c on the default execution space, 2^26 doubles, best of 10;
5// 24 bytes per element.
6// memory bytes allocated in the default memory space from addScalar through the first steady
7// solve of ONE single-phase cut-cell scalar โ€” the scalar field, the cut state, the
8// ScalarMG hierarchy and the Krylov vectors โ€” per inner cell, live at the end and peak
9// (Kokkos Tools callbacks); the geometry record (one per solver, shared by every
10// cut-cell scalar) is built first and reported separately.
11// matvec Solver::scalarCutMatvec (the G = 2 ghost fill + the 7-point band operator + the facet
12// overlay), best of `reps`; the effective bandwidth on the byte model 9 doubles per
13// extended cell (AC and six bands read, x read, y written; the overlay excluded), so a
14// lower bound on the traffic, against the triad.
15//
16// Setup: an n^3 periodic box (cell units), one sphere of solid fraction 0.3 (G5b's simple-cubic
17// array, one period), Dirichlet walls c = 1, a uniform sink; steady.
18// Usage: bench_scalar_cutcell [n = 128] [reps = 50]
19#include <chrono>
20#include <cmath>
21#include <cstdio>
22#include <cstdlib>
23#include <cstring>
24#include <Kokkos_Core.hpp>
25#include <map>
26#include <vector>
27
28#include "flow_ibm.hpp"
29
30namespace {
33using Clock = std::chrono::steady_clock;
34
35// ---- Kokkos Tools allocation ledger (default memory space only) ----
36std::map<const void*, std::uint64_t> gLive;
37std::uint64_t gBytes = 0, gPeak = 0;
38bool gArmed = false;
39
40bool defaultSpace(const Kokkos::Profiling::SpaceHandle h) {
41 return std::strcmp(h.name, Kokkos::DefaultExecutionSpace::memory_space::name()) == 0;
42}
43void onAlloc(const Kokkos::Profiling::SpaceHandle h, const char*, const void* p,
44 const std::uint64_t n) {
45 if (!gArmed || !defaultSpace(h))
46 return;
47 gLive[p] = n;
48 gBytes += n;
49 gPeak = gBytes > gPeak ? gBytes : gPeak;
50}
51void onFree(const Kokkos::Profiling::SpaceHandle h, const char*, const void* p,
52 const std::uint64_t) {
53 if (!defaultSpace(h))
54 return;
55 auto it = gLive.find(p);
56 if (it == gLive.end())
57 return; // allocated before the ledger was armed
58 gBytes -= it->second;
59 gLive.erase(it);
60}
61
62double seconds(Clock::time_point t0) {
63 return std::chrono::duration<double>(Clock::now() - t0).count();
64}
65
66double streamTriad() {
67 const std::size_t n = std::size_t(1) << 26;
68 Kokkos::View<double*> a("a", n), b("b", n), c("c", n);
69 Kokkos::deep_copy(b, 1.0);
70 Kokkos::deep_copy(c, 2.0);
71 double best = 1e30;
72 for (int r = 0; r < 11; ++r) {
73 Kokkos::fence();
74 const auto t0 = Clock::now();
75 Kokkos::parallel_for(
76 "bench_triad", Kokkos::RangePolicy<>(0, n),
77 KOKKOS_LAMBDA(const std::size_t i) { a(i) = b(i) + 0.5 * c(i); });
78 Kokkos::fence();
79 if (r > 0)
80 best = std::fmin(best, seconds(t0));
81 }
82 return 24.0 * (double)n / best / 1e9;
83}
84
85std::vector<double> sphereSdf(int n) {
86 const double R = std::cbrt(0.3 * 3.0 / (4.0 * M_PI)) * n;
87 const double c[3] = {0.513 * n, 0.479 * n, 0.507 * n};
88 std::vector<double> s((std::size_t)n * n * n);
89 for (int z = 0; z < n; ++z)
90 for (int y = 0; y < n; ++y)
91 for (int x = 0; x < n; ++x) {
92 const double dx = x + 0.5 - c[0], dy = y + 0.5 - c[1], dz = z + 0.5 - c[2];
93 s[(std::size_t)x + (std::size_t)y * n + (std::size_t)z * n * n] =
94 std::sqrt(dx * dx + dy * dy + dz * dz) - R;
95 }
96 return s;
97}
98} // namespace
99
100int main(int argc, char** argv) {
101 Kokkos::initialize(argc, argv);
102 {
103 const int n = argc > 1 ? std::atoi(argv[1]) : 128;
104 const int reps = argc > 2 ? std::atoi(argv[2]) : 50;
105 Kokkos::Tools::Experimental::set_allocate_data_callback(onAlloc);
106 Kokkos::Tools::Experimental::set_deallocate_data_callback(onFree);
107
108 const double bwStream = streamTriad();
109 std::printf("G-perf backend %s, n = %d\n", Kokkos::DefaultExecutionSpace::name(), n);
110 std::printf(" stream triad: %.1f GB/s\n", bwStream);
111
112 IbmSolver s(n, n, n);
113 s.setRho(1.0);
114 s.setMu(1.0);
115 s.setDt(1.0);
116 s.setSolid(sphereSdf(n), true);
117 Kokkos::fence();
118 const double cells = (double)n * n * n;
119 gArmed = true;
120 const auto t0 = Clock::now();
121 (void)s.scalarCutGeometry(); // the geometry record: built once per solver, shared by scalars
122 Kokkos::fence();
123 const double tGeom = seconds(t0);
124 const std::uint64_t geomBytes = gBytes;
125 gPeak = gBytes = 0;
126 gLive.clear(); // the record stays allocated; the scalar's own bytes from here
127 const auto t1 = Clock::now();
128 s.addScalar("c", 0.7, 1, 50, true);
129 s.setScalarWall("c", 1, 1.0, 0.0, -1);
130 s.setScalarSource("c", -0.3);
131 s.solveScalarSteady("c");
132 Kokkos::fence();
133 const double tSolve = seconds(t1);
134 gArmed = false;
135 auto& sc = s.scalarField("c");
136 std::printf(
137 " memory: geometry record (per solver, shared) %.0f B/cell; one single-phase scalar "
138 "(steady, %d MG levels) live %.0f B/cell, peak %.0f B/cell (bound 300)\n",
139 (double)geomBytes / cells, sc.cut->mgLevels, (double)gBytes / cells, (double)gPeak / cells);
140 std::printf(" geometry record build: %.3f s\n", tGeom);
141 std::printf(" first steady solve (assembly + MG build + %d iterations): %.3f s\n",
142 sc.cut->iterations, tSolve);
143
144 const std::size_t ne = sc.c.extent(0);
145 CCField x("bx", ne), y("by", ne);
146 Kokkos::deep_copy(x, sc.c);
147 s.scalarCutMatvec(sc, y, x); // warm-up
148 Kokkos::fence();
149 double best = 1e30, sum = 0.0;
150 for (int r = 0; r < reps; ++r) {
151 const auto t1 = Clock::now();
152 s.scalarCutMatvec(sc, y, x);
153 Kokkos::fence();
154 const double t = seconds(t1);
155 best = std::fmin(best, t);
156 sum += t;
157 }
158 const double bw = 9.0 * 8.0 * (double)ne / best / 1e9;
159 std::printf(
160 " matvec: best %.3f ms, mean %.3f ms; effective %.1f GB/s on 72 B per extended cell = "
161 "%.0f %% of the triad (red flag < 50 %%)\n",
162 1e3 * best, 1e3 * sum / reps, bw, 100.0 * bw / bwStream);
163 }
164 Kokkos::finalize();
165 return 0;
166}
int main()
Definition dbg_scene.cpp:13
flow โ€” host-facing Kokkos IBM Navier-Stokes solver (drop-in flow-style API).
x
Definition cmp.py:24
y
Definition cmp.py:24
Kokkos::View< double *, CCMem > CCField
Solver< Staggered > IbmSolver
static std::vector< double > sphereSdf()