Vlasiator ebf0dd394 on dev (v5.4.0 + 1054 commits)
Loading...
Searching...
No Matches
block_adjust_gpu.cpp
Go to the documentation of this file.
1/*
2 * This file is part of Vlasiator.
3 * Copyright 2010-2024 Finnish Meteorological Institute and University of Helsinki
4 *
5 * For details of usage, see the COPYING file and read the "Rules of the Road"
6 * at http://www.physics.helsinki.fi/vlasiator/
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23#include "block_adjust_gpu.hpp"
25#include "../arch/gpu_base.hpp"
26#include "../object_wrapper.h"
28
29namespace spatial_cell {
30
38 dccrg::Dccrg<spatial_cell::SpatialCell,dccrg::Cartesian_Geometry>& mpiGrid,
39 const vector<CellID>& cells,
40 const uint popID) {
41
42 const uint nCells = cells.size();
43 if (nCells == 0) {
44 return;
45 }
46 if (nCells > 65535) {
47 std::cerr<<"ERROR: too many cells ("<<nCells<<") passed to GPU batch operations! Please use more GPUs / MPI tasks."<<std::endl;
48 abort();
49 }
50
51 // Consider mass loss evaluation?
52 const bool gatherMass = getObjectWrapper().particleSpecies[popID].sparse_conserve_mass;
53
54 const gpuStream_t baseStream = gpu_getStream();
55 // Allocate buffers for GPU operations
56 phiprof::Timer mallocTimer {"allocate buffers for content list analysis"};
57 gpu_batch_allocate(nCells,0);
58
59 gpuMemoryManager.startSession(0,0);
60 SESSION_HOST_ALLOCATE(gpuMemoryManager, vmesh::LocalID, host_nWithContent, nCells * sizeof(vmesh::LocalID));
61 SESSION_HOST_ALLOCATE(gpuMemoryManager, Real, host_mass, nCells * sizeof(Real));
62 SESSION_ALLOCATE(gpuMemoryManager, vmesh::LocalID, dev_nWithContent, nCells * sizeof(vmesh::LocalID));
63 SESSION_ALLOCATE(gpuMemoryManager, Real, dev_mass, nCells * sizeof(Real));
64
65 mallocTimer.stop();
66
67 phiprof::Timer sparsityTimer {"update Sparsity values, apply memory reservations"};
68 size_t largestSizePower = 0;
69 size_t largestVelMesh = 0;
70 #pragma omp parallel
71 {
72 size_t threadLargestVelMesh = 0;
73 size_t threadLargestSizePower = 0;
74 SpatialCell *SC;
75 #pragma omp for schedule(dynamic)
76 for (uint i=0; i<nCells; ++i) {
77 SC = mpiGrid[cells[i]];
79 SC->updateSparseMinValue(popID);
80
82 // Make sure local vectors are large enough
83 const size_t mySize = vmesh->size();
84 SC->setReservation(popID,mySize);
85 SC->applyReservation(popID);
86
87 // might be better to apply reservation *after* clearing maps, but pointers might change.
88 // Store values and pointers
91 (GET_POINTER(gpuMemoryManager, Real, host_minValues))[i] = SC->getVelocityBlockMinValue(popID);
92 (GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), host_allMaps))[2*i] = SC->dev_velocity_block_with_content_map;
93 (GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), host_allMaps))[2*i+1] = SC->dev_velocity_block_with_no_content_map;
94 (GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, host_vbwcl_vec))[i] = SC->dev_velocity_block_with_content_list;
95
96 // Gather largest values
97 threadLargestVelMesh = std::max(threadLargestVelMesh, mySize);
98 threadLargestSizePower = std::max(threadLargestSizePower, (size_t)SC->vbwcl_sizePower);
99 threadLargestSizePower = std::max(threadLargestSizePower, (size_t)SC->vbwncl_sizePower);
100 }
101 #pragma omp critical
102 {
103 largestVelMesh = std::max(threadLargestVelMesh, largestVelMesh);
104 largestSizePower = std::max(threadLargestSizePower, largestSizePower);
105 }
106 }
107 sparsityTimer.stop();
108
109 phiprof::Timer copyTimer {"copy values to device"};
110 // Copy pointers and counters over to device
111 CHK_ERR( gpuMemcpyAsync(GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps), GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), host_allMaps), 2*nCells*sizeof(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), gpuMemcpyHostToDevice, baseStream) );
112 CHK_ERR( gpuMemcpyAsync(GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_vbwcl_vec), GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, host_vbwcl_vec), nCells*sizeof(split::SplitVector<vmesh::GlobalID>*), gpuMemcpyHostToDevice, baseStream) );
113 CHK_ERR( gpuMemcpyAsync(GET_POINTER(gpuMemoryManager, Real, dev_minValues), GET_POINTER(gpuMemoryManager, Real, host_minValues), nCells*sizeof(Real), gpuMemcpyHostToDevice, baseStream) );
116 if (gatherMass) {
117 CHK_ERR( gpuMemsetAsync(GET_SESSION_HOST_POINTER(gpuMemoryManager, Real, dev_mass), 0, nCells*sizeof(Real), baseStream) );
118 }
119 CHK_ERR( gpuStreamSynchronize(baseStream) );
120 copyTimer.stop();
121
122 // Batch clear all hash maps
123 phiprof::Timer clearTimer {"clear all content maps"};
124 clear_maps_caller(nCells,largestSizePower, baseStream);
125 CHK_ERR( gpuStreamSynchronize(baseStream) );
126 clearTimer.stop();
127
128 // Batch gather GID-LID-pairs into two maps (one with content, one without)
129 phiprof::Timer blockKernelTimer {"update content lists kernel"};
130 const dim3 grid2(largestVelMesh,nCells,1);
131 batch_update_velocity_block_content_lists_kernel<<<grid2, WID3, 0, baseStream>>> (
134 GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps),
135 GET_POINTER(gpuMemoryManager, Real, dev_minValues),
136 gatherMass, // Also gathers total mass?
138 );
140 CHK_ERR( gpuStreamSynchronize(baseStream) );
141 blockKernelTimer.stop();
142
143 // Extract all keys from content maps into content list
144 phiprof::Timer extractKeysTimer {"extract content keys"};
145 auto rule = []
146 __device__(const Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID> *map,
147 const Hashinator::hash_pair<vmesh::GlobalID, vmesh::LocalID>& kval,
149 const vmesh::LocalID invalidLID,
150 const vmesh::GlobalID invalidGID) -> bool {
151 // This rule does not use the threshold value
152 const vmesh::GlobalID emptybucket = map->get_emptybucket();
153 const vmesh::GlobalID tombstone = map->get_tombstone();
154 return ( (kval.first != emptybucket) &&( kval.first != tombstone) );
155 };
156 // Go via launcher due to templating
158 GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps), // points to has_content maps
159 GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_vbwcl_vec), // content list vectors, output value
160 GET_SESSION_POINTER(gpuMemoryManager, vmesh::LocalID, dev_nWithContent), // content list vector sizes, output value
161 rule,
162 GET_POINTER(gpuMemoryManager, vmesh::VelocityMesh*, dev_vmeshes), // rule_meshes, not used in this call
163 GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps), // rule_maps, not used in this call
164 GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_vbwcl_vec), // rule_vectors, not used in this call
165 nCells,
166 baseStream
167 );
168 CHK_ERR( gpuStreamSynchronize(baseStream) );
169 extractKeysTimer.stop();
170
171 // Update host-side size values
172 phiprof::Timer blocklistTimer {"update content lists extract"};
175 #pragma omp parallel for schedule(static)
176 for (uint i=0; i<nCells; ++i) {
177 mpiGrid[cells[i]]->velocity_block_with_content_list_size = (GET_SESSION_HOST_POINTER(gpuMemoryManager, vmesh::LocalID, host_nWithContent))[i];
178 mpiGrid[cells[i]]->density_pre_adjust = (GET_SESSION_HOST_POINTER(gpuMemoryManager, Real, host_mass))[i]; // Only one counter per cell, but both this and adjustment are done per-pop before moving to next population.
179 }
180 blocklistTimer.stop();
181 gpuMemoryManager.endSession();
182}
183
185 dccrg::Dccrg<spatial_cell::SpatialCell,dccrg::Cartesian_Geometry>& mpiGrid,
186 const vector<CellID>& cellsToAdjust,
187 const uint popID
188 ) {
189
190 int adjustPreId {phiprof::initializeTimer("Adjusting blocks Pre")};
191 int adjustId {phiprof::initializeTimer("Adjusting blocks")};
192 int cleanupId {phiprof::initializeTimer("Hashmap cleanup")};
193 int adjustPostId {phiprof::initializeTimer("Adjusting blocks Post")};
194 const gpuStream_t baseStream = gpu_getStream();
195 const gpuStream_t priorityStream = gpu_getPriorityStream();
196 const uint nCells = cellsToAdjust.size();
197
198 if (nCells > 65535) {
199 std::cerr<<"ERROR: too many cells ("<<nCells<<") passed to GPU batch operations! Please use more GPUs / MPI tasks."<<std::endl;
200 abort();
201 }
202
203 if(nCells == 0){
204 return;
205 }
206
207 //GPUTODO: make nCells last dimension of grid in dim3(*,*,nCells)?
208 // Allocate buffers for GPU operations
209 phiprof::Timer mallocTimer {"allocate buffers for content list analysis"};
210 gpu_batch_allocate(nCells,0);
211
212 size_t maxNeighbors = 0;
213 size_t largestContentList = 0;
214 size_t largestContentListNeighbors = 0;
215 // Count maximum number of neighbors, largest size of content blocks
216 #pragma omp parallel
217 {
218 size_t threadMaxNeighbors = 0;
219 size_t threadLargestContentList = 0;
220 size_t threadLargestContentListNeighbors = 0;
221 #pragma omp for schedule(dynamic)
222 for (size_t i=0; i<nCells; ++i) {
223 CellID cell_id = cellsToAdjust[i];
224 SpatialCell* SC = mpiGrid[cell_id];
226 continue;
227 }
228 threadLargestContentList = std::max(threadLargestContentList, (size_t)SC->velocity_block_with_content_list_size);
229 size_t cellLargestContentListNeighbors = 0;
230 std::unordered_set<CellID> uniqueNeighbors;
231 const auto* neighbors = mpiGrid.get_neighbors_of(cell_id, Neighborhoods::NEAREST);
232 // find only unique neighbor cells
233 for ( const auto& [neighbor_id, dir] : *neighbors) {
234 cellLargestContentListNeighbors = std::max(cellLargestContentListNeighbors, (size_t)(mpiGrid[neighbor_id]->velocity_block_with_content_list_size));
235 if (neighbor_id != cell_id) {
236 uniqueNeighbors.insert(neighbor_id);
237 }
238 }
239 size_t reservationSize = SC->getReservation(popID);
240 reservationSize = std::max(cellLargestContentListNeighbors, reservationSize);
241 SC->setReservation(popID,reservationSize);
242 SC->applyReservation(popID);
243 size_t nNeighbors = uniqueNeighbors.size();
244 threadMaxNeighbors = std::max(threadMaxNeighbors, nNeighbors);
245 threadLargestContentListNeighbors = std::max(threadLargestContentListNeighbors, cellLargestContentListNeighbors);
246 }
247 #pragma omp critical
248 {
249 maxNeighbors = std::max(maxNeighbors, threadMaxNeighbors);
250 largestContentList = std::max(threadLargestContentList, largestContentList);
251 largestContentListNeighbors = std::max(threadLargestContentListNeighbors, largestContentListNeighbors);
252 }
253 } // end parallel region
254
255 // Early return if empty region for this population (
256 // GPUTODO FIX: BREAKS VLASOV SUBSTEPPING
257 // if (largestContentList==largestContentListNeighbors==0) {
258 // return;
259 // }
260 gpu_batch_allocate(nCells,maxNeighbors);
261
263 SESSION_HOST_ALLOCATE(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, host_vbwcl_neigh, maxNeighbors * nCells * sizeof(split::SplitVector<vmesh::GlobalID>*));
264 SESSION_ALLOCATE(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_vbwcl_neigh, maxNeighbors * nCells * sizeof(split::SplitVector<vmesh::GlobalID>*));
265
266 mallocTimer.stop();
267
268 size_t largestVelMesh = 0;
269 #pragma omp parallel
270 {
271 phiprof::Timer timer {adjustPreId};
272 size_t threadLargestVelMesh = 0;
273 #pragma omp for schedule(dynamic)
274 for (size_t i=0; i<nCells; ++i) {
275 CellID cell_id=cellsToAdjust[i];
276 SpatialCell* SC = mpiGrid[cell_id];
278 (GET_POINTER(gpuMemoryManager, vmesh::VelocityMesh*, host_vmeshes))[i]=0;
279 (GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), host_allMaps))[2*i]=0;
280 (GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), host_allMaps))[2*i+1]=0;
281 (GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, host_vbwcl_vec))[i]=0;
282 (GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, host_lists_with_replace_new))[i]=0;
283 continue;
284 }
285
286 // Gather largest mesh size for launch parameters
287 vmesh::VelocityMesh* vmesh = SC->get_velocity_mesh(popID);
288 threadLargestVelMesh = std::max(threadLargestVelMesh, vmesh->size());
289
290 // gather vector with pointers to spatial neighbor lists
291 const auto* neighbors = mpiGrid.get_neighbors_of(cell_id, Neighborhoods::NEAREST);
292 // Note: at AMR refinement boundaries this can cause blocks to propagate further
293 // than absolutely required. Face neighbors, however, are not enough as we must
294 // account for diagonal propagation.
295
296 // find only unique neighbor cells
297 std::unordered_set<CellID> uniqueNeighbors;
298 for ( const auto& [neighbor_id, dir] : *neighbors) {
299 if (neighbor_id != cell_id) {
300 uniqueNeighbors.insert(neighbor_id);
301 }
302 }
303 std::vector<CellID> reducedNeighbors;
304 reducedNeighbors.insert(reducedNeighbors.end(), uniqueNeighbors.begin(), uniqueNeighbors.end());
305 const uint nNeighbors = reducedNeighbors.size();
306 for (uint iN = 0; iN < maxNeighbors; ++iN) {
307 if (iN >= nNeighbors) {
308 (GET_SESSION_HOST_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, host_vbwcl_neigh))[i*maxNeighbors + iN] = 0; // no neighbor at this index
309 continue;
310 }
311 CellID neighbor_id = reducedNeighbors.at(iN);
312 // store pointer to neighbor content list
313 SpatialCell* NC = mpiGrid[neighbor_id];
315 (GET_SESSION_HOST_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, host_vbwcl_neigh))[i*maxNeighbors + iN] = 0;
316 } else {
317 (GET_SESSION_HOST_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, host_vbwcl_neigh))[i*maxNeighbors + iN] = mpiGrid[neighbor_id]->dev_velocity_block_with_content_list;
318 }
319 }
320
321 // Store values and pointers
322 (GET_POINTER(gpuMemoryManager, vmesh::VelocityMesh*, host_vmeshes))[i] = SC->dev_get_velocity_mesh(popID);
323 (GET_POINTER(gpuMemoryManager, vmesh::VelocityBlockContainer*, host_VBCs))[i] = SC->dev_get_velocity_blocks(popID);
324 (GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), host_allMaps))[2*i] = SC->dev_velocity_block_with_content_map;
325 (GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), host_allMaps))[2*i+1] = SC->dev_velocity_block_with_no_content_map;
326 (GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, host_vbwcl_vec))[i] = SC->dev_velocity_block_with_content_list;
327 (GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, host_lists_with_replace_new))[i] = SC->dev_list_with_replace_new;
328 (GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), host_lists_delete))[i] = SC->dev_list_delete;
329 (GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), host_lists_to_replace))[i] = SC->dev_list_to_replace;
330 (GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), host_lists_with_replace_old))[i] = SC->dev_list_with_replace_old;
331 }
332 timer.stop();
333 #pragma omp critical
334 {
335 largestVelMesh = std::max(threadLargestVelMesh, largestVelMesh);
336 }
337 } // end parallel region
338
339 /*
340 * Perform block adjustment via batch operations
341 * */
342 phiprof::Timer copyTimer {"copy values to device"};
343 // Copy pointers and counters over to device
344 CHK_ERR( gpuMemcpyAsync(GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps), GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), host_allMaps), 2*nCells*sizeof(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), gpuMemcpyHostToDevice, baseStream) );
345 CHK_ERR( gpuMemcpyAsync(GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_vbwcl_vec), GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, host_vbwcl_vec), nCells*sizeof(split::SplitVector<vmesh::GlobalID>*), gpuMemcpyHostToDevice, baseStream) );
346 CHK_ERR( gpuMemcpyAsync(GET_POINTER(gpuMemoryManager, vmesh::VelocityMesh*, dev_vmeshes), GET_POINTER(gpuMemoryManager, vmesh::VelocityMesh*, host_vmeshes), nCells*sizeof(vmesh::VelocityMesh*), gpuMemcpyHostToDevice, baseStream) );
347 if (maxNeighbors>0) {
348 CHK_ERR( gpuMemcpyAsync(GET_SESSION_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_vbwcl_neigh), GET_SESSION_HOST_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, host_vbwcl_neigh), nCells*maxNeighbors*sizeof(split::SplitVector<vmesh::GlobalID>*), gpuMemcpyHostToDevice, baseStream) );
349 }
350 CHK_ERR( gpuMemsetAsync(GET_POINTER(gpuMemoryManager, vmesh::LocalID, dev_nBefore), 0, nCells*sizeof(vmesh::LocalID), baseStream) );
351 CHK_ERR( gpuMemsetAsync(GET_POINTER(gpuMemoryManager, vmesh::LocalID, dev_nAfter), 0, nCells*sizeof(vmesh::LocalID), baseStream) );
352 CHK_ERR( gpuMemsetAsync(GET_POINTER(gpuMemoryManager, vmesh::LocalID, dev_nBlocksToChange), 0, nCells*sizeof(vmesh::LocalID), baseStream) );
355 CHK_ERR( gpuMemcpyAsync(GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_lists_with_replace_new), GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, host_lists_with_replace_new), nCells*sizeof(split::SplitVector<vmesh::GlobalID>*), gpuMemcpyHostToDevice, baseStream) );
356 CHK_ERR( gpuMemcpyAsync(GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), dev_lists_delete), GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), host_lists_delete), nCells*sizeof(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), gpuMemcpyHostToDevice, baseStream) );
357 CHK_ERR( gpuMemcpyAsync(GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), dev_lists_to_replace), GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), host_lists_to_replace), nCells*sizeof(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), gpuMemcpyHostToDevice, baseStream) );
358 CHK_ERR( gpuMemcpyAsync(GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), dev_lists_with_replace_old), GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), host_lists_with_replace_old), nCells*sizeof(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), gpuMemcpyHostToDevice, baseStream) );
359 CHK_ERR( gpuMemcpyAsync(GET_POINTER(gpuMemoryManager, vmesh::VelocityBlockContainer*, dev_VBCs), GET_POINTER(gpuMemoryManager, vmesh::VelocityBlockContainer*, host_VBCs), nCells*sizeof(vmesh::VelocityBlockContainer*), gpuMemcpyHostToDevice, baseStream) );
360 CHK_ERR( gpuStreamSynchronize(baseStream) );
361 copyTimer.stop();
362
363 // Note: Velocity halo and spatial neighbor halo can both be evaluated simultaneously.
364 // Thus, we launch one into the prioritystream, the other into baseStream.
365
366 // Evaluate velocity halo for local content blocks
367 phiprof::Timer blockHaloTimer {"Block halo batch kernels"};
368 const int addWidthV = getObjectWrapper().particleSpecies[popID].sparseBlockAddWidthV;
369 if (addWidthV!=1) {
370 std::cerr<<"Error! "<<__FILE__<<":"<<__LINE__<<" Halo extent is not 1, unsupported size."<<std::endl;
371 abort();
372 }
373 // Halo of 1 in each direction adds up to 26 velocity neighbors.
374
375 if (largestContentList > 0) {
376 #ifdef USE_BATCH_WARPACCESSORS
377 // For NVIDIA/CUDA, we can do 26 neighbors and 32 threads per warp in a single block.
378 // For AMD/HIP, we can do 13 neighbors and 64 threads per warp in a single block, meaning two loops per cell.
379 // In either case, we launch blocks equal to largest found velocity_block_with_content_list_size, which was stored
380 // into largestContentList
381 dim3 grid_vel_halo(largestContentList,nCells,1);
383 GET_POINTER(gpuMemoryManager, vmesh::VelocityMesh*, dev_vmeshes),
384 GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_vbwcl_vec),
385 GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps) // Needs both content and no content maps
386 );
388 #else
389 const uint warpsPerBlockBatchHalo = (threadsPerMP/GPUTHREADS + blocksPerMP - 1)/blocksPerMP;
390 dim3 grid_vel_halo((largestContentList + warpsPerBlockBatchHalo - 1)/warpsPerBlockBatchHalo,nCells,1);
391 dim3 block_vel_halo(GPUTHREADS, warpsPerBlockBatchHalo, 1);
392 // We do 26 (launch with GPUTHREADS) neighbors in a single block at a time.
394 GET_POINTER(gpuMemoryManager, vmesh::VelocityMesh*, dev_vmeshes),
395 GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_vbwcl_vec),
396 GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps), // Needs both content and no content maps
397 warpsPerBlockBatchHalo
398 );
400 #endif
401 // CHK_ERR( gpuStreamSynchronize(priorityStream) );
402 }
403
404 if (maxNeighbors>0 && largestContentListNeighbors>0) {
405 // largestContentListNeighbors accounts for remote (ghost neighbor) content list sizes as well
406 #ifdef USE_BATCH_WARPACCESSORS
407 // ceil int division
408 const size_t blocksNeeded_neigh = 1 + ((largestContentListNeighbors - 1) / (WARPSPERBLOCK));
409 dim3 grid_neigh_halo(blocksNeeded_neigh,nCells,maxNeighbors);
410 // For NVIDIA/CUDA, we can do 32 neighbor GIDs and 32 threads per warp in a single block.
411 // For AMD/HIP, we can do 16 neighbor GIDs and 64 threads per warp in a single block
412 // This is handled in-kernel.
413 batch_update_neighbour_halo_kernel<<<grid_neigh_halo, WARPSPERBLOCK*GPUTHREADS, 0, baseStream>>> (
414 GET_POINTER(gpuMemoryManager, vmesh::VelocityMesh*, dev_vmeshes),
415 GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps), // Needs both has_content and has_no_content maps
416 GET_SESSION_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_vbwcl_neigh)
417 );
419 #else
420 // Try smaller launch for more spatial cell -parallelism
421 const size_t blocksNeeded_neigh = 1 + ((largestContentListNeighbors - 1) / (WARPSPERBLOCK*GPUTHREADS));
422 dim3 grid_neigh_halo(blocksNeeded_neigh,nCells,maxNeighbors);
423 // Each threads manages a single GID from the neighbour at hand
424 batch_update_neighbour_halo_kernel<<<grid_neigh_halo, WARPSPERBLOCK*GPUTHREADS, 0, baseStream>>> (
425 GET_POINTER(gpuMemoryManager, vmesh::VelocityMesh*, dev_vmeshes),
426 GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps), // Needs both has_content and has_no_content maps
427 GET_SESSION_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_vbwcl_neigh)
428 );
430 #endif
431 }
432 // Sync both streams
433 CHK_ERR( gpuStreamSynchronize(priorityStream) );
434 CHK_ERR( gpuStreamSynchronize(baseStream) );
435 //CHK_ERR( gpuDeviceSynchronize() );
436 blockHaloTimer.stop();
438
439 // Ensure vectors in dev_lists_with_replace_new have sufficient capacity for has_content_maps.
440 // Launch kernel which accesses the vector capacities with the map sizes and stores the required capacity
441 // for vectors in a buffer (or 0 to indicate no need to recapacitate). After that, copy that buffer to host,
442 // go through it, recapcitate as necessary, and if any recapacitiations happened, update the
443 // dev_lists_with_replace_new buffer with new vector addresses and upload it to device again.
444 // (this re-uploading is probably not needed, would need verifying that splitvector device handles don't get
445 // reallocated)
446 check_vector_capacities<<<nCells,1,0,baseStream>>>(
447 GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps),
448 GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_lists_with_replace_new),
450 );
453 CHK_ERR( gpuStreamSynchronize(baseStream) );
454 bool reUpload = false;
455 for (size_t i=0; i<nCells; ++i) {
456 if ((GET_POINTER(gpuMemoryManager, vmesh::LocalID, host_overflownElements))[i] != 0) {
457 reUpload = true;
458 CellID cell_id = cellsToAdjust[i];
459 SpatialCell* SC = mpiGrid[cell_id];
461 SC->applyReservation(popID);
462 (GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, host_lists_with_replace_new))[i] = SC->dev_list_with_replace_new;
463 }
464 }
467 if (reUpload) {
468 CHK_ERR( gpuMemcpyAsync(GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_lists_with_replace_new), GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, host_lists_with_replace_new), nCells*sizeof(split::SplitVector<vmesh::GlobalID>*), gpuMemcpyHostToDevice, baseStream) );
469 CHK_ERR( gpuStreamSynchronize(baseStream) );
470 }
471
485 phiprof::Timer extractKeysTimer {"extract content keys"};
486 // Go via caller, then launcher due to templating. Templating manages rule lambda type,
487 // output vector type, as well as a flag whether the output vector should take the whole
488 // element from the map, or just the first of the pair.
489
490 // Finds new Blocks (GID,LID) needing to be added
491 // Note:list_with_replace_new then contains both new GIDs to use for replacements and new GIDs to place at end of vmesh
493 GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps), // input maps: this is has_content_maps
494 GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_lists_with_replace_new), // output vecs
495 NULL, // pass null to not store vector lengths
496 GET_POINTER(gpuMemoryManager, vmesh::VelocityMesh*, dev_vmeshes), // rule_meshes, not used in this call
497 GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps)+1, // rule_maps, not used in this call
498 GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_vbwcl_vec), // rule_vectors, not used in this call
499 nCells,
500 baseStream
501 ); // This needs to complete before the next 3 extractions
502 // Finds Blocks (GID,LID) to be rescued from end of v-space
504 GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps), // input maps: this is has_content_maps
505 GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), dev_lists_with_replace_old), // output vecs
506 NULL, // pass null to not store vector lengths
507 GET_POINTER(gpuMemoryManager, vmesh::VelocityMesh*, dev_vmeshes), // rule_meshes
508 GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps)+1, // rule_maps: this is has_no_content_maps
509 GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_lists_with_replace_new), // rule_vectors
510 nCells,
511 baseStream
512 );
513 // Find Blocks (GID,LID) to be outright deleted
515 GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps)+1, // input maps: this is has_no_content_maps
516 GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), dev_lists_delete), // output vecs
517 NULL, // pass null to not store vector lengths
518 GET_POINTER(gpuMemoryManager, vmesh::VelocityMesh*, dev_vmeshes), // rule_meshes
519 GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps)+1, // rule_maps: this is has_no_content_maps
520 GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_lists_with_replace_new), // rule_vectors
521 nCells,
522 baseStream
523 );
524 // Find Blocks (GID,LID) to be replaced with new ones
526 GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps)+1, // input maps: this is has_no_content_maps
527 GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), dev_lists_to_replace), // output vecs
528 NULL, // pass null to not store vector lengths
529 GET_POINTER(gpuMemoryManager, vmesh::VelocityMesh*, dev_vmeshes), // rule_meshes
530 GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps)+1, // rule_maps: this is has_no_content_maps
531 GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_lists_with_replace_new), // rule_vectors
532 nCells,
533 baseStream
534 );
535 CHK_ERR( gpuStreamSynchronize(baseStream) );
536 extractKeysTimer.stop();
537
538 // Call sub-function for actual block adjustment (including resizing vmeshes)
539 // This same sub-function is also called from acceleration (TODO).
540 // We don't need to give host or device arrays as parameters as they are universal.
541 uint largestBlocksToChange = 0;
542 uint largestBlocksBeforeOrAfter = 0;
544 cellsToAdjust,
545 0, // no offset
546 largestBlocksToChange,
547 largestBlocksBeforeOrAfter,
548 popID);
549
550 /* Batch tombstone cleaning
551 * Extract all entries (GID,LID) which are overflown (see Hashinator for further details). At same time,
552 * remove tombstones and overflown elements.
553 *
554 * By calling a few kernels which operate over all spatial cells at once instead of launching a few kernels per cell,
555 * we reduce operational time by circa 10x.
556 */
557 phiprof::Timer tombstoneTimer {"GPU batch clean tombstones"};
558 auto rule_overflown = []
559 __device__(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID> *map,
560 Hashinator::hash_pair<vmesh::GlobalID, vmesh::LocalID>& kval) -> bool {
561 const vmesh::GlobalID emptybucket = map->get_emptybucket();
562 const vmesh::GlobalID tombstone = map->get_tombstone();
563 if (kval.first == emptybucket) {
564 return false;
565 }
566 if (kval.first == tombstone) {
567 // Note: tombstones preceding overflown are deleted, so
568 // resetting overflown elements after this cannot rely on
569 // tombstones.
570 kval.first = emptybucket;
571 return false;
572 }
573 const size_t currentSizePower = map->getSizePower();
574 Hashinator::hash_pair<vmesh::GlobalID, vmesh::LocalID> *bck_ptr = map->expose_bucketdata<false>();
575 //const size_t hashIndex = Hashinator::HashFunction::_hash(kval.first, currentSizePower);
576 const size_t hashIndex = map->hash(kval.first);
577 const int bitMask = (1 << (currentSizePower)) - 1;
578 const bool isOverflown = (bck_ptr[hashIndex & bitMask].first != kval.first);
579 return isOverflown;
580 };
582 GET_POINTER(gpuMemoryManager, vmesh::VelocityMesh*, dev_vmeshes), // velocity meshes which include the hash maps to clean
583 GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), dev_lists_with_replace_old), // use this for storing overflown elements
584 GET_POINTER(gpuMemoryManager, vmesh::LocalID, dev_overflownElements), // return values: n_overflown_elements
585 rule_overflown,
586 nCells,
587 baseStream
588 );
589 // Re-insert overflown elements back in vmeshes. First calculate
590 // Launch parameters after using blocking memcpy to get overflow counts
592 CHK_ERR( gpuStreamSynchronize(baseStream) );
593 uint largestOverflow = 0;
594 #pragma omp parallel
595 {
596 uint thread_largestOverflow = 0;
597 #pragma omp for schedule(static)
598 for (size_t i=0; i<nCells; ++i) {
599 thread_largestOverflow = std::max(thread_largestOverflow, (GET_POINTER(gpuMemoryManager, vmesh::LocalID, host_overflownElements))[i]);
600 }
601 #pragma omp critical
602 {
603 largestOverflow = std::max(thread_largestOverflow, largestOverflow);
604 }
605 } // end parallel region
606 if (largestOverflow > 0) {
607 dim3 grid_reinsert(largestOverflow,nCells,1);
608 batch_insert_kernel<<<grid_reinsert, GPUTHREADS, 0, baseStream>>>(
609 GET_POINTER(gpuMemoryManager, vmesh::VelocityMesh*, dev_vmeshes), // velocity meshes which include the hash maps to clean
610 GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), dev_lists_with_replace_old) // use this for storing overflown elements
611 );
613 CHK_ERR( gpuStreamSynchronize(baseStream) );
614 }
615 tombstoneTimer.stop();
616
617 #pragma omp parallel
618 {
619 #pragma omp for schedule(dynamic)
620 for (size_t i=0; i<nCells; ++i) {
621 SpatialCell* SC = mpiGrid[cellsToAdjust[i]];
623 SC->get_velocity_mesh(popID)->setNewCachedSize(0);
624 SC->get_velocity_blocks(popID)->setNewCachedSize(0);
625 continue;
626 }
627 // Perform hashmap cleanup here (instead of at acceleration mid-steps)
628 phiprof::Timer cleanupTimer {cleanupId};
629 //SC->get_velocity_mesh(popID)->gpu_cleanHashMap(gpu_getStream());
630 //SC->dev_upload_population(popID);
631 cleanupTimer.stop();
632
633 phiprof::Timer postTimer {adjustPostId};
634 #ifdef DEBUG_SPATIAL_CELL
635 // Not re-doing old debug here, this should be enough
636 SC->checkSizes(popID);
637 #endif
638 #ifdef DEBUG_VLASIATOR
639 // This is a bit extreme
640 SC->checkMesh(popID);
641 #endif
642
643 if (getObjectWrapper().particleSpecies[popID].sparse_conserve_mass) {
644 // Block adjustment can only add empty blocks or delete existing blocks,
645 // So post_adjust density must be equal to pre_adjust density minus mass loss.
647 if ( (SC->density_post_adjust > 0.0) && ((GET_POINTER(gpuMemoryManager, Real, host_massLoss))[i] != 0) ) {
648 //SC->scale_population(SC->density_pre_adjust/SC->density_post_adjust, popID);
649 // Now use the massloss buffer for the scaling value
650 const Real mass_scaling = SC->density_pre_adjust/SC->density_post_adjust;
651 (GET_POINTER(gpuMemoryManager, Real, host_massLoss))[i] = mass_scaling;
652 } else {
653 // Skip scaling this cell
654 (GET_POINTER(gpuMemoryManager, Real, host_massLoss))[i] = 0;
655 }
656 } // end if conserve mass
657 postTimer.stop();
658 } // end cell loop
659 } // end parallel region
660
661 if ( (getObjectWrapper().particleSpecies[popID].sparse_conserve_mass)
662 && (largestBlocksToChange > 0) ) {
663 phiprof::Timer massConservationTimer {"GPU batch conserve mass"};
664 CHK_ERR( gpuMemcpyAsync(GET_POINTER(gpuMemoryManager, Real, dev_massLoss), GET_POINTER(gpuMemoryManager, Real, host_massLoss), nCells*sizeof(Real), gpuMemcpyHostToDevice, baseStream) );
665 // Launch parameters: Although post-adjustment, some VBCs can have more blocks than when entering
666 // block adjustment, any new blocks will be empty and thus do not need to be scaled. Thus, we can use
667 // The count which is the gathered max value over all cells of a counter which is either blocksBeforeAdjust
668 // or BlocksAfterAdjust, whichever is smaller.
669
670 // Third argument specifies the number of bytes in *shared memory* that is
671 // dynamically allocated per block for this call in addition to the statically allocated memory.
672 dim3 grid_mass_conservation(largestBlocksBeforeOrAfter,nCells,1);
673 batch_population_scale_kernel<<<grid_mass_conservation, WID3, 0, baseStream>>> (
674 GET_POINTER(gpuMemoryManager, vmesh::VelocityBlockContainer*, dev_VBCs),
675 GET_POINTER(gpuMemoryManager, Real, dev_massLoss) // used now for scaling parameter
676 );
678 CHK_ERR( gpuStreamSynchronize(baseStream) );
679 }
680}
681
682void clear_maps_caller(const uint nCells,
683 const size_t largestSizePower,
684 gpuStream_t stream,
685 const size_t offset
686 ) {
687 const size_t largestMapSize = std::pow(2,largestSizePower);
688 // fast ceil for positive ints
689 //const size_t blocksNeeded = 1 + ((largestMapSize - 1) / Hashinator::defaults::MAX_BLOCKSIZE);
690 size_t blocksNeeded = 1 + floor(sqrt(largestMapSize / Hashinator::defaults::MAX_BLOCKSIZE)-1);
691 blocksNeeded = std::max((size_t)1, blocksNeeded);
692 dim3 grid1(blocksNeeded,nCells,2);
693 batch_reset_all_to_empty<<<grid1, Hashinator::defaults::MAX_BLOCKSIZE, 0, stream>>>(
694 GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps)+2*offset
695 );
697 CHK_ERR( gpuStreamSynchronize(stream) );
698}
699
700
702 dccrg::Dccrg<spatial_cell::SpatialCell,dccrg::Cartesian_Geometry>& mpiGrid,
703 const vector<CellID>& cellsToAdjust,
704 const uint cellOffset,
705 uint &out_largestBlocksToChange,
706 uint &out_largestBlocksBeforeOrAfter,
707 const uint popID
708 ) {
709
710 const uint nCells = cellsToAdjust.size();
711 if (nCells == 0) {
712 return;
713 }
714 const gpuStream_t baseStream = gpu_getStream();
715
716 // Resizes are faster this way with larger grid and single thread per block.
717 phiprof::Timer deviceResizeTimer {"GPU resize mesh on-device"};
721 GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_lists_with_replace_new)+cellOffset,
722 GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), dev_lists_delete)+cellOffset,
723 GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), dev_lists_to_replace)+cellOffset,
724 GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), dev_lists_with_replace_old)+cellOffset,
729 GET_POINTER(gpuMemoryManager, Real, dev_massLoss)+cellOffset // mass loss, set to zero
730 );
736 CHK_ERR( gpuStreamSynchronize(baseStream) );
737 deviceResizeTimer.stop();
738
739 phiprof::Timer hostResizeTimer {"GPU resize mesh from host "};
740 uint largestBlocksToChange = 0;
741 uint largestBlocksBeforeOrAfter = 0;
742 // This loop appears to be faster non-threaded!
743 for (size_t i=0; i<nCells; ++i) {
744 SpatialCell* SC = mpiGrid[cellsToAdjust[i]];
746 continue;
747 }
748 // Grow mesh if necessary and on-device resize did not work??
749 const vmesh::LocalID nBlocksBeforeAdjust = (GET_POINTER(gpuMemoryManager, vmesh::LocalID, host_nBefore))[i+cellOffset];
750 const vmesh::LocalID nBlocksAfterAdjust = (GET_POINTER(gpuMemoryManager, vmesh::LocalID, host_nAfter))[i+cellOffset];
751 const vmesh::LocalID nBlocksToChange = (GET_POINTER(gpuMemoryManager, vmesh::LocalID, host_nBlocksToChange))[i+cellOffset];
752 const vmesh::LocalID resizeDevSuccess = (GET_POINTER(gpuMemoryManager, vmesh::LocalID, host_resizeSuccess))[i+cellOffset];
753 largestBlocksToChange = std::max(largestBlocksToChange, nBlocksToChange);
754 // This is gathered for mass loss correction: for each cell, we want the smaller of either blocks before or after. Then,
755 // we want to gather the largest of those values.
756 const vmesh::LocalID lowBlocks = std::min(nBlocksBeforeAdjust, nBlocksAfterAdjust);
757 largestBlocksBeforeOrAfter = std::max(largestBlocksBeforeOrAfter, lowBlocks);
758 if ( (nBlocksAfterAdjust > nBlocksBeforeAdjust) && (resizeDevSuccess == 0)) {
759 //GPUTODO is _FACTOR enough instead of _PADDING?
760 SC->get_velocity_mesh(popID)->setNewCapacity(nBlocksAfterAdjust*BLOCK_ALLOCATION_PADDING);
761 SC->get_velocity_mesh(popID)->setNewSize(nBlocksAfterAdjust);
762 SC->get_velocity_blocks(popID)->setNewCapacity(nBlocksAfterAdjust*BLOCK_ALLOCATION_PADDING);
763 SC->get_velocity_blocks(popID)->setNewSize(nBlocksAfterAdjust);
764 SC->dev_upload_population(popID);
765 }
766 // Update cached sizes
767 SC->get_velocity_mesh(popID)->setNewCachedSize(nBlocksAfterAdjust);
768 SC->get_velocity_blocks(popID)->setNewCachedSize(nBlocksAfterAdjust);
769 } // end cell loop
771 hostResizeTimer.stop();
772 // Writing directly into pass-by-reference variables from within OMP parallel region caused issues
773 out_largestBlocksToChange = largestBlocksToChange;
774 out_largestBlocksBeforeOrAfter = largestBlocksBeforeOrAfter;
775
776 // Do we actually have any changes to perform?
777 if (largestBlocksToChange > 0) {
778 phiprof::Timer addRemoveKernelTimer {"GPU batch add and remove blocks kernel"};
779 // Third argument specifies the number of bytes in *shared memory* that is
780 // dynamically allocated per block for this call in addition to the statically allocated memory.
781 dim3 grid_addremove(largestBlocksToChange,nCells,1);
782 // Launch grid is sized so that for all spatial cells, we launch up to the maximum number of required
783 // operations (add a block, delete a block, replace a block with a new one, replace a block with an existing one)
784 batch_update_velocity_blocks_kernel<<<grid_addremove, WID3, 0, baseStream>>> (
787 GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_lists_with_replace_new)+cellOffset,
788 GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), dev_lists_delete)+cellOffset,
789 GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), dev_lists_to_replace)+cellOffset,
790 GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), dev_lists_with_replace_old)+cellOffset,
795 );
797 // Pull mass loss values to host
799 CHK_ERR( gpuStreamSynchronize(baseStream) );
800 // Update mass Loss (not worth threading)
801 for (size_t i=0; i<nCells; ++i) {
802 SpatialCell* SC = mpiGrid[cellsToAdjust[i]];
804 continue;
805 }
806 SC->increment_mass_loss(popID, (GET_POINTER(gpuMemoryManager, Real, host_massLoss))[i]);
807 }
808 addRemoveKernelTimer.stop();
809
810 // Should not re-allocate on shrinking, so do on-device
811 phiprof::Timer deviceResizePostTimer {"GPU resize mesh on-device post"};
812 // Resizes are faster this way with larger grid and single thread
817 );
819 CHK_ERR( gpuStreamSynchronize(baseStream) );
820 deviceResizePostTimer.stop();
821 }
822}
823
825 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>** input_maps,
826 split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>> **output_vecs,
827 vmesh::LocalID* output_sizes,
828 vmesh::VelocityMesh** rule_meshes,
829 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>** rule_maps,
830 split::SplitVector<vmesh::GlobalID>** rule_vectors,
831 const uint nCells,
832 gpuStream_t stream
833 ) {
834 auto rule_to_replace = [] __device__(const Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID> *map,
835 const Hashinator::hash_pair<vmesh::GlobalID, vmesh::LocalID>& kval,
837 const vmesh::LocalID invalidLID,
838 const vmesh::GlobalID invalidGID) -> bool {
839 const vmesh::GlobalID emptybucket = map->get_emptybucket();
840 const vmesh::GlobalID tombstone = map->get_tombstone();
841 return kval.first != emptybucket &&
842 kval.first != tombstone &&
843 kval.first != invalidGID &&
844 kval.second < threshold &&
845 kval.second != invalidLID;
846 };
847
848 // Find Blocks (GID,LID) to be replaced with new ones
849 extract_GIDs_kernel_launcher<decltype(rule_to_replace),Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>,false>(
850 input_maps,
851 output_vecs,
852 output_sizes,
853 rule_to_replace,
854 rule_meshes,
855 rule_maps,
856 rule_vectors,
857 nCells,
858 stream
859 );
860}
861
863 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>** input_maps,
864 split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>> **output_vecs,
865 vmesh::LocalID* output_sizes,
866 vmesh::VelocityMesh** rule_meshes,
867 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>** rule_maps,
868 split::SplitVector<vmesh::GlobalID>** rule_vectors,
869 const uint nCells,
870 gpuStream_t stream
871 ) {
872 auto rule_delete_move = [] __device__(const Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID> *map,
873 const Hashinator::hash_pair<vmesh::GlobalID, vmesh::LocalID>& kval,
875 const vmesh::LocalID invalidLID,
876 const vmesh::GlobalID invalidGID) -> bool {
877 const vmesh::GlobalID emptybucket = map->get_emptybucket();
878 const vmesh::GlobalID tombstone = map->get_tombstone();
879 return kval.first != emptybucket &&
880 kval.first != tombstone &&
881 kval.first != invalidGID &&
882 kval.second >= threshold &&
883 kval.second != invalidLID;
884 };
885 extract_GIDs_kernel_launcher<decltype(rule_delete_move),Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>,false>(
886 input_maps,
887 output_vecs,
888 output_sizes,
889 rule_delete_move,
890 rule_meshes,
891 rule_maps,
892 rule_vectors,
893 nCells,
894 stream
895 );
896}
897
899 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>** input_maps,
900 split::SplitVector<vmesh::GlobalID> **output_vecs,
901 vmesh::LocalID* output_sizes,
902 vmesh::VelocityMesh** rule_meshes,
903 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>** rule_maps,
904 split::SplitVector<vmesh::GlobalID>** rule_vectors,
905 const uint nCells,
906 gpuStream_t stream
907 ) {
908 auto rule_add = [] __device__(const Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID> *map,
909 const Hashinator::hash_pair<vmesh::GlobalID, vmesh::LocalID>& kval,
911 const vmesh::LocalID invalidLID,
912 const vmesh::GlobalID invalidGID) -> bool {
913 // This rule does not use the threshold value
914 const vmesh::GlobalID emptybucket = map->get_emptybucket();
915 const vmesh::GlobalID tombstone = map->get_tombstone();
916 return kval.first != emptybucket &&
917 kval.first != tombstone &&
918 kval.first != invalidGID &&
919 // Required GIDs which do not yet exist in vmesh were stored in
920 // velocity_block_with_content_map with kval.second==invalidLID
921 kval.second == invalidLID;
922 };
924 input_maps,
925 output_vecs,
926 output_sizes,
927 rule_add,
928 rule_meshes,
929 rule_maps,
930 rule_vectors,
931 nCells,
932 stream
933 );
934}
935
936} // namespace
for i
Definition Dispersion.m:24
sqrt(1.0+vA *vA/(c *c))) % Ion-acoustic wave cS
#define gpuPeekAtLastError
#define WARPSPERBLOCK
#define gpuStream_t
#define gpuStreamSynchronize
#define gpuMemcpyHostToDevice
#define CHK_ERR(err)
#define gpuMemcpy
#define gpuMemcpyDeviceToHost
#define gpuMemcpyAsync
#define gpuDeviceSynchronize
#define gpuMemsetAsync
#define GPUTHREADS
__global__ void batch_update_velocity_halo_kernel(const vmesh::VelocityMesh *__restrict__ const *vmeshes, const split::SplitVector< vmesh::GlobalID > *__restrict__ const *velocity_block_with_content_lists, Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > **allMaps, const uint warpsPerBlockBatchHalo)
__global__ void batch_resize_vbc_kernel_pre(vmesh::VelocityMesh **vmeshes, vmesh::VelocityBlockContainer **blockContainers, split::SplitVector< vmesh::GlobalID > **dev_list_with_replace_new, split::SplitVector< Hashinator::hash_pair< vmesh::GlobalID, vmesh::LocalID > > **dev_list_delete, split::SplitVector< Hashinator::hash_pair< vmesh::GlobalID, vmesh::LocalID > > **dev_list_to_replace, split::SplitVector< Hashinator::hash_pair< vmesh::GlobalID, vmesh::LocalID > > **dev_list_with_replace_old, vmesh::LocalID *dev_nBefore, vmesh::LocalID *dev_nAfter, vmesh::LocalID *dev_nBlocksToChange, vmesh::LocalID *dev_resizeSuccess, Real *dev_rhoLossAdjust)
__global__ void batch_resize_vbc_kernel_post(vmesh::VelocityMesh **vmeshes, vmesh::VelocityBlockContainer **blockContainers, vmesh::LocalID *dev_nAfter)
void extract_GIDs_kernel_launcher(Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > **input_maps, split::SplitVector< ELEMENT > **output_vecs, vmesh::LocalID *output_sizes, Rule rule, vmesh::VelocityMesh **rule_meshes, Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > **rule_maps, split::SplitVector< vmesh::GlobalID > **rule_vectors, const uint nCells, gpuStream_t stream)
void clean_tombstones_launcher(vmesh::VelocityMesh **vmeshes, split::SplitVector< Hashinator::hash_pair< vmesh::GlobalID, vmesh::LocalID > > **overflown_elements, vmesh::LocalID *output_sizes, Rule rule, const uint nCells, gpuStream_t stream)
bool checkMesh(const uint popID)
split::SplitVector< vmesh::GlobalID > * dev_velocity_block_with_content_list
void updateSparseMinValue(const uint popID)
vmesh::VelocityMesh * get_velocity_mesh(const size_t &popID)
vmesh::VelocityBlockContainer * get_velocity_blocks(const size_t &popID)
bool checkSizes(const uint popID)
Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > * dev_velocity_block_with_no_content_map
void dev_upload_population(const uint popID)
vmesh::LocalID getReservation(const uint popID) const
Real getVelocityBlockMinValue(const uint popID) const
Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > * dev_velocity_block_with_content_map
void increment_mass_loss(cuint popID, Real increment)
split::SplitVector< Hashinator::hash_pair< vmesh::GlobalID, vmesh::LocalID > > * dev_list_to_replace
split::SplitVector< vmesh::GlobalID > * dev_list_with_replace_new
void applyReservation(const uint popID)
split::SplitVector< Hashinator::hash_pair< vmesh::GlobalID, vmesh::LocalID > > * dev_list_delete
split::SplitVector< Hashinator::hash_pair< vmesh::GlobalID, vmesh::LocalID > > * dev_list_with_replace_old
vmesh::LocalID velocity_block_with_content_list_size
void setReservation(const uint popID, const vmesh::LocalID reservationsize, bool force=false)
vmesh::VelocityBlockContainer * dev_get_velocity_blocks(const size_t &popID)
vmesh::VelocityMesh * dev_get_velocity_mesh(const size_t &popID)
bool setNewCapacity(const vmesh::LocalID capacity)
ARCH_HOSTDEV bool setNewSize(const vmesh::LocalID newSize)
void setNewCapacity(const vmesh::LocalID &newCapacity)
void setNewCachedSize(const vmesh::LocalID newSize)
void setNewSize(const vmesh::LocalID &newSize)
size_t size(bool dummy=0) const
float Real
Definition definitions.h:41
uint64_t CellID
Definition definitions.h:54
const vmesh::VelocityMesh *__restrict__ vmesh
const uint cellOffset
__global__ void vmesh::VelocityMesh **__restrict__ ColumnOffsets split::SplitVector< vmesh::GlobalID > Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > const uint *__restrict__ const Realf const int const int const Realf const Realf vmesh::LocalID vmesh::LocalID * dev_overflownElements
__global__ void vmesh::VelocityMesh **__restrict__ ColumnOffsets split::SplitVector< vmesh::GlobalID > Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > const uint *__restrict__ const Realf const int const int const Realf const Realf vmesh::LocalID * dev_resizeSuccess
int blocksPerMP
Definition gpu_base.cpp:43
GPUMemoryManager gpuMemoryManager
Definition gpu_base.cpp:64
__host__ gpuStream_t gpu_getPriorityStream()
Definition gpu_base.cpp:248
int threadsPerMP
Definition gpu_base.cpp:44
__host__ gpuStream_t gpu_getStream()
Definition gpu_base.cpp:244
__host__ void gpu_batch_allocate(uint nCells, uint maxNeighbours)
Definition gpu_base.cpp:462
#define SESSION_HOST_ALLOCATE(object, type, member, bytes)
Definition gpu_base.hpp:600
#define SESSION_ALLOCATE(object, type, member, bytes)
Definition gpu_base.hpp:557
#define GET_SESSION_POINTER(object, type, member)
Definition gpu_base.hpp:833
static const double BLOCK_ALLOCATION_PADDING
Definition gpu_base.hpp:60
#define GET_SESSION_HOST_POINTER(object, type, member)
Definition gpu_base.hpp:853
#define GET_POINTER(object, type, member)
Definition gpu_base.hpp:809
#define SINGLE_ARG(...)
Definition gpu_base.hpp:280
__global__ void const Realf const uint *__restrict__ const uint *__restrict__ const vmesh::GlobalID *__restrict__ const uint const uint const uint const Realf threshold
ObjectWrapper & getObjectWrapper()
Definition main.cpp:33
uint32_t uint
void extract_to_delete_or_move_caller(Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > **input_maps, split::SplitVector< Hashinator::hash_pair< vmesh::GlobalID, vmesh::LocalID > > **output_vecs, vmesh::LocalID *output_sizes, vmesh::VelocityMesh **rule_meshes, Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > **rule_maps, split::SplitVector< vmesh::GlobalID > **rule_vectors, const uint nCells, gpuStream_t stream)
void adjust_velocity_blocks_in_cells(dccrg::Dccrg< spatial_cell::SpatialCell, dccrg::Cartesian_Geometry > &mpiGrid, const vector< CellID > &cellsToAdjust, const uint popID)
void update_velocity_block_content_lists(dccrg::Dccrg< SpatialCell, dccrg::Cartesian_Geometry > &mpiGrid, const vector< CellID > &cells, const uint popID)
void batch_adjust_blocks_caller(dccrg::Dccrg< spatial_cell::SpatialCell, dccrg::Cartesian_Geometry > &mpiGrid, const vector< CellID > &cellsToAdjust, const uint cellOffset, uint &out_largestBlocksToChange, uint &out_largestBlocksBeforeOrAfter, const uint popID)
void extract_to_add_caller(Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > **input_maps, split::SplitVector< vmesh::GlobalID > **output_vecs, vmesh::LocalID *output_sizes, vmesh::VelocityMesh **rule_meshes, Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > **rule_maps, split::SplitVector< vmesh::GlobalID > **rule_vectors, const uint nCells, gpuStream_t stream)
void clear_maps_caller(const uint nCells, const size_t largestSizePower, gpuStream_t stream, const size_t offset)
void extract_to_replace_caller(Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > **input_maps, split::SplitVector< Hashinator::hash_pair< vmesh::GlobalID, vmesh::LocalID > > **output_vecs, vmesh::LocalID *output_sizes, vmesh::VelocityMesh **rule_meshes, Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > **rule_maps, split::SplitVector< vmesh::GlobalID > **rule_vectors, const uint nCells, gpuStream_t stream)
uint32_t LocalID
Definition definitions.h:60
uint32_t GlobalID
Definition definitions.h:59
bool startSession(size_t dev_bytes, size_t host_bytes)
Definition gpu_base.hpp:351
std::vector< species::Species > particleSpecies
static ARCH_HOSTDEV VecSimple< T > floor(VecSimple< T > const &a)