Skip to content

Commit 43bcb82

Browse files
committed
Multi-threading in normal_estimate()
1 parent 91be2f7 commit 43bcb82

File tree

2 files changed

+88
-35
lines changed

2 files changed

+88
-35
lines changed

tests/test_normal.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ TEST(Voxelizer, Volume) {
3030
symbol_value v = 5;
3131
arguments["max_depth"] = v;
3232
}
33+
/*
34+
{
35+
symbol_value v = 4;
36+
arguments["THREADS"] = v;
37+
}
38+
*/
3339

3440
auto r = normal_estimate.invoke(arguments);
3541
auto result = (regular_voxel_storage*)boost::get<abstract_voxel_storage*>(r);

voxec.h

Lines changed: 82 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2448,48 +2448,95 @@ class op_normal_estimate : public voxel_operation {
24482448
auto result = voxels->empty_copy_as(&fmt);
24492449

24502450
const int max_depth = scope.get_value<int>("max_depth");
2451-
auto box_dim = 1 + max_depth * 2;
24522451

2453-
std::vector<float> coords;
2454-
coords.reserve(box_dim * box_dim * box_dim);
2452+
// scoped lock around setting result values
2453+
auto result_set = [result](const vec_n<3>& ijk, normal_and_curvature<int16_t>* v) {
2454+
static std::mutex m;
2455+
std::lock_guard<std::mutex> lock{ m };
2456+
result->Set(ijk, v);
2457+
};
24552458

2456-
for (auto it = voxels->begin(); it != voxels->end(); ++it) {
2457-
visitor<26> vis;
2458-
vis.max_depth = max_depth;
2459+
// lambda wrapper around the 3-dim loop to
2460+
// either call directly or invoke from worker
2461+
// threads. In the last case the z-coord
2462+
// of the voxel will be used to skip processing
2463+
// within a thread based on the modelo with the
2464+
// thread count.
2465+
auto process = [
2466+
&result_set, voxels, max_depth
2467+
](const boost::optional<size_t>& num_threads = boost::none, const boost::optional<size_t>& thread_id = boost::none)
2468+
{
2469+
uint32_t u32;
2470+
normal_and_curvature<int16_t> v;
24592471

2460-
coords.clear();
2461-
2462-
vis([&coords](const tagged_index& pos) {
2463-
if (pos.which == tagged_index::VOXEL) {
2464-
coords.push_back(pos.pos.get(0));
2465-
coords.push_back(pos.pos.get(1));
2466-
coords.push_back(pos.pos.get(2));
2467-
} else {
2468-
throw std::runtime_error("Unexpected");
2472+
std::vector<float> coords;
2473+
auto box_dim = 1 + max_depth * 2;
2474+
coords.reserve(box_dim * box_dim * box_dim);
2475+
2476+
for (auto it = voxels->begin(); it != voxels->end(); ++it) {
2477+
if (num_threads && thread_id && ((*it).get<2>() % *num_threads) != *thread_id) {
2478+
continue;
24692479
}
2470-
}, voxels, *it);
24712480

2472-
Eigen::MatrixXf points = Eigen::Map<Eigen::MatrixXf>(coords.data(), 3, coords.size() / 3).transpose();
2481+
visitor<26> vis;
2482+
vis.max_depth = max_depth;
24732483

2474-
// Eigen::RowVector3f it_as_vec((*it).get(0), (*it).get(1), (*it).get(2));
2475-
// Eigen::MatrixXf centered = points.rowwise() - it_as_vec;
2476-
2477-
Eigen::MatrixXf centered = points.rowwise() - points.colwise().mean();
2478-
Eigen::MatrixXf cov = centered.adjoint() * centered;
2479-
Eigen::SelfAdjointEigenSolver<Eigen::MatrixXf> eig(cov);
2480-
2481-
auto vec = eig.eigenvectors().col(0);
2482-
auto norm = vec.normalized();
2483-
auto curv = eig.eigenvalues().col(0).value() / eig.eigenvalues().sum();
2484-
2485-
v.nxyz_curv = {
2486-
(int16_t) (norm.x() * (float) (std::numeric_limits<int16_t>::max()-1)),
2487-
(int16_t) (norm.y() * (float) (std::numeric_limits<int16_t>::max()-1)),
2488-
(int16_t) (norm.z() * (float) (std::numeric_limits<int16_t>::max()-1)),
2489-
(int16_t) (curv * (float) (std::numeric_limits<int16_t>::max()-1))
2490-
};
2484+
coords.clear();
24912485

2492-
result->Set(*it, &v);
2486+
vis([&coords](const tagged_index& pos) {
2487+
if (pos.which == tagged_index::VOXEL) {
2488+
coords.push_back(pos.pos.get(0));
2489+
coords.push_back(pos.pos.get(1));
2490+
coords.push_back(pos.pos.get(2));
2491+
} else {
2492+
throw std::runtime_error("Unexpected");
2493+
}
2494+
}, voxels, *it);
2495+
2496+
Eigen::MatrixXf points = Eigen::Map<Eigen::MatrixXf>(coords.data(), 3, coords.size() / 3).transpose();
2497+
2498+
// Eigen::RowVector3f it_as_vec((*it).get(0), (*it).get(1), (*it).get(2));
2499+
// Eigen::MatrixXf centered = points.rowwise() - it_as_vec;
2500+
2501+
Eigen::MatrixXf centered = points.rowwise() - points.colwise().mean();
2502+
Eigen::MatrixXf cov = centered.adjoint() * centered;
2503+
Eigen::SelfAdjointEigenSolver<Eigen::MatrixXf> eig(cov);
2504+
2505+
auto vec = eig.eigenvectors().col(0);
2506+
auto norm = vec.normalized();
2507+
auto curv = eig.eigenvalues().col(0).value() / eig.eigenvalues().sum();
2508+
2509+
v.nxyz_curv = {
2510+
(int16_t)(norm.x() * (float)(std::numeric_limits<int16_t>::max() - 1)),
2511+
(int16_t)(norm.y() * (float)(std::numeric_limits<int16_t>::max() - 1)),
2512+
(int16_t)(norm.z() * (float)(std::numeric_limits<int16_t>::max() - 1)),
2513+
(int16_t)(curv * (float)(std::numeric_limits<int16_t>::max() - 1))
2514+
};
2515+
2516+
result_set(*it, &v);
2517+
}
2518+
};
2519+
2520+
boost::optional<size_t> threads;
2521+
if (scope.has("THREADS")) {
2522+
int t = scope.get_value<int>("THREADS");
2523+
if (t > 1) {
2524+
threads = (size_t)t;
2525+
}
2526+
}
2527+
if (threads) {
2528+
std::vector<std::thread> ts;
2529+
ts.reserve(*threads);
2530+
for (size_t i = 0; i < *threads; ++i) {
2531+
ts.emplace_back(std::thread([&process, threads, i]() {
2532+
process(threads, i);
2533+
}));
2534+
}
2535+
for (auto jt = ts.begin(); jt != ts.end(); ++jt) {
2536+
jt->join();
2537+
}
2538+
} else {
2539+
process();
24932540
}
24942541

24952542
return result;

0 commit comments

Comments
 (0)