Implement naive multithreading for N:N clashes. The clash task queue is divided by num_threads equally.

This commit is contained in:
Dion Moult
2024-02-12 11:47:20 +11:00
parent 6cec57816e
commit 911b7153d7
+114 -18
View File
@@ -40,6 +40,9 @@
#include <BRepBuilderAPI_MakeEdge.hxx> #include <BRepBuilderAPI_MakeEdge.hxx>
#include <BRepExtrema_ExtPF.hxx> #include <BRepExtrema_ExtPF.hxx>
#include <vector>
#include <future>
#include <mutex>
#include <stack> #include <stack>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
@@ -1100,10 +1103,29 @@ namespace IfcGeom {
return box_set; return box_set;
} }
struct clash_task {
T a, b;
};
std::vector<std::vector<clash_task>> allocate_tasks_to_threads(
std::vector<clash_task>& task_queue) const {
int num_threads = std::thread::hardware_concurrency();
std::vector<std::vector<clash_task>> threaded_tasks(num_threads);
size_t tasks_per_thread = task_queue.size() / num_threads;
for (int i = 0; i < num_threads; ++i) {
auto startIter = std::next(task_queue.begin(), i * tasks_per_thread);
auto endIter = (i == num_threads - 1) ? task_queue.end() : std::next(startIter, tasks_per_thread);
threaded_tasks[i] = std::vector<clash_task>(startIter, endIter);
}
return threaded_tasks;
}
std::vector<clash> clash_intersection_many( std::vector<clash> clash_intersection_many(
const std::vector<T>& set_a, const std::vector<T>& set_b, const std::vector<T>& set_a, const std::vector<T>& set_b,
double tolerance = 0.002, bool check_all = true double tolerance = 0.002, bool check_all = true
) const { ) const {
std::vector<clash_task> task_queue;
std::vector<clash> results; std::vector<clash> results;
std::unique_ptr<BVH_BoxSet<double, 3>> box_set_a = build_box_set(set_a); std::unique_ptr<BVH_BoxSet<double, 3>> box_set_a = build_box_set(set_a);
@@ -1138,8 +1160,23 @@ namespace IfcGeom {
continue; continue;
} }
const auto& obb_a = obbs_.find(t_a)->second; task_queue.emplace_back(clash_task{t_a, t_b});
auto obb_b = obbs_.find(t_b)->second; }
}
}
}
std::vector<std::vector<clash_task>> threaded_tasks = allocate_tasks_to_threads(task_queue);
std::vector<std::thread> threads;
std::mutex results_mutex;
for (auto& tasks : threaded_tasks) {
threads.emplace_back([this, &tasks, &results, &results_mutex, tolerance, check_all] {
std::vector<clash> thread_results;
for (auto& task : tasks) {
const auto& obb_a = obbs_.find(task.a)->second;
auto obb_b = obbs_.find(task.b)->second;
obb_b.Enlarge(-tolerance); obb_b.Enlarge(-tolerance);
if (obb_a.IsOut(obb_b)) { if (obb_a.IsOut(obb_b)) {
continue; continue;
@@ -1149,24 +1186,23 @@ namespace IfcGeom {
bool is_manifold = false; bool is_manifold = false;
clash result; clash result;
if (is_manifold_.find(t_b)->second) { if (is_manifold_.find(task.b)->second) {
is_manifold = true; is_manifold = true;
clash intersection = test_intersection(t_a, t_b, tolerance, check_all); clash intersection = test_intersection(task.a, task.b, tolerance, check_all);
if (intersection.clash_type != -1) { if (intersection.clash_type != -1) {
has_clash = true; has_clash = true;
result = intersection; result = intersection;
if ( ! check_all) { if ( ! check_all) {
results.push_back(result); thread_results.push_back(result);
continue; continue;
} }
} }
} }
if (is_manifold_.find(t_a)->second) { if (is_manifold_.find(task.a)->second) {
is_manifold = true; is_manifold = true;
clash intersection = test_intersection(t_b, t_a, tolerance, check_all); clash intersection = test_intersection(task.b, task.a, tolerance, check_all);
if (intersection.clash_type != -1) { if (intersection.clash_type != -1) {
has_clash = true;
// Replace the clash result if any of these criteria apply: // Replace the clash result if any of these criteria apply:
// - We don't have a clash yet // - We don't have a clash yet
// - Our previous clash is piercing, and our new one is a protrusion // - Our previous clash is piercing, and our new one is a protrusion
@@ -1179,13 +1215,14 @@ namespace IfcGeom {
&& intersection.distance > result.distance && intersection.distance > result.distance
) )
) { ) {
has_clash = true;
result = intersection; result = intersection;
} }
} }
} }
if ( ! is_manifold) { if ( ! is_manifold) {
clash collision = test_collision(t_a, t_b, false); clash collision = test_collision(task.a, task.b, false);
if (collision.clash_type != -1) { if (collision.clash_type != -1) {
has_clash = true; has_clash = true;
result = collision; result = collision;
@@ -1193,10 +1230,19 @@ namespace IfcGeom {
} }
if (has_clash) { if (has_clash) {
results.push_back(result); thread_results.push_back(result);
} }
} }
{
std::lock_guard<std::mutex> lock(results_mutex);
results.insert(results.end(), thread_results.begin(), thread_results.end());
} }
});
}
for (auto& thread : threads) {
if (thread.joinable()) {
thread.join();
} }
} }
@@ -1206,6 +1252,7 @@ namespace IfcGeom {
std::vector<clash> clash_collision_many( std::vector<clash> clash_collision_many(
const std::vector<T>& set_a, const std::vector<T>& set_b, bool allow_touching = false const std::vector<T>& set_a, const std::vector<T>& set_b, bool allow_touching = false
) const { ) const {
std::vector<clash_task> task_queue;
std::vector<clash> results; std::vector<clash> results;
std::unique_ptr<BVH_BoxSet<double, 3>> box_set_a = build_box_set(set_a); std::unique_ptr<BVH_BoxSet<double, 3>> box_set_a = build_box_set(set_a);
@@ -1240,19 +1287,43 @@ namespace IfcGeom {
continue; continue;
} }
const auto& obb_a = obbs_.find(t_a)->second; task_queue.emplace_back(clash_task{t_a, t_b});
auto obb_b = obbs_.find(t_b)->second; }
}
}
}
std::vector<std::vector<clash_task>> threaded_tasks = allocate_tasks_to_threads(task_queue);
std::vector<std::thread> threads;
std::mutex results_mutex;
for (auto& tasks : threaded_tasks) {
threads.emplace_back([this, &tasks, &results, &results_mutex, allow_touching] {
std::vector<clash> thread_results;
for (auto& task : tasks) {
const auto& obb_a = obbs_.find(task.a)->second;
auto obb_b = obbs_.find(task.b)->second;
obb_b.Enlarge(-0.001); obb_b.Enlarge(-0.001);
if (obb_a.IsOut(obb_b)) { if (obb_a.IsOut(obb_b)) {
continue; continue;
} }
clash result = test_collision(t_a, t_b, allow_touching); clash result = test_collision(task.a, task.b, allow_touching);
if (result.clash_type != -1) { if (result.clash_type != -1) {
results.push_back(result); thread_results.push_back(result);
} }
} }
{
std::lock_guard<std::mutex> lock(results_mutex);
results.insert(results.end(), thread_results.begin(), thread_results.end());
} }
});
}
for (auto& thread : threads) {
if (thread.joinable()) {
thread.join();
} }
} }
@@ -1263,6 +1334,7 @@ namespace IfcGeom {
const std::vector<T>& set_a, const std::vector<T>& set_b, const std::vector<T>& set_a, const std::vector<T>& set_b,
double clearance = 0.05, bool check_all = false double clearance = 0.05, bool check_all = false
) const { ) const {
std::vector<clash_task> task_queue;
std::vector<clash> results; std::vector<clash> results;
std::unique_ptr<BVH_BoxSet<double, 3>> box_set_a = build_box_set(set_a); std::unique_ptr<BVH_BoxSet<double, 3>> box_set_a = build_box_set(set_a);
@@ -1297,19 +1369,43 @@ namespace IfcGeom {
continue; continue;
} }
const auto& obb_a = obbs_.find(t_a)->second; task_queue.emplace_back(clash_task{t_a, t_b});
auto obb_b = obbs_.find(t_b)->second; }
}
}
}
std::vector<std::vector<clash_task>> threaded_tasks = allocate_tasks_to_threads(task_queue);
std::vector<std::thread> threads;
std::mutex results_mutex;
for (auto& tasks : threaded_tasks) {
threads.emplace_back([this, &tasks, &results, &results_mutex, clearance, check_all] {
std::vector<clash> thread_results;
for (auto& task : tasks) {
const auto& obb_a = obbs_.find(task.a)->second;
auto obb_b = obbs_.find(task.b)->second;
obb_b.Enlarge(clearance); obb_b.Enlarge(clearance);
if (obb_a.IsOut(obb_b)) { if (obb_a.IsOut(obb_b)) {
continue; continue;
} }
clash result = test_clearance(t_a, t_b, clearance, check_all); clash result = test_clearance(task.a, task.b, clearance, check_all);
if (result.clash_type != -1) { if (result.clash_type != -1) {
results.push_back(result); thread_results.push_back(result);
} }
} }
{
std::lock_guard<std::mutex> lock(results_mutex);
results.insert(results.end(), thread_results.begin(), thread_results.end());
} }
});
}
for (auto& thread : threads) {
if (thread.joinable()) {
thread.join();
} }
} }