3#include <wmtk/threading/dynamic_parallel_for.hpp>
4#include <wmtk/threading/task_group.hpp>
17namespace wmtk::utils {
40template <
typename NeighborFn>
41std::vector<std::vector<size_t>> greedy_vertex_coloring(
42 const std::vector<size_t>& vids,
45 NeighborFn&& neighbors)
47 constexpr uint32_t kNone = UINT32_MAX;
48 std::vector<uint32_t> slot(vert_capacity, kNone);
49 for (
size_t i = 0; i < vids.size(); ++i) {
52 assert(slot[vids[i]] == kNone &&
"greedy_vertex_coloring: a vertex is listed twice");
53 slot[vids[i]] = uint32_t(i);
57 std::vector<std::vector<uint32_t>> adjacency(vids.size());
58 threading::dynamic_parallel_for(vids.size(), num_threads, 256, [&](
size_t b,
size_t e) {
59 std::vector<size_t> nb;
60 for (size_t i = b; i < e; ++i) {
62 neighbors(vids[i], nb);
63 for (const size_t u : nb) {
64 if (u < slot.size() && slot[u] != kNone && slot[u] != i) {
65 adjacency[i].push_back(slot[u]);
72 std::vector<uint32_t> color(vids.size(), kNone);
73 std::vector<size_t> seen_at;
74 uint32_t n_colors = 0;
75 for (
size_t i = 0; i < vids.size(); ++i) {
76 for (
const uint32_t j : adjacency[i]) {
77 const uint32_t c = color[j];
83 while (c < n_colors && seen_at[c] == i + 1) {
93 std::vector<std::vector<size_t>> classes(n_colors);
94 for (
size_t i = 0; i < vids.size(); ++i) {
95 classes[color[i]].push_back(vids[i]);
109inline size_t color_class_chunk(
size_t n,
int num_threads,
size_t max_chunk)
111 const size_t per = n / (8 * size_t(num_threads > 0 ? num_threads : 1));
112 return per < 1 ? 1 : (per > max_chunk ? max_chunk : per);
143size_t for_each_in_classes(
144 const std::vector<std::vector<size_t>>& classes,
150 for (
const auto& cls : classes) n_total += cls.size();
151 if (n_total == 0)
return 0;
152 const size_t nt = size_t(num_threads > 1 ? num_threads : 1);
154 size_t successes = 0;
155 for (
const auto& cls : classes) {
156 for (
const size_t v : cls) {
157 if (fn(v)) ++successes;
164 auto next = std::make_unique<std::atomic<size_t>[]>(classes.size());
165 for (
size_t c = 0; c < classes.size(); ++c) next[c].store(0, std::memory_order_relaxed);
167 std::atomic<size_t> arrived{0};
168 std::atomic<size_t> generation{0};
169 const auto barrier = [&] {
170 const size_t gen = generation.load(std::memory_order_acquire);
171 if (arrived.fetch_add(1, std::memory_order_acq_rel) + 1 == nt) {
172 arrived.store(0, std::memory_order_relaxed);
173 generation.fetch_add(1, std::memory_order_release);
175 while (generation.load(std::memory_order_acquire) == gen) {
176 std::this_thread::yield();
180 std::atomic<size_t> successes{0};
181 std::atomic<bool> failed{
false};
182 std::exception_ptr error;
183 std::mutex error_mutex;
186 enum :
int { kLaunching = 0, kGo = 1, kAbort = 2 };
187 std::atomic<int> gate{kLaunching};
189 threading::task_group tg;
190 const auto task = [&] {
192 while ((state = gate.load(std::memory_order_acquire)) == kLaunching) {
193 std::this_thread::yield();
195 if (state == kAbort)
return;
197 for (
size_t c = 0; c < classes.size(); ++c) {
198 const std::vector<size_t>& cls = classes[c];
199 const size_t chunk = color_class_chunk(cls.size(),
int(nt), max_chunk);
200 while (!failed.load(std::memory_order_relaxed)) {
201 const size_t b = next[c].fetch_add(chunk, std::memory_order_relaxed);
202 if (b >= cls.size())
break;
203 const size_t e = std::min(cls.size(), b + chunk);
205 for (
size_t i = b; i < e; ++i) {
206 if (fn(cls[i])) ++mine;
209 std::lock_guard<std::mutex> lock(error_mutex);
210 if (!error) error = std::current_exception();
211 failed.store(
true, std::memory_order_relaxed);
215 if (c + 1 < classes.size()) barrier();
217 successes.fetch_add(mine, std::memory_order_relaxed);
220 for (
size_t t = 0; t < nt; ++t) {
224 gate.store(kAbort, std::memory_order_release);
227 gate.store(kGo, std::memory_order_release);
229 if (error) std::rethrow_exception(error);
230 return successes.load();
244template <
typename Mesh>
246smooth_classes_checked(Mesh& m,
const std::vector<std::vector<size_t>>& classes,
size_t max_chunk)
252 ~Restore() { flag = saved; }
253 } restore{m.m_check_smoothing_stays_in_star, m.m_check_smoothing_stays_in_star},
254 restore_pass{m.m_in_operation_pass, m.m_in_operation_pass};
255 m.m_check_smoothing_stays_in_star =
true;
258 m.m_in_operation_pass =
true;
259 return for_each_in_classes(classes, m.NUM_THREADS, max_chunk, [&m](
size_t v) {
260 return m.smooth_vertex(m.tuple_from_vertex(v));