GCC Code Coverage Report


Directory: ./
File: basic/process.cpp
Date: 2026-09-27 03:12:47
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 255 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 973 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 973 times.
973 if(name == "sys:nb_cores") {
103 ✗ value = String::to_string(Process::number_of_cores());
104 ✗ return true;
105 }
106
2/2
✓ Branch 0 taken 357 times.
✓ Branch 1 taken 616 times.
973 if(name == "sys:multithread") {
107 357 value = String::to_string(multithreading_enabled_);
108 357 return true;
109 }
110
2/2
✓ Branch 0 taken 105 times.
✓ Branch 1 taken 511 times.
616 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 406 times.
511 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 301 times.
406 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 196 times.
301 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 135 times.
✓ Branch 1 taken 61 times.
196 if(name == "algo:random_seed") {
129 135 value = String::to_string(random_seed_);
130 135 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 12585 bool set_local_value(
148 const std::string& name, const std::string& value
149 ) override {
150
2/2
✓ Branch 0 taken 255 times.
✓ Branch 1 taken 12330 times.
12585 if(name == "sys:multithread") {
151 255 Process::enable_multithreading(String::to_bool(value));
152 255 return true;
153 }
154
2/2
✓ Branch 0 taken 255 times.
✓ Branch 1 taken 12075 times.
12330 if(name == "sys:max_threads") {
155 255 Process::set_max_threads(String::to_uint(value));
156 255 return true;
157 }
158
2/2
✓ Branch 0 taken 255 times.
✓ Branch 1 taken 11820 times.
12075 if(name == "sys:FPE") {
159 255 Process::enable_FPE(String::to_bool(value));
160 255 return true;
161 }
162
2/2
✓ Branch 0 taken 255 times.
✓ Branch 1 taken 11565 times.
11820 if(name == "sys:cancel") {
163 255 Process::enable_cancel(String::to_bool(value));
164 255 return true;
165 }
166
2/2
✓ Branch 0 taken 255 times.
✓ Branch 1 taken 11310 times.
11565 if(name == "sys:assert") {
167
1/2
✓ Branch 0 taken 255 times.
✗ Branch 1 not taken.
255 if(value == "throw") {
168 255 set_assert_mode(ASSERT_THROW);
169 255 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 11068 times.
✓ Branch 1 taken 242 times.
11310 if(name == "algo:random_seed") {
186 242 random_seed_ = String::to_int(value);
187 242 Numeric::random_reset(random_seed_);
188 242 return true;
189 }
190 return false;
191 }
192
193 /** ProcessEnvironment destructor */
194 510 ~ProcessEnvironment() override {
195 510 }
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 13498 void Thread::set_current(Thread* thread) {
314 13498 geo_current_thread_ = thread;
315 13498 }
316
317 4878 Thread* Thread::current() {
318 4878 return geo_current_thread_;
319 }
320
321 27028 Thread::~Thread() {
322 27028 }
323
324 /************************************************************************/
325
326 510 ThreadManager::~ThreadManager() {
327 510 }
328
329 3111 void ThreadManager::run_threads(ThreadGroup& threads) {
330 3111 index_t max_threads = maximum_concurrent_threads();
331
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 3111 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3111 times.
3111 if(Process::multithreading_enabled() && max_threads > 1) {
332 3111 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 3111 }
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 255 void initialize(int flags) {
362
363 255 Environment* env = Environment::instance();
364 255 env->add_environment(new ProcessEnvironment);
365
366
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 255 times.
255 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 255 times.
✗ Branch 2 not taken.
255 (::getenv("GEO_NO_SIGNAL_HANDLER") == nullptr) &&
387
1/2
✓ Branch 0 taken 255 times.
✗ Branch 1 not taken.
255 ((flags & GEOGRAM_INSTALL_HANDLERS) != 0)
388 ) {
389 255 os_install_signal_handlers();
390 }
391
392 // Initialize Process default values
393 255 enable_multithreading(multithreading_enabled_);
394 255 set_max_threads(number_of_cores());
395
1/2
✓ Branch 0 taken 255 times.
✗ Branch 1 not taken.
255 if (flags & GEOGRAM_INSTALL_FPE) {
396 255 enable_FPE(fpe_enabled_);
397 }
398 255 enable_cancel(cancel_enabled_);
399
400 255 start_time_ = Stopwatch::now();
401 255 }
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 255 times.
✗ Branch 1 not taken.
255 void terminate() {
441 thread_manager_.reset();
442 255 }
443
444 ✗ void brute_force_kill() {
445 ✗ os_brute_force_kill();
446 ✗ }
447
448 4386 index_t number_of_cores() {
449 static index_t result = 0;
450
2/2
✓ Branch 0 taken 255 times.
✓ Branch 1 taken 4131 times.
4386 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 255 result = os_number_of_cores();
457 #endif
458 }
459 4386 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 255 void set_thread_manager(ThreadManager* thread_manager) {
479 255 thread_manager_ = thread_manager;
480 255 }
481
482 3111 void run_threads(ThreadGroup& threads) {
483 3111 running_threads_invocations_++;
484 3111 thread_manager_->run_threads(threads);
485 3111 running_threads_invocations_--;
486 3111 }
487
488 19587 bool is_running_threads() {
489 #ifdef GEO_OPENMP
490 return (
491
1/2
✓ Branch 1 taken 19587 times.
✗ Branch 2 not taken.
19587 omp_in_parallel() ||
492
2/2
✓ Branch 0 taken 6640 times.
✓ Branch 1 taken 12947 times.
19587 (running_threads_invocations_ > 0)
493 19587 );
494 #else
495 return running_threads_invocations_ > 0;
496 #endif
497 }
498
499 3366 bool multithreading_enabled() {
500 3366 return multithreading_enabled_;
501 }
502
503 510 void enable_multithreading(bool flag) {
504
2/2
✓ Branch 0 taken 255 times.
✓ Branch 1 taken 255 times.
510 if(
505 255 multithreading_initialized_ &&
506
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 255 times.
255 multithreading_enabled_ == flag
507 ) {
508 return;
509 }
510 255 multithreading_initialized_ = true;
511 255 multithreading_enabled_ = flag;
512
1/2
✓ Branch 0 taken 255 times.
✗ Branch 1 not taken.
255 if(multithreading_enabled_) {
513
1/2
✓ Branch 2 taken 255 times.
✗ Branch 3 not taken.
255 Logger::out("Process")
514 << "Multithreading enabled" << std::endl
515
1/2
✓ Branch 1 taken 255 times.
✗ Branch 2 not taken.
255 << "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 255 times.
255 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 255 times.
255 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 510 void set_max_threads(index_t num_threads) {
544 510 if(
545
2/2
✓ Branch 0 taken 255 times.
✓ Branch 1 taken 255 times.
510 max_threads_initialized_ &&
546
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 255 times.
255 max_threads_ == num_threads
547 ) {
548 return;
549 }
550 255 max_threads_initialized_ = true;
551
1/2
✓ Branch 0 taken 255 times.
✗ Branch 1 not taken.
255 if(num_threads == 0) {
552 num_threads = 1;
553
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 255 times.
255 } 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 255 max_threads_ = num_threads;
561
1/2
✓ Branch 2 taken 255 times.
✗ Branch 3 not taken.
510 Logger::out("Process")
562
1/2
✓ Branch 1 taken 255 times.
✗ Branch 2 not taken.
255 << "Max used threads = " << max_threads_
563 << std::endl;
564 }
565
566 2631 index_t maximum_concurrent_threads() {
567
2/4
✓ Branch 0 taken 2631 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2631 times.
✗ Branch 3 not taken.
2631 if(!multithreading_enabled_ || thread_manager_ == nullptr) {
568 return 1;
569 }
570 2631 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 255 bool FPE_enabled() {
584 255 return fpe_enabled_;
585 }
586
587 510 void enable_FPE(bool flag) {
588
3/4
✓ Branch 0 taken 255 times.
✓ Branch 1 taken 255 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 255 times.
510 if(fpe_initialized_ && fpe_enabled_ == flag) {
589 return;
590 }
591 255 fpe_initialized_ = true;
592 255 fpe_enabled_ = flag;
593 255 os_enable_FPE(flag);
594 }
595
596 255 bool cancel_enabled() {
597 255 return cancel_enabled_;
598 }
599
600 510 void enable_cancel(bool flag) {
601
3/4
✓ Branch 0 taken 255 times.
✓ Branch 1 taken 255 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 255 times.
510 if(cancel_initialized_ && cancel_enabled_ == flag) {
602 return;
603 }
604 255 cancel_initialized_ = true;
605 255 cancel_enabled_ = flag;
606
607
1/2
✓ Branch 1 taken 255 times.
✗ Branch 2 not taken.
255 if(os_enable_cancel(flag)) {
608
1/2
✓ Branch 2 taken 255 times.
✗ Branch 3 not taken.
510 Logger::out("Process")
609
2/4
✓ Branch 0 taken 255 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 255 times.
✗ Branch 4 not taken.
510 << (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 7616 ParallelThread(
634 std::function<void(void)> func
635
1/2
✓ Branch 1 taken 7616 times.
✗ Branch 2 not taken.
7616 ) : func_(func) {
636 7616 }
637
638 /**
639 * \copydoc Thread::run()
640 */
641
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7616 times.
7616 void run() override {
642 func_();
643 7616 }
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 2192 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2192 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 708 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 708 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 2115 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 2115 times.
✗ Branch 17 not taken.
5015 ) : func_(func), from_(from), to_(to), step_(step) {
666 ✗ }
667
668 /**
669 * \copydoc Thread::run()
670 */
671 5015 void run() override {
672
2/2
✓ Branch 0 taken 3438024 times.
✓ Branch 1 taken 5015 times.
3443039 for(index_t i = from_; i < to_; i += step_) {
673 3438024 func_(i);
674 }
675 5015 }
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 1256 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 1256 to - from,
728 1256 Process::maximum_concurrent_threads() * threads_per_core
729 );
730
731 nb_threads = std::max(index_t(1), nb_threads);
732
733 1256 index_t batch_size = (to - from) / nb_threads;
734
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1256 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1256 times.
1256 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 708 times.
✓ Branch 1 taken 548 times.
1256 if(interleaved) {
741
2/2
✓ Branch 0 taken 2192 times.
✓ Branch 1 taken 548 times.
2740 for(index_t i = 0; i < nb_threads; i++) {
742 threads.push_back(
743 2192 new ParallelForThread(
744 func, from + i, to, nb_threads
745
2/6
✓ Branch 1 taken 2192 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2192 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
4384 )
746 );
747 }
748 } else {
749 index_t cur = from;
750
2/2
✓ Branch 0 taken 2823 times.
✓ Branch 1 taken 708 times.
3531 for(index_t i = 0; i < nb_threads; i++) {
751
2/2
✓ Branch 0 taken 708 times.
✓ Branch 1 taken 2115 times.
2823 if(i == nb_threads - 1) {
752 threads.push_back(
753 708 new ParallelForThread(
754 func, cur, to
755
2/6
✓ Branch 1 taken 708 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 708 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
1416 )
756 );
757 } else {
758 threads.push_back(
759 2115 new ParallelForThread(
760 func, cur, cur + batch_size
761
2/6
✓ Branch 1 taken 2115 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2115 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
4230 )
762 );
763 }
764 2823 cur += batch_size;
765 }
766 }
767
1/2
✓ Branch 1 taken 1256 times.
✗ Branch 2 not taken.
1256 Process::run_threads(threads);
768 1256 }
769 1256 }
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 544 void parallel(
815 std::function<void()> f1,
816 std::function<void()> f2
817 ) {
818
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 544 times.
544 if(Process::is_running_threads()) {
819 f1();
820 f2();
821 } else {
822 ThreadGroup threads;
823
3/8
✓ Branch 1 taken 544 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 544 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 544 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
544 threads.push_back(new ParallelThread(f1));
824
3/8
✓ Branch 1 taken 544 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 544 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 544 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
544 threads.push_back(new ParallelThread(f2));
825
1/2
✓ Branch 1 taken 544 times.
✗ Branch 2 not taken.
544 Process::run_threads(threads);
826 544 }
827 544 }
828
829
830 544 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 544 times.
544 if(Process::is_running_threads()) {
837 f1();
838 f2();
839 f3();
840 f4();
841 } else {
842 ThreadGroup threads;
843
3/8
✓ Branch 1 taken 544 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 544 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 544 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
544 threads.push_back(new ParallelThread(f1));
844
3/8
✓ Branch 1 taken 544 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 544 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 544 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
544 threads.push_back(new ParallelThread(f2));
845
3/8
✓ Branch 1 taken 544 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 544 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 544 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
544 threads.push_back(new ParallelThread(f3));
846
3/8
✓ Branch 1 taken 544 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 544 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 544 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
544 threads.push_back(new ParallelThread(f4));
847
1/2
✓ Branch 1 taken 544 times.
✗ Branch 2 not taken.
544 Process::run_threads(threads);
848 544 }
849 544 }
850
851
852 544 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 544 times.
544 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 544 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 544 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 544 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
544 threads.push_back(new ParallelThread(f1));
874
3/8
✓ Branch 1 taken 544 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 544 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 544 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
544 threads.push_back(new ParallelThread(f2));
875
3/8
✓ Branch 1 taken 544 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 544 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 544 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
544 threads.push_back(new ParallelThread(f3));
876
3/8
✓ Branch 1 taken 544 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 544 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 544 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
544 threads.push_back(new ParallelThread(f4));
877
3/8
✓ Branch 1 taken 544 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 544 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 544 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
544 threads.push_back(new ParallelThread(f5));
878
3/8
✓ Branch 1 taken 544 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 544 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 544 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
544 threads.push_back(new ParallelThread(f6));
879
3/8
✓ Branch 1 taken 544 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 544 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 544 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
544 threads.push_back(new ParallelThread(f7));
880
3/8
✓ Branch 1 taken 544 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 544 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 544 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
544 threads.push_back(new ParallelThread(f8));
881
1/2
✓ Branch 1 taken 544 times.
✗ Branch 2 not taken.
544 Process::run_threads(threads);
882 544 }
883 544 }
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