Tulip  5.2.1
Large graphs analysis and drawing
ParallelTools.h
1 /*
2  *
3  * This file is part of Tulip (http://tulip.labri.fr)
4  *
5  * Authors: David Auber and the Tulip development Team
6  * from LaBRI, University of Bordeaux
7  *
8  * Tulip is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation, either version 3
11  * of the License, or (at your option) any later version.
12  *
13  * Tulip is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16  * See the GNU General Public License for more details.
17  *
18  */
19 
20 #ifndef TLP_PARALLEL_TOOLS_H
21 #define TLP_PARALLEL_TOOLS_H
22 
23 #include <tulip/tulipconf.h>
24 #include <vector>
25 
26 #ifndef TLP_NO_THREADS
27 
28 #ifdef _OPENMP
29 // _OPENMP is supposed to be defined as an integer
30 // representing the year/month of the supported version
31 #if _OPENMP < 200805
32 // only signed integer types are supported
33 // for OpenMP < 3.0
34 typedef long long OMP_ITER_TYPE;
35 #else
36 typedef size_t OMP_ITER_TYPE;
37 #endif
38 #ifndef _MSC_VER
39 #define OMP(x) _Pragma(STRINGIFY(omp x))
40 #define OMP_CRITICAL_SECTION(x) _Pragma(STRINGIFY(omp critical(x)))
41 #else
42 #define OMP(x) __pragma(omp x)
43 #define OMP_CRITICAL_SECTION(x) __pragma(omp critical(x))
44 #endif
45 
46 #else
47 
48 // OPENMP no available use c++11 threads
49 #include <iostream>
50 #include <algorithm>
51 #include <mutex>
52 #include <thread>
53 
54 #endif
55 
56 #endif
57 
58 namespace tlp {
59 
60 class TlpThread;
61 // ===================================================================================
62 
63 /**
64  * @brief Static wrapper class around the std::thread
65  *
66  * @since Tulip 5.2
67  */
68 class TLP_SCOPE ThreadManager {
69 
70  static unsigned int maxNumberOfThreads;
71 
72 #if !defined(TLP_NO_THREADS) && !defined(_OPENMP)
73 
74  // allocate a number for the calling thread
75  static void allocateThreadNumber();
76 
77  // deallocate the number of the calling thread
78  static void freeThreadNumber();
79 
80  // create a thread dedicated to the execution of a function
81  // used to iterate between begin and end indices
82  template <typename runFunction>
83  static inline std::thread *launchThread(const runFunction &f, uint begin, uint end) {
84  auto thrdFunction = [&](uint begin, uint end) {
85  allocateThreadNumber();
86  f(begin, end);
87  freeThreadNumber();
88  };
89  return new std::thread(thrdFunction, begin, end);
90  }
91 
92 #endif
93 
94 public:
95 #if !defined(TLP_NO_THREADS) && !defined(_OPENMP)
96 
97  // create a thread dedicated to the execution of a function
98  // with no arguments
99  template <typename runFunction>
100  static inline std::thread *launchThread(const runFunction &f) {
101  auto thrdFunction = [&]() {
102  allocateThreadNumber();
103  f();
104  freeThreadNumber();
105  };
106  return new std::thread(thrdFunction);
107  }
108 
109 #endif
110 
111  /**
112  * Returns the number of processors on the host system.
113  */
114  static unsigned int getNumberOfProcs();
115 
116  /**
117  * Returns the number of threads used by default in subsequent OpenMP parallel sections.
118  */
119  static unsigned int getNumberOfThreads();
120 
121  /**
122  * Sets the number of threads used by default in subsequent parallel sections.
123  */
124  static void setNumberOfThreads(unsigned int nbThreads);
125 
126  /**
127  * Returns the current thread number.
128  */
129  static unsigned int getThreadNumber();
130 
131 #ifndef _OPENMP
132 
133  /**
134  * Parallel iteration of the same function over partitioned ranges
135  * between 0 and maxId
136  */
137  template <typename ThreadFunction>
138  static inline void iterate(size_t maxId, const ThreadFunction &threadFunction) {
139 #ifndef TLP_NO_THREADS
140  if (maxId == 0)
141  return;
142 
143  // compute the size of range indices for each thread
144  size_t nbPerThread = maxId == 1 ? 1 : std::max(maxId / (maxNumberOfThreads - 1), size_t(2));
145  // begin and end indices of thread partition
146  size_t begin = 0, end = nbPerThread;
147  maxId -= end - begin;
148  // create threads
149  std::vector<std::thread *> threads;
150  threads.reserve(maxNumberOfThreads - 1);
151  while (maxId) {
152  threads.push_back(launchThread(threadFunction, begin, end));
153  if (nbPerThread > 1) {
154  if (maxNumberOfThreads - threads.size() == maxId)
155  nbPerThread = 1;
156  }
157  begin = end;
158  end += std::min(nbPerThread, maxId);
159  maxId -= end - begin;
160  }
161  // iterate on last partition
162  threadFunction(begin, end);
163  // wait for threads
164  for (auto thrd : threads) {
165  thrd->join();
166  delete thrd;
167  }
168 #else
169  threadFunction(0, maxId);
170 #endif
171  }
172 
173 #endif
174 };
175 
176 #ifndef TLP_NO_THREADS
177 
178 #define TLP_MAX_NB_THREADS 128
179 
180 #define TLP_NB_THREADS tlp::ThreadManager::getNumberOfThreads()
181 
182 #ifdef _OPENMP
183 
184 #define TLP_LOCK_SECTION(mtx) OMP_CRITICAL_SECTION(mtx)
185 #define TLP_UNLOCK_SECTION(mtx)
186 #define TLP_DECLARE_GLOBAL_LOCK(mtx) extern void mtx()
187 #define TLP_DEFINE_GLOBAL_LOCK(mtx) extern void mtx()
188 #define TLP_GLOBALLY_LOCK_SECTION(mtx) OMP_CRITICAL_SECTION(mtx)
189 #define TLP_GLOBALLY_UNLOCK_SECTION(mtx)
190 
191 #else
192 
193 #define TLP_LOCK_SECTION(mtx) \
194  static std::mutex mtx; \
195  mtx.lock();
196 #define TLP_UNLOCK_SECTION(mtx) mtx.unlock()
197 #define TLP_DECLARE_GLOBAL_LOCK(mtx) extern std::mutex mtx
198 #define TLP_DEFINE_GLOBAL_LOCK(mtx) std::mutex mtx
199 #define TLP_GLOBALLY_LOCK_SECTION(mtx) mtx.lock();
200 #define TLP_GLOBALLY_UNLOCK_SECTION(mtx) mtx.unlock()
201 
202 #endif
203 
204 #else
205 
206 // no multi-threading
207 #define TLP_MAX_NB_THREADS 1
208 
209 #define TLP_NB_THREADS 1
210 
211 #define TLP_LOCK_SECTION(mtx)
212 #define TLP_UNLOCK_SECTION(mtx)
213 #define TLP_DECLARE_GLOBAL_LOCK(mtx) extern void mtx()
214 #define TLP_DEFINE_GLOBAL_LOCK(mtx) extern void mtx()
215 #define TLP_GLOBALLY_LOCK_SECTION(mtx)
216 #define TLP_GLOBALLY_UNLOCK_SECTION(mtx)
217 
218 #endif
219 
220 // ===================================================================================
221 
222 /**
223  * Template function to ease the creation of parallel threads taking
224  * an index as parameter (0 <= index < maxIdx).
225  *
226  * @since Tulip 5.2
227  *
228  * @param maxIdx the upper bound exclusive of the indices range
229  * @param idxFunction callable object (e.g. lambda function) taking an unsigned integer as parameter
230  *
231  * Example of use:
232  *
233  * @code
234  * auto computationIntensiveTask = [&](unsigned int id) {
235  * double result = 0;
236  * ...
237  * return result;
238  * };
239  * const unsigned int N = ... ;
240  * std::vector<double> result(N);
241  * TLP_PARALLEL_MAP_INDICES(N, [&](unsigned int i) {
242  * // run task in a thread
243  * result[i] = computationIntensiveTask(i);
244  * });
245  * @endcode
246  */
247 template <typename IdxFunction>
248 void inline TLP_PARALLEL_MAP_INDICES(size_t maxIdx, const IdxFunction &idxFunction) {
249 #ifdef _OPENMP
250  OMP(parallel for)
251  for (OMP_ITER_TYPE i = 0; i < OMP_ITER_TYPE(maxIdx); ++i) {
252  idxFunction(i);
253  }
254 #else
255  auto threadFunction = [&](size_t begin, size_t end) {
256  for (; begin < end; ++begin) {
257  idxFunction(begin);
258  }
259  };
260  ThreadManager::iterate(maxIdx, threadFunction);
261 #endif
262 }
263 
264 template <typename EltType, typename IdxFunction>
265 void inline TLP_PARALLEL_MAP_VECTOR(const std::vector<EltType> &vect,
266  const IdxFunction &idxFunction) {
267 #ifdef _OPENMP
268  auto maxIdx = vect.size();
269  OMP(parallel for)
270  for (OMP_ITER_TYPE i = 0; i < OMP_ITER_TYPE(maxIdx); ++i) {
271  idxFunction(vect[i]);
272  }
273 #else
274  auto threadFunction = [&](size_t begin, size_t end) {
275  for (; begin < end; ++begin) {
276  idxFunction(vect[begin]);
277  }
278  };
279 
280  ThreadManager::iterate(vect.size(), threadFunction);
281 #endif
282 }
283 
284 template <typename EltType, typename IdxFunction>
285 void inline TLP_PARALLEL_MAP_VECTOR_AND_INDICES(const std::vector<EltType> &vect,
286  const IdxFunction &idxFunction) {
287 #ifdef _OPENMP
288  auto maxIdx = vect.size();
289  OMP(parallel for)
290  for (OMP_ITER_TYPE i = 0; i < OMP_ITER_TYPE(maxIdx); ++i) {
291  idxFunction(vect[i], i);
292  }
293 #else
294  auto threadFunction = [&](size_t begin, size_t end) {
295  for (; begin < end; ++begin) {
296  idxFunction(vect[begin], begin);
297  }
298  };
299 
300  ThreadManager::iterate(vect.size(), threadFunction);
301 #endif
302 }
303 
304 template <typename F1, typename F2>
305 void inline TLP_PARALLEL_SECTIONS(const F1 &f1, const F2 &f2) {
306 #ifndef TLP_NO_THREADS
307 #ifdef _OPENMP
308  OMP(parallel) {
309  OMP(sections nowait) {
310  OMP(section) {
311  f1();
312  }
313  }
314  OMP(master) {
315  f2();
316  }
317  }
318 #else
319  auto thrd = ThreadManager::launchThread(f1);
320  f2();
321  thrd->join();
322  delete thrd;
323 #endif
324 #else
325  f1();
326  f2();
327 #endif
328 }
329 
330 template <typename F1, typename F2, typename F3>
331 void inline TLP_PARALLEL_SECTIONS(const F1 &f1, const F2 &f2, const F3 &f3) {
332 #ifndef TLP_NO_THREADS
333 #ifdef _OPENMP
334  OMP(parallel) {
335  OMP(sections nowait) {
336  OMP(section) {
337  f1();
338  }
339  OMP(section) {
340  f2();
341  }
342  }
343  OMP(master) {
344  f3();
345  }
346  }
347 #else
348  auto thrd1 = ThreadManager::launchThread(f1);
349  auto thrd2 = ThreadManager::launchThread(f2);
350  f3();
351  thrd1->join();
352  thrd2->join();
353  delete thrd1;
354  delete thrd2;
355 #endif
356 #else
357  f1();
358  f2();
359  f3();
360 #endif
361 }
362 
363 template <typename F1, typename F2, typename F3, typename F4>
364 void inline TLP_PARALLEL_SECTIONS(const F1 &f1, const F2 &f2, const F3 &f3, const F4 &f4) {
365 #ifndef TLP_NO_THREADS
366 #ifdef _OPENMP
367  OMP(parallel) {
368  OMP(sections nowait) {
369  OMP(section) {
370  f1();
371  }
372  OMP(section) {
373  f2();
374  }
375  OMP(section) {
376  f3();
377  }
378  }
379  OMP(master) {
380  f4();
381  }
382  }
383 #else
384  auto thrd1 = ThreadManager::launchThread(f1);
385  auto thrd2 = ThreadManager::launchThread(f2);
386  auto thrd3 = ThreadManager::launchThread(f3);
387  f4();
388  thrd1->join();
389  thrd2->join();
390  thrd3->join();
391  delete thrd1;
392  delete thrd2;
393  delete thrd3;
394 #endif
395 #else
396  f1();
397  f2();
398  f3();
399  f4();
400 #endif
401 }
402 } // namespace tlp
403 
404 #endif // TLP_PARALLEL_TOOLS_H
static void iterate(size_t maxId, const ThreadFunction &threadFunction)
void TLP_PARALLEL_MAP_INDICES(size_t maxIdx, const IdxFunction &idxFunction)
Static wrapper class around the std::thread.
Definition: ParallelTools.h:68