GCC Code Coverage Report


Directory: ./
File: lib/geogram/basic/process.cpp
Date: 2026-09-07 02:25:23
Exec Total Coverage
Lines: 214 290 73.8%
Functions: 32 46 69.6%
Branches: 157 350 44.9%

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 249 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 944 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 944 times.
944 if(name == "sys:nb_cores") {
103 value = String::to_string(Process::number_of_cores());
104 return true;
105 }
106
2/2
✓ Branch 0 taken 349 times.
✓ Branch 1 taken 595 times.
944 if(name == "sys:multithread") {
107 349 value = String::to_string(multithreading_enabled_);
108 349 return true;
109 }
110
2/2
✓ Branch 0 taken 105 times.
✓ Branch 1 taken 490 times.
595 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 385 times.
490 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 280 times.
385 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 175 times.
280 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 130 times.
✓ Branch 1 taken 45 times.
175 if(name == "algo:random_seed") {
129 130 value = String::to_string(random_seed_);
130 130 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 12256 bool set_local_value(
148 const std::string& name, const std::string& value
149 ) override {
150
2/2
✓ Branch 0 taken 249 times.
✓ Branch 1 taken 12007 times.
12256 if(name == "sys:multithread") {
151 249 Process::enable_multithreading(String::to_bool(value));
152 249 return true;
153 }
154
2/2
✓ Branch 0 taken 249 times.
✓ Branch 1 taken 11758 times.
12007 if(name == "sys:max_threads") {
155 249 Process::set_max_threads(String::to_uint(value));
156 249 return true;
157 }
158
2/2
✓ Branch 0 taken 249 times.
✓ Branch 1 taken 11509 times.
11758 if(name == "sys:FPE") {
159 249 Process::enable_FPE(String::to_bool(value));
160 249 return true;
161 }
162
2/2
✓ Branch 0 taken 249 times.
✓ Branch 1 taken 11260 times.
11509 if(name == "sys:cancel") {
163 249 Process::enable_cancel(String::to_bool(value));
164 249 return true;
165 }
166
2/2
✓ Branch 0 taken 249 times.
✓ Branch 1 taken 11011 times.
11260 if(name == "sys:assert") {
167
1/2
✓ Branch 0 taken 249 times.
✗ Branch 1 not taken.
249 if(value == "throw") {
168 249 set_assert_mode(ASSERT_THROW);
169 249 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 10775 times.
✓ Branch 1 taken 236 times.
11011 if(name == "algo:random_seed") {
186 236 random_seed_ = String::to_int(value);
187 236 Numeric::random_reset(random_seed_);
188 236 return true;
189 }
190 return false;
191 }
192
193 /** ProcessEnvironment destructor */
194 498 ~ProcessEnvironment() override {
195 498 }
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 12200 void Thread::set_current(Thread* thread) {
314 12200 geo_current_thread_ = thread;
315 12200 }
316
317 4872 Thread* Thread::current() {
318 4872 return geo_current_thread_;
319 }
320
321 24416 Thread::~Thread() {
322 24416 }
323
324 /************************************************************************/
325
326 498 ThreadManager::~ThreadManager() {
327 498 }
328
329 2817 void ThreadManager::run_threads(ThreadGroup& threads) {
330 2817 index_t max_threads = maximum_concurrent_threads();
331
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 2817 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2817 times.
2817 if(Process::multithreading_enabled() && max_threads > 1) {
332 2817 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 2817 }
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 249 void initialize(int flags) {
362
363 249 Environment* env = Environment::instance();
364 249 env->add_environment(new ProcessEnvironment);
365
366
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 249 times.
249 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 249 times.
✗ Branch 2 not taken.
249 (::getenv("GEO_NO_SIGNAL_HANDLER") == nullptr) &&
387
1/2
✓ Branch 0 taken 249 times.
✗ Branch 1 not taken.
249 ((flags & GEOGRAM_INSTALL_HANDLERS) != 0)
388 ) {
389 249 os_install_signal_handlers();
390 }
391
392 // Initialize Process default values
393 249 enable_multithreading(multithreading_enabled_);
394 249 set_max_threads(number_of_cores());
395
1/2
✓ Branch 0 taken 249 times.
✗ Branch 1 not taken.
249 if (flags & GEOGRAM_INSTALL_FPE) {
396 249 enable_FPE(fpe_enabled_);
397 }
398 249 enable_cancel(cancel_enabled_);
399
400 249 start_time_ = Stopwatch::now();
401 249 }
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 249 times.
✗ Branch 1 not taken.
249 void terminate() {
441 thread_manager_.reset();
442 249 }
443
444 void brute_force_kill() {
445 os_brute_force_kill();
446 }
447
448 4062 index_t number_of_cores() {
449 static index_t result = 0;
450
2/2
✓ Branch 0 taken 249 times.
✓ Branch 1 taken 3813 times.
4062 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 249 result = os_number_of_cores();
457 #endif
458 }
459 4062 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 249 void set_thread_manager(ThreadManager* thread_manager) {
479 249 thread_manager_ = thread_manager;
480 249 }
481
482 2817 void run_threads(ThreadGroup& threads) {
483 2817 running_threads_invocations_++;
484 2817 thread_manager_->run_threads(threads);
485 2817 running_threads_invocations_--;
486 2817 }
487
488 17656 bool is_running_threads() {
489 #ifdef GEO_OPENMP
490 return (
491
1/2
✓ Branch 1 taken 17656 times.
✗ Branch 2 not taken.
17656 omp_in_parallel() ||
492
2/2
✓ Branch 0 taken 6639 times.
✓ Branch 1 taken 11017 times.
17656 (running_threads_invocations_ > 0)
493 17656 );
494 #else
495 return running_threads_invocations_ > 0;
496 #endif
497 }
498
499 3066 bool multithreading_enabled() {
500 3066 return multithreading_enabled_;
501 }
502
503 498 void enable_multithreading(bool flag) {
504
2/2
✓ Branch 0 taken 249 times.
✓ Branch 1 taken 249 times.
498 if(
505 249 multithreading_initialized_ &&
506
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 249 times.
249 multithreading_enabled_ == flag
507 ) {
508 return;
509 }
510 249 multithreading_initialized_ = true;
511 249 multithreading_enabled_ = flag;
512
1/2
✓ Branch 0 taken 249 times.
✗ Branch 1 not taken.
249 if(multithreading_enabled_) {
513
1/2
✓ Branch 2 taken 249 times.
✗ Branch 3 not taken.
249 Logger::out("Process")
514 << "Multithreading enabled" << std::endl
515
1/2
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
249 << "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 249 times.
249 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 249 times.
249 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 498 void set_max_threads(index_t num_threads) {
544 498 if(
545
2/2
✓ Branch 0 taken 249 times.
✓ Branch 1 taken 249 times.
498 max_threads_initialized_ &&
546
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 249 times.
249 max_threads_ == num_threads
547 ) {
548 return;
549 }
550 249 max_threads_initialized_ = true;
551
1/2
✓ Branch 0 taken 249 times.
✗ Branch 1 not taken.
249 if(num_threads == 0) {
552 num_threads = 1;
553
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 249 times.
249 } 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 249 max_threads_ = num_threads;
561
1/2
✓ Branch 2 taken 249 times.
✗ Branch 3 not taken.
498 Logger::out("Process")
562
1/2
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
249 << "Max used threads = " << max_threads_
563 << std::endl;
564 }
565
566 2422 index_t maximum_concurrent_threads() {
567
2/4
✓ Branch 0 taken 2422 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2422 times.
✗ Branch 3 not taken.
2422 if(!multithreading_enabled_ || thread_manager_ == nullptr) {
568 return 1;
569 }
570 2422 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 249 bool FPE_enabled() {
584 249 return fpe_enabled_;
585 }
586
587 498 void enable_FPE(bool flag) {
588
3/4
✓ Branch 0 taken 249 times.
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 249 times.
498 if(fpe_initialized_ && fpe_enabled_ == flag) {
589 return;
590 }
591 249 fpe_initialized_ = true;
592 249 fpe_enabled_ = flag;
593 249 os_enable_FPE(flag);
594 }
595
596 249 bool cancel_enabled() {
597 249 return cancel_enabled_;
598 }
599
600 498 void enable_cancel(bool flag) {
601
3/4
✓ Branch 0 taken 249 times.
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 249 times.
498 if(cancel_initialized_ && cancel_enabled_ == flag) {
602 return;
603 }
604 249 cancel_initialized_ = true;
605 249 cancel_enabled_ = flag;
606
607
1/2
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
249 if(os_enable_cancel(flag)) {
608
1/2
✓ Branch 2 taken 249 times.
✗ Branch 3 not taken.
498 Logger::out("Process")
609
2/4
✓ Branch 0 taken 249 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 249 times.
✗ Branch 4 not taken.
498 << (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 6762 ParallelThread(
634 std::function<void(void)> func
635
1/2
✓ Branch 1 taken 6762 times.
✗ Branch 2 not taken.
6762 ) : func_(func) {
636 6762 }
637
638 /**
639 * \copydoc Thread::run()
640 */
641
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6762 times.
6762 void run() override {
642 func_();
643 6762 }
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 2008 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2008 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 652 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 652 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 1947 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 1947 times.
✗ Branch 17 not taken.
4607 ) : func_(func), from_(from), to_(to), step_(step) {
666 }
667
668 /**
669 * \copydoc Thread::run()
670 */
671 4607 void run() override {
672
2/2
✓ Branch 0 taken 2989673 times.
✓ Branch 1 taken 4607 times.
2994280 for(index_t i = from_; i < to_; i += step_) {
673 2989673 func_(i);
674 }
675 4607 }
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 683 ParallelForSliceThread(
696 std::function<void(index_t,index_t)> func,
697 index_t from, index_t to
698
1/2
✓ Branch 1 taken 683 times.
✗ Branch 2 not taken.
683 ) : func_(func), from_(from), to_(to) {
699 683 }
700
701 /**
702 * \copydoc Thread::run()
703 */
704 683 void run() override {
705
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 683 times.
683 func_(from_, to_);
706 683 }
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 1154 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 1154 to - from,
728 1154 Process::maximum_concurrent_threads() * threads_per_core
729 );
730
731 nb_threads = std::max(index_t(1), nb_threads);
732
733 1154 index_t batch_size = (to - from) / nb_threads;
734
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1154 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1154 times.
1154 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 652 times.
✓ Branch 1 taken 502 times.
1154 if(interleaved) {
741
2/2
✓ Branch 0 taken 2008 times.
✓ Branch 1 taken 502 times.
2510 for(index_t i = 0; i < nb_threads; i++) {
742 threads.push_back(
743 2008 new ParallelForThread(
744 func, from + i, to, nb_threads
745
2/6
✓ Branch 1 taken 2008 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2008 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
4016 )
746 );
747 }
748 } else {
749 index_t cur = from;
750
2/2
✓ Branch 0 taken 2599 times.
✓ Branch 1 taken 652 times.
3251 for(index_t i = 0; i < nb_threads; i++) {
751
2/2
✓ Branch 0 taken 652 times.
✓ Branch 1 taken 1947 times.
2599 if(i == nb_threads - 1) {
752 threads.push_back(
753 652 new ParallelForThread(
754 func, cur, to
755
2/6
✓ Branch 1 taken 652 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 652 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
1304 )
756 );
757 } else {
758 threads.push_back(
759 1947 new ParallelForThread(
760 func, cur, cur + batch_size
761
2/6
✓ Branch 1 taken 1947 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1947 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
3894 )
762 );
763 }
764 2599 cur += batch_size;
765 }
766 }
767
1/2
✓ Branch 1 taken 1154 times.
✗ Branch 2 not taken.
1154 Process::run_threads(threads);
768 1154 }
769 1154 }
770
771
772 188 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 188 to - from,
783 188 Process::maximum_concurrent_threads() * threads_per_core
784 );
785
786 nb_threads = std::max(index_t(1), nb_threads);
787
788 188 index_t batch_size = (to - from) / nb_threads;
789
3/4
✓ Branch 1 taken 188 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 177 times.
✓ Branch 4 taken 11 times.
188 if(Process::is_running_threads() || nb_threads == 1) {
790 11 func(from, to);
791 } else {
792 ThreadGroup threads;
793 index_t cur = from;
794
2/2
✓ Branch 0 taken 683 times.
✓ Branch 1 taken 177 times.
860 for(index_t i = 0; i < nb_threads; i++) {
795
2/2
✓ Branch 0 taken 177 times.
✓ Branch 1 taken 506 times.
683 if(i == nb_threads - 1) {
796 threads.push_back(
797 177 new ParallelForSliceThread(
798 func, cur, to
799
3/8
✓ Branch 1 taken 177 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 177 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 177 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
354 )
800 );
801 } else {
802 threads.push_back(
803 506 new ParallelForSliceThread(
804 func, cur, cur + batch_size
805
3/8
✓ Branch 1 taken 506 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 506 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 506 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
1012 )
806 );
807 }
808 683 cur += batch_size;
809 }
810
1/2
✓ Branch 1 taken 177 times.
✗ Branch 2 not taken.
177 Process::run_threads(threads);
811 177 }
812 188 }
813
814 483 void parallel(
815 std::function<void()> f1,
816 std::function<void()> f2
817 ) {
818
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 483 times.
483 if(Process::is_running_threads()) {
819 f1();
820 f2();
821 } else {
822 ThreadGroup threads;
823
3/8
✓ Branch 1 taken 483 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 483 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 483 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
483 threads.push_back(new ParallelThread(f1));
824
3/8
✓ Branch 1 taken 483 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 483 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 483 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
483 threads.push_back(new ParallelThread(f2));
825
1/2
✓ Branch 1 taken 483 times.
✗ Branch 2 not taken.
483 Process::run_threads(threads);
826 483 }
827 483 }
828
829
830 483 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 483 times.
483 if(Process::is_running_threads()) {
837 f1();
838 f2();
839 f3();
840 f4();
841 } else {
842 ThreadGroup threads;
843
3/8
✓ Branch 1 taken 483 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 483 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 483 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
483 threads.push_back(new ParallelThread(f1));
844
3/8
✓ Branch 1 taken 483 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 483 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 483 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
483 threads.push_back(new ParallelThread(f2));
845
3/8
✓ Branch 1 taken 483 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 483 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 483 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
483 threads.push_back(new ParallelThread(f3));
846
3/8
✓ Branch 1 taken 483 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 483 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 483 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
483 threads.push_back(new ParallelThread(f4));
847
1/2
✓ Branch 1 taken 483 times.
✗ Branch 2 not taken.
483 Process::run_threads(threads);
848 483 }
849 483 }
850
851
852 483 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 483 times.
483 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 483 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 483 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 483 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
483 threads.push_back(new ParallelThread(f1));
874
3/8
✓ Branch 1 taken 483 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 483 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 483 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
483 threads.push_back(new ParallelThread(f2));
875
3/8
✓ Branch 1 taken 483 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 483 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 483 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
483 threads.push_back(new ParallelThread(f3));
876
3/8
✓ Branch 1 taken 483 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 483 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 483 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
483 threads.push_back(new ParallelThread(f4));
877
3/8
✓ Branch 1 taken 483 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 483 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 483 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
483 threads.push_back(new ParallelThread(f5));
878
3/8
✓ Branch 1 taken 483 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 483 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 483 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
483 threads.push_back(new ParallelThread(f6));
879
3/8
✓ Branch 1 taken 483 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 483 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 483 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
483 threads.push_back(new ParallelThread(f7));
880
3/8
✓ Branch 1 taken 483 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 483 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 483 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
483 threads.push_back(new ParallelThread(f8));
881
1/2
✓ Branch 1 taken 483 times.
✗ Branch 2 not taken.
483 Process::run_threads(threads);
882 483 }
883 483 }
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