GCC Code Coverage Report


Directory: ./
File: lib/geogram/basic/process.cpp
Date: 2026-09-07 02:28:19
Exec Total Coverage
Lines: 214 290 73.8%
Functions: 32 46 69.6%
Branches: 157 352 44.6%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2000-2022 Inria
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contact: Bruno Levy
30 *
31 * https://www.inria.fr/fr/bruno-levy
32 *
33 * Inria,
34 * Domaine de Voluceau,
35 * 78150 Le Chesnay - Rocquencourt
36 * FRANCE
37 *
38 */
39
40 #include <geogram/basic/process.h>
41 #include <geogram/basic/process_private.h>
42 #include <geogram/basic/logger.h>
43 #include <geogram/basic/environment.h>
44 #include <geogram/basic/string.h>
45 #include <geogram/basic/command_line.h>
46 #include <geogram/basic/stopwatch.h>
47 #include <thread>
48 #include <chrono>
49
50 #ifdef GEO_OPENMP
51 #include <omp.h>
52 #endif
53
54 #ifdef GEO_TBB
55 #include <tbb/parallel_for.h>
56 #include <tbb/task_arena.h>
57 #endif
58
59 namespace {
60 using namespace GEO;
61
62 ThreadManager_var thread_manager_;
63 int running_threads_invocations_ = 0;
64
65 bool multithreading_initialized_ = false;
66 bool multithreading_enabled_ = true;
67
68 index_t max_threads_initialized_ = false;
69 index_t max_threads_ = 0;
70
71 bool fpe_initialized_ = false;
72 bool fpe_enabled_ = false;
73
74 bool cancel_initialized_ = false;
75 bool cancel_enabled_ = false;
76
77 double start_time_ = 0.0;
78
79 /************************************************************************/
80
81 /**
82 * \brief Process Environment
83 * \details This environment exposes and controls the configuration of the
84 * Process module.
85 */
86 251 class ProcessEnvironment : public Environment {
87 protected:
88 /**
89 * \brief Gets a Process property
90 * \details Retrieves the value of the property \p name and stores it
91 * in \p value. The property must be a valid Process property (see
92 * sys:xxx properties in Vorpaline's help).
93 * \param[in] name name of the property
94 * \param[out] value receives the value of the property
95 * \retval true if the property is a valid Process property
96 * \retval false otherwise
97 * \see Environment::get_value()
98 */
99 953 bool get_local_value(
100 const std::string& name, std::string& value
101 ) const override {
102
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 953 times.
953 if(name == "sys:nb_cores") {
103 value = String::to_string(Process::number_of_cores());
104 return true;
105 }
106
2/2
✓ Branch 0 taken 355 times.
✓ Branch 1 taken 598 times.
953 if(name == "sys:multithread") {
107 355 value = String::to_string(multithreading_enabled_);
108 355 return true;
109 }
110
2/2
✓ Branch 0 taken 105 times.
✓ Branch 1 taken 493 times.
598 if(name == "sys:max_threads") {
111 210 value = String::to_string(
112 105 Process::maximum_concurrent_threads()
113 105 );
114 105 return true;
115 }
116
2/2
✓ Branch 0 taken 105 times.
✓ Branch 1 taken 388 times.
493 if(name == "sys:FPE") {
117 105 value = String::to_string(fpe_enabled_);
118 105 return true;
119 }
120
2/2
✓ Branch 0 taken 105 times.
✓ Branch 1 taken 283 times.
388 if(name == "sys:cancel") {
121 105 value = String::to_string(cancel_enabled_);
122 105 return true;
123 }
124
2/2
✓ Branch 0 taken 105 times.
✓ Branch 1 taken 178 times.
283 if(name == "sys:assert") {
125
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 105 times.
105 value = assert_mode() == ASSERT_THROW ? "throw" : "abort";
126 105 return true;
127 }
128
2/2
✓ Branch 0 taken 132 times.
✓ Branch 1 taken 46 times.
178 if(name == "algo:random_seed") {
129 132 value = String::to_string(random_seed_);
130 132 return true;
131 }
132 return false;
133 }
134
135 /**
136 * \brief Sets a Process property
137 * \details Sets the property \p name with value \p value in the
138 * Process. The property must be a valid Process property (see sys:xxx
139 * properties in Vorpaline's help) and \p value must be a legal value
140 * for the property.
141 * \param[in] name name of the property
142 * \param[in] value value of the property
143 * \retval true if the property was successfully set
144 * \retval false otherwise
145 * \see Environment::set_value()
146 */
147 12377 bool set_local_value(
148 const std::string& name, const std::string& value
149 ) override {
150
2/2
✓ Branch 0 taken 251 times.
✓ Branch 1 taken 12126 times.
12377 if(name == "sys:multithread") {
151 251 Process::enable_multithreading(String::to_bool(value));
152 251 return true;
153 }
154
2/2
✓ Branch 0 taken 251 times.
✓ Branch 1 taken 11875 times.
12126 if(name == "sys:max_threads") {
155 251 Process::set_max_threads(String::to_uint(value));
156 251 return true;
157 }
158
2/2
✓ Branch 0 taken 251 times.
✓ Branch 1 taken 11624 times.
11875 if(name == "sys:FPE") {
159 251 Process::enable_FPE(String::to_bool(value));
160 251 return true;
161 }
162
2/2
✓ Branch 0 taken 251 times.
✓ Branch 1 taken 11373 times.
11624 if(name == "sys:cancel") {
163 251 Process::enable_cancel(String::to_bool(value));
164 251 return true;
165 }
166
2/2
✓ Branch 0 taken 251 times.
✓ Branch 1 taken 11122 times.
11373 if(name == "sys:assert") {
167
1/2
✓ Branch 0 taken 251 times.
✗ Branch 1 not taken.
251 if(value == "throw") {
168 251 set_assert_mode(ASSERT_THROW);
169 251 return true;
170 }
171 if(value == "abort") {
172 set_assert_mode(ASSERT_ABORT);
173 return true;
174 }
175 if(value == "breakpoint") {
176 set_assert_mode(ASSERT_BREAKPOINT);
177 return true;
178 }
179 Logger::err("Process")
180 << "Invalid value for property sys:abort: "
181 << value
182 << std::endl;
183 return false;
184 }
185
2/2
✓ Branch 0 taken 10884 times.
✓ Branch 1 taken 238 times.
11122 if(name == "algo:random_seed") {
186 238 random_seed_ = String::to_int(value);
187 238 Numeric::random_reset(random_seed_);
188 238 return true;
189 }
190 return false;
191 }
192
193 /** ProcessEnvironment destructor */
194 502 ~ProcessEnvironment() override {
195 502 }
196 private:
197 int random_seed_ = -1;
198 };
199
200 /************************************************************************/
201
202 #ifdef GEO_OPENMP
203
204 /**
205 * \brief OpenMP Thread Manager
206 * \details
207 * OMPThreadManager is an implementation of ThreadManager that uses OpenMP
208 * for running concurrent threads and control critical sections.
209 */
210 class GEOGRAM_API OMPThreadManager : public ThreadManager {
211 public:
212 /**
213 * \brief Creates and initializes the OpenMP ThreadManager
214 */
215 OMPThreadManager() {
216 }
217
218 /** \copydoc GEO::ThreadManager::maximum_concurrent_threads() */
219 virtual index_t maximum_concurrent_threads() {
220 return Process::number_of_cores();
221 }
222
223 protected:
224 /** \brief OMPThreadManager destructor */
225 virtual ~OMPThreadManager() {
226 }
227
228 /** \copydoc GEO::ThreadManager::run_concurrent_threads() */
229 virtual void run_concurrent_threads(
230 ThreadGroup& threads, index_t max_threads
231 ) {
232 // TODO: take max_threads_ into account
233 geo_argused(max_threads);
234
235 #pragma omp parallel for schedule(dynamic)
236 for(int i = 0; i < int(threads.size()); i++) {
237 index_t ii = index_t(i);
238 set_thread_id(threads[ii],ii);
239 set_current_thread(threads[ii]);
240 threads[ii]->run();
241 }
242 }
243 };
244
245 #endif
246
247 #ifdef GEO_TBB
248
249 /**
250 * \brief TBB Thread Manager
251 * \details
252 * TBBThreadManager is an implementation of ThreadManager that uses TBB
253 * for running concurrent threads and control critical sections.
254 */
255 class GEOGRAM_API TBBThreadManager : public ThreadManager {
256 public:
257 /**
258 * \brief Creates and initializes the TBB ThreadManager
259 */
260 TBBThreadManager() {
261 }
262
263 /** \copydoc GEO::ThreadManager::maximum_concurrent_threads() */
264 virtual index_t maximum_concurrent_threads() {
265 return tbb::this_task_arena::max_concurrency();
266 }
267
268 protected:
269 /** \brief TBBThreadManager destructor */
270 virtual ~TBBThreadManager() {
271 }
272
273 /** \copydoc GEO::ThreadManager::run_concurrent_threads() */
274 virtual void run_concurrent_threads(
275 ThreadGroup& threads, index_t max_threads
276 ) {
277 tbb::task_arena arena(static_cast<std::int32_t>(max_threads));
278 arena.execute([&threads] {
279 tbb::parallel_for(
280 tbb::blocked_range<std::size_t>(0, threads.size()),
281 [&threads](const tbb::blocked_range<std::size_t>& tbb_range) {
282 for (std::size_t i = tbb_range.begin(); i < tbb_range.end(); ++i) {
283 index_t ii = static_cast<index_t>(i);
284 set_thread_id(threads[ii],ii);
285 set_current_thread(threads[ii]);
286 threads[ii]->run();
287 }
288 }
289 );
290 });
291
292 }
293 };
294
295 #endif
296 }
297
298
299 namespace {
300 /**
301 * \brief The (thread-local) variable that stores a
302 * pointer to the current thread.
303 * \details It cannot be a static member of class
304 * Thread, because Visual C++ does not accept
305 * to export thread local storage variables in
306 * DLLs.
307 */
308 thread_local Thread* geo_current_thread_ = nullptr;
309 }
310
311 namespace GEO {
312
313 11976 void Thread::set_current(Thread* thread) {
314 11976 geo_current_thread_ = thread;
315 11976 }
316
317 4878 Thread* Thread::current() {
318 4878 return geo_current_thread_;
319 }
320
321 23968 Thread::~Thread() {
322 23968 }
323
324 /************************************************************************/
325
326 502 ThreadManager::~ThreadManager() {
327 502 }
328
329 2766 void ThreadManager::run_threads(ThreadGroup& threads) {
330 2766 index_t max_threads = maximum_concurrent_threads();
331
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 2766 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2766 times.
2766 if(Process::multithreading_enabled() && max_threads > 1) {
332 2766 run_concurrent_threads(threads, max_threads);
333 } else {
334 for(index_t i = 0; i < threads.size(); i++) {
335 threads[i]->run();
336 }
337 }
338 2766 }
339
340 /************************************************************************/
341
342 MonoThreadingThreadManager::~MonoThreadingThreadManager() {
343 }
344
345 void MonoThreadingThreadManager::run_concurrent_threads(
346 ThreadGroup& threads, index_t max_threads
347 ) {
348 geo_argused(threads);
349 geo_argused(max_threads);
350 geo_assert_not_reached;
351 }
352
353 index_t MonoThreadingThreadManager::maximum_concurrent_threads() {
354 return 1;
355 }
356
357 /************************************************************************/
358
359 namespace Process {
360
361 251 void initialize(int flags) {
362
363 251 Environment* env = Environment::instance();
364 251 env->add_environment(new ProcessEnvironment);
365
366
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 251 times.
251 if(!os_init_threads()) {
367 #ifdef GEO_OPENMP
368 Logger::out("Process")
369 << "Using OpenMP threads"
370 << std::endl;
371 set_thread_manager(new OMPThreadManager);
372 #elif defined(GEO_TBB)
373 Logger::out("Process")
374 << "Using TBB threads"
375 << std::endl;
376 set_thread_manager(new TBBThreadManager);
377 #else
378 Logger::out("Process")
379 << "Multithreading not supported, going monothread"
380 << std::endl;
381 set_thread_manager(new MonoThreadingThreadManager);
382 #endif
383 }
384
385 if(
386
1/2
✓ Branch 1 taken 251 times.
✗ Branch 2 not taken.
251 (::getenv("GEO_NO_SIGNAL_HANDLER") == nullptr) &&
387
1/2
✓ Branch 0 taken 251 times.
✗ Branch 1 not taken.
251 ((flags & GEOGRAM_INSTALL_HANDLERS) != 0)
388 ) {
389 251 os_install_signal_handlers();
390 }
391
392 // Initialize Process default values
393 251 enable_multithreading(multithreading_enabled_);
394 251 set_max_threads(number_of_cores());
395
1/2
✓ Branch 0 taken 251 times.
✗ Branch 1 not taken.
251 if (flags & GEOGRAM_INSTALL_FPE) {
396 251 enable_FPE(fpe_enabled_);
397 }
398 251 enable_cancel(cancel_enabled_);
399
400 251 start_time_ = Stopwatch::now();
401 251 }
402
403 void show_stats() {
404
405 Stopwatch::show_stats();
406
407 const size_t K=size_t(1024);
408 const size_t M=K*K;
409 const size_t G=K*M;
410
411 size_t max_mem = Process::max_used_memory() ;
412 size_t r = max_mem;
413
414 size_t mem_G = r / G;
415 r = r % G;
416 size_t mem_M = r / M;
417 r = r % M;
418 size_t mem_K = r / K;
419 r = r % K;
420
421 std::string s;
422 if(mem_G != 0) {
423 s += String::to_string(mem_G)+"G ";
424 }
425 if(mem_M != 0) {
426 s += String::to_string(mem_M)+"M ";
427 }
428 if(mem_K != 0) {
429 s += String::to_string(mem_K)+"K ";
430 }
431 if(r != 0) {
432 s += String::to_string(r);
433 }
434
435 Logger::out("Process") << "Maximum used memory: "
436 << max_mem << " (" << s << ")"
437 << std::endl;
438 }
439
440
1/2
✓ Branch 0 taken 251 times.
✗ Branch 1 not taken.
251 void terminate() {
441 thread_manager_.reset();
442 251 }
443
444 void brute_force_kill() {
445 os_brute_force_kill();
446 }
447
448 4021 index_t number_of_cores() {
449 static index_t result = 0;
450
2/2
✓ Branch 0 taken 251 times.
✓ Branch 1 taken 3770 times.
4021 if(result == 0) {
451 #ifdef GEO_NO_THREAD_LOCAL
452 // Deactivate multithreading if thread_local is
453 // not supported (e.g. with old OS-X).
454 result = 1;
455 #else
456 251 result = os_number_of_cores();
457 #endif
458 }
459 4021 return result;
460 }
461
462 size_t used_memory() {
463 return os_used_memory();
464 }
465
466 size_t max_used_memory() {
467 return os_max_used_memory();
468 }
469
470 std::string executable_filename() {
471 return os_executable_filename();
472 }
473
474 void print_stack_trace() {
475 os_print_stack_trace();
476 }
477
478 251 void set_thread_manager(ThreadManager* thread_manager) {
479 251 thread_manager_ = thread_manager;
480 251 }
481
482 2766 void run_threads(ThreadGroup& threads) {
483 2766 running_threads_invocations_++;
484 2766 thread_manager_->run_threads(threads);
485 2766 running_threads_invocations_--;
486 2766 }
487
488 17692 bool is_running_threads() {
489 #ifdef GEO_OPENMP
490 return (
491
1/2
✓ Branch 1 taken 17692 times.
✗ Branch 2 not taken.
17692 omp_in_parallel() ||
492
2/2
✓ Branch 0 taken 6639 times.
✓ Branch 1 taken 11053 times.
17692 (running_threads_invocations_ > 0)
493 17692 );
494 #else
495 return running_threads_invocations_ > 0;
496 #endif
497 }
498
499 3017 bool multithreading_enabled() {
500 3017 return multithreading_enabled_;
501 }
502
503 502 void enable_multithreading(bool flag) {
504
2/2
✓ Branch 0 taken 251 times.
✓ Branch 1 taken 251 times.
502 if(
505 251 multithreading_initialized_ &&
506
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 251 times.
251 multithreading_enabled_ == flag
507 ) {
508 return;
509 }
510 251 multithreading_initialized_ = true;
511 251 multithreading_enabled_ = flag;
512
1/2
✓ Branch 0 taken 251 times.
✗ Branch 1 not taken.
251 if(multithreading_enabled_) {
513
1/2
✓ Branch 2 taken 251 times.
✗ Branch 3 not taken.
251 Logger::out("Process")
514 << "Multithreading enabled" << std::endl
515
1/2
✓ Branch 1 taken 251 times.
✗ Branch 2 not taken.
251 << "Available cores = " << number_of_cores()
516 << std::endl;
517 // Logger::out("Process")
518 // << "Max. concurrent threads = "
519 // << maximum_concurrent_threads() << std::endl ;
520
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 251 times.
251 if(number_of_cores() == 1) {
521 Logger::warn("Process")
522 << "Processor is not a multicore"
523 << "(or multithread is not supported)"
524 << std::endl;
525 }
526
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 251 times.
251 if(thread_manager_ == nullptr) {
527 Logger::warn("Process")
528 << "Missing multithreading manager"
529 << std::endl;
530 }
531 } else {
532 Logger::out("Process")
533 << "Multithreading disabled" << std::endl;
534 }
535 }
536
537 index_t max_threads() {
538 return max_threads_initialized_
539 ? max_threads_
540 : number_of_cores();
541 }
542
543 502 void set_max_threads(index_t num_threads) {
544 502 if(
545
2/2
✓ Branch 0 taken 251 times.
✓ Branch 1 taken 251 times.
502 max_threads_initialized_ &&
546
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 251 times.
251 max_threads_ == num_threads
547 ) {
548 return;
549 }
550 251 max_threads_initialized_ = true;
551
1/2
✓ Branch 0 taken 251 times.
✗ Branch 1 not taken.
251 if(num_threads == 0) {
552 num_threads = 1;
553
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 251 times.
251 } else if(num_threads > number_of_cores()) {
554 Logger::warn("Process")
555 << "Cannot allocate " << num_threads
556 << " for multithreading"
557 << std::endl;
558 num_threads = number_of_cores();
559 }
560 251 max_threads_ = num_threads;
561
1/2
✓ Branch 2 taken 251 times.
✗ Branch 3 not taken.
502 Logger::out("Process")
562
1/2
✓ Branch 1 taken 251 times.
✗ Branch 2 not taken.
251 << "Max used threads = " << max_threads_
563 << std::endl;
564 }
565
566 2377 index_t maximum_concurrent_threads() {
567
2/4
✓ Branch 0 taken 2377 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2377 times.
✗ Branch 3 not taken.
2377 if(!multithreading_enabled_ || thread_manager_ == nullptr) {
568 return 1;
569 }
570 2377 return max_threads_;
571 /*
572 // commented out for now, since under Windows,
573 // it seems that maximum_concurrent_threads() does not
574 // report the number of hyperthreaded cores.
575 return
576 geo_min(
577 thread_manager_->maximum_concurrent_threads(),
578 max_threads_
579 ) ;
580 */
581 }
582
583 251 bool FPE_enabled() {
584 251 return fpe_enabled_;
585 }
586
587 502 void enable_FPE(bool flag) {
588
3/4
✓ Branch 0 taken 251 times.
✓ Branch 1 taken 251 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 251 times.
502 if(fpe_initialized_ && fpe_enabled_ == flag) {
589 return;
590 }
591 251 fpe_initialized_ = true;
592 251 fpe_enabled_ = flag;
593 251 os_enable_FPE(flag);
594 }
595
596 251 bool cancel_enabled() {
597 251 return cancel_enabled_;
598 }
599
600 502 void enable_cancel(bool flag) {
601
3/4
✓ Branch 0 taken 251 times.
✓ Branch 1 taken 251 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 251 times.
502 if(cancel_initialized_ && cancel_enabled_ == flag) {
602 return;
603 }
604 251 cancel_initialized_ = true;
605 251 cancel_enabled_ = flag;
606
607
1/2
✓ Branch 1 taken 251 times.
✗ Branch 2 not taken.
251 if(os_enable_cancel(flag)) {
608
1/2
✓ Branch 2 taken 251 times.
✗ Branch 3 not taken.
502 Logger::out("Process")
609
2/4
✓ Branch 0 taken 251 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 251 times.
✗ Branch 4 not taken.
502 << (flag ? "Cancel mode enabled" : "Cancel mode disabled")
610 << std::endl;
611 } else {
612 Logger::warn("Process")
613 << "Cancel mode not implemented" << std::endl;
614 }
615 }
616 }
617 }
618
619
620 namespace {
621 using namespace GEO;
622
623 /**
624 * \brief Used by the implementation of GEO::parallel()
625 * \see GEO::parallel()
626 */
627 class ParallelThread : public Thread {
628 public:
629 /**
630 * \brief ParallelThread constructor.
631 * \param[in] func a void function with no parameter.
632 */
633 6622 ParallelThread(
634 std::function<void(void)> func
635
1/2
✓ Branch 1 taken 6622 times.
✗ Branch 2 not taken.
6622 ) : func_(func) {
636 6622 }
637
638 /**
639 * \copydoc Thread::run()
640 */
641
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6622 times.
6622 void run() override {
642 func_();
643 6622 }
644 private:
645 std::function<void()> func_;
646 };
647
648
649 /**
650 * \brief Used by the implementation of GEO::parallel_for()
651 * \see GEO::parallel_for()
652 */
653 class ParallelForThread : public Thread {
654 public:
655
656 /**
657 * \param[in] func a void function that takes an index_t
658 * \param[in] from the first iteration index
659 * \param[in] to one position past the last interation index
660 * \param[in] step iteration step
661 */
662 ParallelForThread(
663 std::function<void(index_t)> func,
664 index_t from, index_t to, index_t step=1
665
6/12
✓ Branch 1 taken 1952 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1952 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 640 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 640 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 1911 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 1911 times.
✗ Branch 17 not taken.
4503 ) : func_(func), from_(from), to_(to), step_(step) {
666 }
667
668 /**
669 * \copydoc Thread::run()
670 */
671 4503 void run() override {
672
2/2
✓ Branch 0 taken 2964539 times.
✓ Branch 1 taken 4503 times.
2969042 for(index_t i = from_; i < to_; i += step_) {
673 2964539 func_(i);
674 }
675 4503 }
676 private:
677 std::function<void(index_t)> func_;
678 index_t from_;
679 index_t to_;
680 index_t step_;
681 };
682
683 /**
684 * \brief Used by the implementation of GEO::parallel_for_slice()
685 * \see GEO::parallel_for_slice()
686 */
687 class ParallelForSliceThread : public Thread {
688 public:
689
690 /**
691 * \param[in] func a void function that takes two index_t arguments
692 * \param[in] from the first iteration index
693 * \param[in] to one position past the last interation index
694 */
695 687 ParallelForSliceThread(
696 std::function<void(index_t,index_t)> func,
697 index_t from, index_t to
698
1/2
✓ Branch 1 taken 687 times.
✗ Branch 2 not taken.
687 ) : func_(func), from_(from), to_(to) {
699 687 }
700
701 /**
702 * \copydoc Thread::run()
703 */
704 687 void run() override {
705
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 687 times.
687 func_(from_, to_);
706 687 }
707 private:
708 std::function<void(index_t,index_t)> func_;
709 index_t from_;
710 index_t to_;
711 };
712
713 }
714
715 namespace GEO {
716
717 1128 void parallel_for(
718 index_t from, index_t to, std::function<void(index_t)> func,
719 index_t threads_per_core, bool interleaved
720 ) {
721 #ifdef GEO_OS_WINDOWS
722 // TODO: This is a limitation of WindowsThreadManager, to be fixed.
723 threads_per_core = 1;
724 #endif
725
726 index_t nb_threads = std::min(
727 1128 to - from,
728 1128 Process::maximum_concurrent_threads() * threads_per_core
729 );
730
731 nb_threads = std::max(index_t(1), nb_threads);
732
733 1128 index_t batch_size = (to - from) / nb_threads;
734
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1128 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1128 times.
1128 if(Process::is_running_threads() || nb_threads == 1) {
735 for(index_t i = from; i < to; i++) {
736 func(i);
737 }
738 } else {
739 ThreadGroup threads;
740
2/2
✓ Branch 0 taken 640 times.
✓ Branch 1 taken 488 times.
1128 if(interleaved) {
741
2/2
✓ Branch 0 taken 1952 times.
✓ Branch 1 taken 488 times.
2440 for(index_t i = 0; i < nb_threads; i++) {
742 threads.push_back(
743 1952 new ParallelForThread(
744 func, from + i, to, nb_threads
745
2/6
✓ Branch 1 taken 1952 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1952 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
3904 )
746 );
747 }
748 } else {
749 index_t cur = from;
750
2/2
✓ Branch 0 taken 2551 times.
✓ Branch 1 taken 640 times.
3191 for(index_t i = 0; i < nb_threads; i++) {
751
2/2
✓ Branch 0 taken 640 times.
✓ Branch 1 taken 1911 times.
2551 if(i == nb_threads - 1) {
752 threads.push_back(
753 640 new ParallelForThread(
754 func, cur, to
755
2/6
✓ Branch 1 taken 640 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 640 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
1280 )
756 );
757 } else {
758 threads.push_back(
759 1911 new ParallelForThread(
760 func, cur, cur + batch_size
761
2/6
✓ Branch 1 taken 1911 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1911 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
3822 )
762 );
763 }
764 2551 cur += batch_size;
765 }
766 }
767
1/2
✓ Branch 1 taken 1128 times.
✗ Branch 2 not taken.
1128 Process::run_threads(threads);
768 1128 }
769 1128 }
770
771
772 191 void parallel_for_slice(
773 index_t from, index_t to, std::function<void(index_t, index_t)> func,
774 index_t threads_per_core
775 ) {
776 #ifdef GEO_OS_WINDOWS
777 // TODO: This is a limitation of WindowsThreadManager, to be fixed.
778 threads_per_core = 1;
779 #endif
780
781 index_t nb_threads = std::min(
782 191 to - from,
783 191 Process::maximum_concurrent_threads() * threads_per_core
784 );
785
786 nb_threads = std::max(index_t(1), nb_threads);
787
788 191 index_t batch_size = (to - from) / nb_threads;
789
3/4
✓ Branch 1 taken 191 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 178 times.
✓ Branch 4 taken 13 times.
191 if(Process::is_running_threads() || nb_threads == 1) {
790 13 func(from, to);
791 } else {
792 ThreadGroup threads;
793 index_t cur = from;
794
2/2
✓ Branch 0 taken 687 times.
✓ Branch 1 taken 178 times.
865 for(index_t i = 0; i < nb_threads; i++) {
795
2/2
✓ Branch 0 taken 178 times.
✓ Branch 1 taken 509 times.
687 if(i == nb_threads - 1) {
796 threads.push_back(
797 178 new ParallelForSliceThread(
798 func, cur, to
799
3/8
✓ Branch 1 taken 178 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 178 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 178 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
356 )
800 );
801 } else {
802 threads.push_back(
803 509 new ParallelForSliceThread(
804 func, cur, cur + batch_size
805
3/8
✓ Branch 1 taken 509 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 509 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 509 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
1018 )
806 );
807 }
808 687 cur += batch_size;
809 }
810
1/2
✓ Branch 1 taken 178 times.
✗ Branch 2 not taken.
178 Process::run_threads(threads);
811 178 }
812 191 }
813
814 473 void parallel(
815 std::function<void()> f1,
816 std::function<void()> f2
817 ) {
818
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 473 times.
473 if(Process::is_running_threads()) {
819 f1();
820 f2();
821 } else {
822 ThreadGroup threads;
823
3/8
✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
473 threads.push_back(new ParallelThread(f1));
824
3/8
✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
473 threads.push_back(new ParallelThread(f2));
825
1/2
✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
473 Process::run_threads(threads);
826 473 }
827 473 }
828
829
830 473 void parallel(
831 std::function<void()> f1,
832 std::function<void()> f2,
833 std::function<void()> f3,
834 std::function<void()> f4
835 ) {
836
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 473 times.
473 if(Process::is_running_threads()) {
837 f1();
838 f2();
839 f3();
840 f4();
841 } else {
842 ThreadGroup threads;
843
3/8
✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
473 threads.push_back(new ParallelThread(f1));
844
3/8
✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
473 threads.push_back(new ParallelThread(f2));
845
3/8
✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
473 threads.push_back(new ParallelThread(f3));
846
3/8
✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
473 threads.push_back(new ParallelThread(f4));
847
1/2
✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
473 Process::run_threads(threads);
848 473 }
849 473 }
850
851
852 473 void parallel(
853 std::function<void()> f1,
854 std::function<void()> f2,
855 std::function<void()> f3,
856 std::function<void()> f4,
857 std::function<void()> f5,
858 std::function<void()> f6,
859 std::function<void()> f7,
860 std::function<void()> f8
861 ) {
862
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 473 times.
473 if(Process::is_running_threads()) {
863 f1();
864 f2();
865 f3();
866 f4();
867 f5();
868 f6();
869 f7();
870 f8();
871 } else {
872 ThreadGroup threads;
873
3/8
✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
473 threads.push_back(new ParallelThread(f1));
874
3/8
✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
473 threads.push_back(new ParallelThread(f2));
875
3/8
✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
473 threads.push_back(new ParallelThread(f3));
876
3/8
✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
473 threads.push_back(new ParallelThread(f4));
877
3/8
✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
473 threads.push_back(new ParallelThread(f5));
878
3/8
✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
473 threads.push_back(new ParallelThread(f6));
879
3/8
✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
473 threads.push_back(new ParallelThread(f7));
880
3/8
✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
473 threads.push_back(new ParallelThread(f8));
881
1/2
✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
473 Process::run_threads(threads);
882 473 }
883 473 }
884
885 namespace Process {
886 void sleep(index_t microseconds) {
887 std::this_thread::sleep_for(
888 std::chrono::microseconds(microseconds)
889 );
890 }
891 }
892 }
893