Skip to content

Commit eb8f71a

Browse files
committed
Performance Update Mesh - Mesh
- kT much faster via Fp32 (fp64 fallback) - dT early memory improvments
1 parent b302612 commit eb8f71a

3 files changed

Lines changed: 165 additions & 44 deletions

File tree

src/DEM/dT.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,7 @@ void DEMDynamicThread::changeOwnerSizes(const std::vector<bodyID_t>& IDs, const
514514
size_t ownerFactorSize = (size_t)simParams->nOwnerBodies * sizeof(float);
515515
// Bool table for whether this owner should change
516516
notStupidBool_t* idBool = (notStupidBool_t*)solverScratchSpace.allocateTempVector("idBool", idBoolSize);
517-
DEME_GPU_CALL(cudaMemset(idBool, 0, idBoolSize));
517+
DEME_GPU_CALL(cudaMemsetAsync(idBool, 0, idBoolSize, streamInfo.stream));
518518
float* ownerFactors = (float*)solverScratchSpace.allocateTempVector("ownerFactors", ownerFactorSize);
519519

520520
// Mark on the bool array those owners that need a change
@@ -632,7 +632,7 @@ void DEMDynamicThread::allocateGPUArrays(size_t nOwnerBodies,
632632
// maxTriTriPenetration usually keeps the max tri--tri penetration during the on-going simulation. But after
633633
// initialization, when it stores no meaningful values, dT will send a work order to kT, so maxTriTriPenetration's
634634
// value has to be initialized.
635-
DEME_GPU_CALL(cudaMemset(maxTriTriPenetration.getDevicePointer(), 0, sizeof(double)));
635+
DEME_GPU_CALL(cudaMemsetAsync(maxTriTriPenetration.getDevicePointer(), 0, sizeof(double), streamInfo.stream));
636636

637637
// Resize to the number of analytical geometries
638638
DEME_DUAL_ARRAY_RESIZE(ownerAnalBody, nAnalGM, 0);
@@ -2495,8 +2495,26 @@ inline void DEMDynamicThread::dispatchPatchBasedForceCorrections(
24952495
const ContactTypeMap<std::pair<contactPairs_t, contactPairs_t>>& typeStartCountPrimitiveMap,
24962496
const ContactTypeMap<std::pair<contactPairs_t, contactPairs_t>>& typeStartCountPatchMap,
24972497
const ContactTypeMap<std::vector<std::pair<std::shared_ptr<JitHelper::CachedProgram>, std::string>>>& typeKernelMap) {
2498-
// Reset max tri-tri penetration for this timestep on device (kT may need this info)
2499-
DEME_GPU_CALL(cudaMemset(maxTriTriPenetration.getDevicePointer(), 0, sizeof(double)));
2498+
// Reset max tri-tri penetration only when mesh contacts are present (avoid needless clears), async to overlap
2499+
bool needTriPenReset = false;
2500+
auto markIfTri = [&](contact_t c) {
2501+
auto itP = typeStartCountPrimitiveMap.find(c);
2502+
if (itP != typeStartCountPrimitiveMap.end() && itP->second.second > 0) {
2503+
needTriPenReset = true;
2504+
return;
2505+
}
2506+
auto itPatch = typeStartCountPatchMap.find(c);
2507+
if (itPatch != typeStartCountPatchMap.end() && itPatch->second.second > 0) {
2508+
needTriPenReset = true;
2509+
}
2510+
};
2511+
markIfTri(SPHERE_TRIANGLE_CONTACT);
2512+
markIfTri(TRIANGLE_TRIANGLE_CONTACT);
2513+
markIfTri(TRIANGLE_ANALYTICAL_CONTACT);
2514+
if (needTriPenReset) {
2515+
DEME_GPU_CALL(
2516+
cudaMemsetAsync(maxTriTriPenetration.getDevicePointer(), 0, sizeof(double), streamInfo.stream));
2517+
}
25002518

25012519
// For each contact type that exists, check if it is patch(mesh)-related type...
25022520
for (size_t i = 0; i < m_numExistingTypes; i++) {
@@ -3375,7 +3393,7 @@ float* DEMDynamicThread::inspectCall(const std::shared_ptr<JitHelper::CachedProg
33753393
// If this boolArrExclude is 1 at an element, that means this element is exluded in the reduction
33763394
notStupidBool_t* boolArrExclude =
33773395
(notStupidBool_t*)solverScratchSpace.allocateTempVector("boolArrExclude", regionTempSize);
3378-
DEME_GPU_CALL(cudaMemset(boolArrExclude, 0, regionTempSize));
3396+
DEME_GPU_CALL(cudaMemsetAsync(boolArrExclude, 0, regionTempSize, streamInfo.stream));
33793397

33803398
// We may actually have 2 reduced returns: in regional reduction, key 0 and 1 give one return each.
33813399
size_t returnSize = sizeof(float) * 2;

src/DEM/kT.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,7 @@ void DEMKinematicThread::changeOwnerSizes(const std::vector<bodyID_t>& IDs, cons
608608
size_t ownerFactorSize = (size_t)simParams->nOwnerBodies * sizeof(float);
609609
// Bool table for whether this owner should change
610610
notStupidBool_t* idBool = (notStupidBool_t*)solverScratchSpace.allocateTempVector("idBool", idBoolSize);
611-
DEME_GPU_CALL(cudaMemset(idBool, 0, idBoolSize));
611+
DEME_GPU_CALL(cudaMemsetAsync(idBool, 0, idBoolSize, streamInfo.stream));
612612
float* ownerFactors = (float*)solverScratchSpace.allocateTempVector("ownerFactors", ownerFactorSize);
613613

614614
// Mark on the bool array those owners that need a change

src/kernel/DEMBinTriangleKernels.cu

Lines changed: 141 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,62 @@ __global__ void makeTriangleSandwich(deme::DEMSimParams* simParams,
5656
}
5757
}
5858

59-
inline __device__ void figureOutNodeAndBoundingBox(deme::DEMSimParams* simParams,
59+
60+
// Compute triangle AABB -> bin index bounds using mixed precision (FP32 fast path + FP64 fallback).
61+
// This mirrors the sphere binning approach (axis_bounds) to avoid precision issues when a bound lies
62+
// close to a bin boundary.
63+
inline __device__ bool boundingBoxIntersectBinAxisBounds(deme::binID_t* L,
64+
deme::binID_t* U,
65+
const float3& vA,
66+
const float3& vB,
67+
const float3& vC,
68+
deme::DEMSimParams* simParams) {
69+
float3 min_pt;
70+
min_pt.x = DEME_MIN(vA.x, DEME_MIN(vB.x, vC.x));
71+
min_pt.y = DEME_MIN(vA.y, DEME_MIN(vB.y, vC.y));
72+
min_pt.z = DEME_MIN(vA.z, DEME_MIN(vB.z, vC.z));
73+
74+
float3 max_pt;
75+
max_pt.x = DEME_MAX(vA.x, DEME_MAX(vB.x, vC.x));
76+
max_pt.y = DEME_MAX(vA.y, DEME_MAX(vB.y, vC.y));
77+
max_pt.z = DEME_MAX(vA.z, DEME_MAX(vB.z, vC.z));
78+
79+
// Enlarge bounding box, so that no triangle lies right between 2 layers of bins
80+
const float enlarge = (float)DEME_BIN_ENLARGE_RATIO_FOR_FACETS * (float)simParams->dyn.binSize;
81+
min_pt -= enlarge;
82+
max_pt += enlarge;
83+
84+
const double invBinSize = simParams->dyn.inv_binSize;
85+
const int nbX = (int)simParams->nbX;
86+
const int nbY = (int)simParams->nbY;
87+
const int nbZ = (int)simParams->nbZ;
88+
89+
// Convert [min,max] to (center, half-range) and use axis_bounds (FP32 fast path with FP64 fallback).
90+
const double cx = 0.5 * ((double)min_pt.x + (double)max_pt.x);
91+
const double rx = 0.5 * ((double)max_pt.x - (double)min_pt.x);
92+
const deme::AxisBounds bx = axis_bounds(cx, rx, nbX, invBinSize);
93+
if (bx.imax < bx.imin) return false;
94+
95+
const double cy = 0.5 * ((double)min_pt.y + (double)max_pt.y);
96+
const double ry = 0.5 * ((double)max_pt.y - (double)min_pt.y);
97+
const deme::AxisBounds by = axis_bounds(cy, ry, nbY, invBinSize);
98+
if (by.imax < by.imin) return false;
99+
100+
const double cz = 0.5 * ((double)min_pt.z + (double)max_pt.z);
101+
const double rz = 0.5 * ((double)max_pt.z - (double)min_pt.z);
102+
const deme::AxisBounds bz = axis_bounds(cz, rz, nbZ, invBinSize);
103+
if (bz.imax < bz.imin) return false;
104+
105+
L[0] = (deme::binID_t)bx.imin;
106+
U[0] = (deme::binID_t)bx.imax;
107+
L[1] = (deme::binID_t)by.imin;
108+
U[1] = (deme::binID_t)by.imax;
109+
L[2] = (deme::binID_t)bz.imin;
110+
U[2] = (deme::binID_t)bz.imax;
111+
return true;
112+
}
113+
114+
inline __device__ bool figureOutNodeAndBoundingBox(deme::DEMSimParams* simParams,
60115
deme::DEMDataKT* granData,
61116
const deme::bodyID_t& triID,
62117
float3& vA,
@@ -85,7 +140,7 @@ inline __device__ void figureOutNodeAndBoundingBox(deme::DEMSimParams* simParams
85140
vB = ownerXYZ + loc_vB;
86141
vC = ownerXYZ + loc_vC;
87142

88-
boundingBoxIntersectBin(L, U, vA, vB, vC, simParams);
143+
return boundingBoxIntersectBinAxisBounds(L, U, vA, vB, vC, simParams);
89144
}
90145

91146
__global__ void getNumberOfBinsEachTriangleTouches(deme::DEMSimParams* simParams,
@@ -106,38 +161,64 @@ __global__ void getNumberOfBinsEachTriangleTouches(deme::DEMSimParams* simParams
106161
// bin-based locations don't need that)
107162
float3 vA1, vB1, vC1, vA2, vB2, vC2;
108163
deme::binID_t L1[3], L2[3], U1[3], U2[3];
109-
figureOutNodeAndBoundingBox(simParams, granData, triID, vA1, vB1, vC1, L1, U1, nodeA1[triID], nodeB1[triID],
110-
nodeC1[triID]);
111-
figureOutNodeAndBoundingBox(simParams, granData, triID, vA2, vB2, vC2, L2, U2, nodeA2[triID], nodeB2[triID],
112-
nodeC2[triID]);
113-
L1[0] = DEME_MIN(L1[0], L2[0]);
114-
L1[1] = DEME_MIN(L1[1], L2[1]);
115-
L1[2] = DEME_MIN(L1[2], L2[2]);
116-
U1[0] = DEME_MAX(U1[0], U2[0]);
117-
U1[1] = DEME_MAX(U1[1], U2[1]);
118-
U1[2] = DEME_MAX(U1[2], U2[2]);
164+
const bool ok1 = figureOutNodeAndBoundingBox(simParams, granData, triID, vA1, vB1, vC1, L1, U1, nodeA1[triID],
165+
nodeB1[triID], nodeC1[triID]);
166+
const bool ok2 = figureOutNodeAndBoundingBox(simParams, granData, triID, vA2, vB2, vC2, L2, U2, nodeA2[triID],
167+
nodeB2[triID], nodeC2[triID]);
168+
169+
// If neither triangle sandwich intersects the bin grid, it cannot touch any bin.
170+
if (!ok1 && !ok2) {
171+
numBinsTriTouches[triID] = 0;
172+
if (meshUniversalContact) {
173+
numAnalGeoTriTouches[triID] = 0;
174+
}
175+
return;
176+
}
177+
178+
// Merge bounds (or take the valid one, if only one is valid).
179+
if (ok1 && ok2) {
180+
L1[0] = DEME_MIN(L1[0], L2[0]);
181+
L1[1] = DEME_MIN(L1[1], L2[1]);
182+
L1[2] = DEME_MIN(L1[2], L2[2]);
183+
U1[0] = DEME_MAX(U1[0], U2[0]);
184+
U1[1] = DEME_MAX(U1[1], U2[1]);
185+
U1[2] = DEME_MAX(U1[2], U2[2]);
186+
} else if (!ok1) {
187+
L1[0] = L2[0];
188+
L1[1] = L2[1];
189+
L1[2] = L2[2];
190+
U1[0] = U2[0];
191+
U1[1] = U2[1];
192+
U1[2] = U2[2];
193+
}
119194

120195
unsigned int numSDsTouched = 0;
121196
// Triangle may span a collection of bins...
122197
// BTW, I don't know why Chrono::GPU had to check the so-called 3 cases, and create thread divergence like that.
123198
// Just sweep through all potential bins and you are fine.
124199
float BinCenter[3];
125-
float BinHalfSizes[3];
126-
BinHalfSizes[0] = simParams->dyn.binSize / 2. + DEME_BIN_ENLARGE_RATIO_FOR_FACETS * simParams->dyn.binSize;
127-
BinHalfSizes[1] = simParams->dyn.binSize / 2. + DEME_BIN_ENLARGE_RATIO_FOR_FACETS * simParams->dyn.binSize;
128-
BinHalfSizes[2] = simParams->dyn.binSize / 2. + DEME_BIN_ENLARGE_RATIO_FOR_FACETS * simParams->dyn.binSize;
129-
for (deme::binID_t i = L1[0]; i <= U1[0]; i++) {
200+
const float binSizeF = (float)simParams->dyn.binSize;
201+
const float binHalfSpan = binSizeF * (0.5f + (float)DEME_BIN_ENLARGE_RATIO_FOR_FACETS);
202+
float BinHalfSizes[3] = {binHalfSpan, binHalfSpan, binHalfSpan};
203+
const float startX = binSizeF * (float)L1[0] + 0.5f * binSizeF;
204+
const float startY = binSizeF * (float)L1[1] + 0.5f * binSizeF;
205+
const float startZ = binSizeF * (float)L1[2] + 0.5f * binSizeF;
206+
for (deme::binID_t i = L1[0], ix = 0; i <= U1[0]; i++, ix++) {
207+
float cy0 = startY;
208+
BinCenter[0] = startX + ix * binSizeF;
130209
for (deme::binID_t j = L1[1]; j <= U1[1]; j++) {
210+
float cz = startZ;
211+
BinCenter[1] = cy0;
131212
for (deme::binID_t k = L1[2]; k <= U1[2]; k++) {
132-
BinCenter[0] = simParams->dyn.binSize * i + simParams->dyn.binSize / 2.;
133-
BinCenter[1] = simParams->dyn.binSize * j + simParams->dyn.binSize / 2.;
134-
BinCenter[2] = simParams->dyn.binSize * k + simParams->dyn.binSize / 2.;
213+
BinCenter[2] = cz;
135214

136215
if (check_TriangleBoxOverlap(BinCenter, BinHalfSizes, vA1, vB1, vC1) ||
137216
check_TriangleBoxOverlap(BinCenter, BinHalfSizes, vA2, vB2, vC2)) {
138217
numSDsTouched++;
139218
}
219+
cz += binSizeF;
140220
}
221+
cy0 += binSizeF;
141222
}
142223
}
143224
numBinsTriTouches[triID] = numSDsTouched;
@@ -221,36 +302,56 @@ __global__ void populateBinTriangleTouchingPairs(deme::DEMSimParams* simParams,
221302
// 3 vertices of the triangle
222303
float3 vA1, vB1, vC1, vA2, vB2, vC2;
223304
deme::binID_t L1[3], L2[3], U1[3], U2[3];
224-
figureOutNodeAndBoundingBox(simParams, granData, triID, vA1, vB1, vC1, L1, U1, nodeA1[triID], nodeB1[triID],
225-
nodeC1[triID]);
226-
figureOutNodeAndBoundingBox(simParams, granData, triID, vA2, vB2, vC2, L2, U2, nodeA2[triID], nodeB2[triID],
227-
nodeC2[triID]);
228-
L1[0] = DEME_MIN(L1[0], L2[0]);
229-
L1[1] = DEME_MIN(L1[1], L2[1]);
230-
L1[2] = DEME_MIN(L1[2], L2[2]);
231-
U1[0] = DEME_MAX(U1[0], U2[0]);
232-
U1[1] = DEME_MAX(U1[1], U2[1]);
233-
U1[2] = DEME_MAX(U1[2], U2[2]);
305+
const bool ok1 = figureOutNodeAndBoundingBox(simParams, granData, triID, vA1, vB1, vC1, L1, U1, nodeA1[triID],
306+
nodeB1[triID], nodeC1[triID]);
307+
const bool ok2 = figureOutNodeAndBoundingBox(simParams, granData, triID, vA2, vB2, vC2, L2, U2, nodeA2[triID],
308+
nodeB2[triID], nodeC2[triID]);
309+
310+
// If neither triangle sandwich intersects the bin grid, it cannot touch any bin.
311+
if (!ok1 && !ok2) {
312+
return;
313+
}
314+
315+
// Merge bounds (or take the valid one, if only one is valid).
316+
if (ok1 && ok2) {
317+
L1[0] = DEME_MIN(L1[0], L2[0]);
318+
L1[1] = DEME_MIN(L1[1], L2[1]);
319+
L1[2] = DEME_MIN(L1[2], L2[2]);
320+
U1[0] = DEME_MAX(U1[0], U2[0]);
321+
U1[1] = DEME_MAX(U1[1], U2[1]);
322+
U1[2] = DEME_MAX(U1[2], U2[2]);
323+
} else if (!ok1) {
324+
L1[0] = L2[0];
325+
L1[1] = L2[1];
326+
L1[2] = L2[2];
327+
U1[0] = U2[0];
328+
U1[1] = U2[1];
329+
U1[2] = U2[2];
330+
}
234331

235332
deme::binsTriangleTouchPairs_t myReportOffset = numBinsTriTouchesScan[triID];
236333
// In case this sweep does not agree with the previous one, we need to intercept such potential segfaults
237334
const deme::binsTriangleTouchPairs_t myReportOffset_end = numBinsTriTouchesScan[triID + 1];
238335

239336
// Triangle may span a collection of bins...
240337
float BinCenter[3];
241-
float BinHalfSizes[3];
242-
BinHalfSizes[0] = simParams->dyn.binSize / 2. + DEME_BIN_ENLARGE_RATIO_FOR_FACETS * simParams->dyn.binSize;
243-
BinHalfSizes[1] = simParams->dyn.binSize / 2. + DEME_BIN_ENLARGE_RATIO_FOR_FACETS * simParams->dyn.binSize;
244-
BinHalfSizes[2] = simParams->dyn.binSize / 2. + DEME_BIN_ENLARGE_RATIO_FOR_FACETS * simParams->dyn.binSize;
245-
for (deme::binID_t i = L1[0]; i <= U1[0]; i++) {
338+
const float binSizeF = (float)simParams->dyn.binSize;
339+
const float binHalfSpan = binSizeF * (0.5f + (float)DEME_BIN_ENLARGE_RATIO_FOR_FACETS);
340+
float BinHalfSizes[3] = {binHalfSpan, binHalfSpan, binHalfSpan};
341+
const float startX = binSizeF * (float)L1[0] + 0.5f * binSizeF;
342+
const float startY = binSizeF * (float)L1[1] + 0.5f * binSizeF;
343+
const float startZ = binSizeF * (float)L1[2] + 0.5f * binSizeF;
344+
for (deme::binID_t i = L1[0], ix = 0; i <= U1[0]; i++, ix++) {
345+
BinCenter[0] = startX + ix * binSizeF;
346+
float cy0 = startY;
246347
for (deme::binID_t j = L1[1]; j <= U1[1]; j++) {
348+
BinCenter[1] = cy0;
349+
float cz = startZ;
247350
for (deme::binID_t k = L1[2]; k <= U1[2]; k++) {
248351
if (myReportOffset >= myReportOffset_end) {
249352
continue; // Don't step on the next triangle's domain
250353
}
251-
BinCenter[0] = simParams->dyn.binSize * i + simParams->dyn.binSize / 2.;
252-
BinCenter[1] = simParams->dyn.binSize * j + simParams->dyn.binSize / 2.;
253-
BinCenter[2] = simParams->dyn.binSize * k + simParams->dyn.binSize / 2.;
354+
BinCenter[2] = cz;
254355

255356
if (check_TriangleBoxOverlap(BinCenter, BinHalfSizes, vA1, vB1, vC1) ||
256357
check_TriangleBoxOverlap(BinCenter, BinHalfSizes, vA2, vB2, vC2)) {
@@ -259,7 +360,9 @@ __global__ void populateBinTriangleTouchingPairs(deme::DEMSimParams* simParams,
259360
triIDsEachBinTouches[myReportOffset] = triID;
260361
myReportOffset++;
261362
}
363+
cz += binSizeF;
262364
}
365+
cy0 += binSizeF;
263366
}
264367
}
265368
// This can happen for like 1 in 10^9 chance, for the tri--bin contact algorithm has stochasticity on GPU

0 commit comments

Comments
 (0)