Minor optimizations (#1551)

* avoid using atomics when not needed

* fast path for empty boxes and cache query

* clarify previous question
This commit is contained in:
pca006132
2026-02-28 12:23:57 +08:00
committed by GitHub
parent 9363fc7b89
commit 330b475d92
3 changed files with 21 additions and 20 deletions
+2
View File
@@ -450,6 +450,8 @@ Vec<int> Winding03_(const Manifold::Impl& inP, const Manifold::Impl& inQ,
Kernel02<expandP, forward> k02{a, b};
auto recorderf = [&](int i, int b) {
const auto [s02, z02] = k02(verts[i], b);
// note that i is distinct on each thread, and verts contains unique
// elements, so this does not require atomics
if (std::isfinite(z02)) w03[verts[i]] += s02 * (forward ? 1 : -1);
};
auto recorder = MakeSimpleRecorder(recorderf);
+7 -16
View File
@@ -64,17 +64,14 @@ struct DuplicateVerts {
}
};
template <bool atomic>
struct CountVerts {
VecView<Halfedge> halfedges;
VecView<int> count;
VecView<const int> inclusion;
void operator()(size_t i) {
if (atomic)
AtomicAdd(count[i / 3], std::abs(inclusion[halfedges[i].startVert]));
else
count[i / 3] += std::abs(inclusion[halfedges[i].startVert]);
for (size_t j : {0, 1, 2})
count[i] += std::abs(inclusion[halfedges[3 * i + j].startVert]);
}
};
@@ -117,17 +114,11 @@ std::tuple<Vec<int>, Vec<int>> SizeOutput(
auto sidesPerFaceP = sidesPerFacePQ.view(0, inP.NumTri());
auto sidesPerFaceQ = sidesPerFacePQ.view(inP.NumTri(), inQ.NumTri());
if (inP.halfedge_.size() >= 1e5) {
for_each(ExecutionPolicy::Par, countAt(0_uz), countAt(inP.halfedge_.size()),
CountVerts<true>({inP.halfedge_, sidesPerFaceP, i03}));
for_each(ExecutionPolicy::Par, countAt(0_uz), countAt(inQ.halfedge_.size()),
CountVerts<true>({inQ.halfedge_, sidesPerFaceQ, i30}));
} else {
for_each(ExecutionPolicy::Seq, countAt(0_uz), countAt(inP.halfedge_.size()),
CountVerts<false>({inP.halfedge_, sidesPerFaceP, i03}));
for_each(ExecutionPolicy::Seq, countAt(0_uz), countAt(inQ.halfedge_.size()),
CountVerts<false>({inQ.halfedge_, sidesPerFaceQ, i30}));
}
auto policy = autoPolicy(inP.halfedge_.size());
for_each(policy, countAt(0_uz), countAt(inP.halfedge_.size() / 3),
CountVerts({inP.halfedge_, sidesPerFaceP, i03}));
for_each(policy, countAt(0_uz), countAt(inQ.halfedge_.size() / 3),
CountVerts({inQ.halfedge_, sidesPerFaceQ, i30}));
if (i12.size() >= 1e5) {
for_each_n(ExecutionPolicy::Par, countAt(0), i12.size(),
+12 -4
View File
@@ -168,10 +168,11 @@ struct FindCollision {
using Local = typename Recorder::Local;
inline int RecordCollision(int node, const int queryIdx, Local& local) {
inline int RecordCollision(std::invoke_result_t<F, const int> query, int node,
const int queryIdx, Local& local) {
auto box = nodeBBox_[node];
if (hasTransform) box = box.Transform(transform);
bool overlaps = box.DoesOverlap(f(queryIdx));
bool overlaps = box.DoesOverlap(query);
if (overlaps && IsLeaf(node)) {
int leafIdx = Node2Leaf(node);
if (!selfCollision || leafIdx != queryIdx) {
@@ -182,6 +183,13 @@ struct FindCollision {
}
void operator()(const int queryIdx) {
auto query = f(queryIdx);
// early exit for empty boxes
if constexpr (std::is_same_v<std::remove_cv_t<decltype(query)>, Box>) {
if (query.min.x == std::numeric_limits<double>::infinity()) return;
}
// stack cannot overflow because radix tree has max depth 30 (Morton code) +
// 32 (index).
int stack[64];
@@ -194,8 +202,8 @@ struct FindCollision {
int child1 = internalChildren_[internal].first;
int child2 = internalChildren_[internal].second;
int traverse1 = RecordCollision(child1, queryIdx, local);
int traverse2 = RecordCollision(child2, queryIdx, local);
int traverse1 = RecordCollision(query, child1, queryIdx, local);
int traverse2 = RecordCollision(query, child2, queryIdx, local);
if (!traverse1 && !traverse2) {
if (top < 0) break; // done