forked from libigl/libigl
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a separate function to control the default number of threads. (li…
…bigl#1684) The default number of threads for parallel for loops can be controlled explicitly by the user. Either by calling igl::default_num_thread with a user-defined thread count before entering any other libigl function, or by setting the environment variable IGL_NUM_THREADS.
- Loading branch information
Showing
3 changed files
with
119 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// This file is part of libigl, a simple c++ geometry processing library. | ||
// | ||
// Copyright (C) 2021 Jérémie Dumas <[email protected]> | ||
// | ||
// This Source Code Form is subject to the terms of the Mozilla Public License | ||
// v. 2.0. If a copy of the MPL was not distributed with this file, You can | ||
// obtain one at http://mozilla.org/MPL/2.0/. | ||
|
||
#include "default_num_threads.h" | ||
|
||
#include <cstdlib> | ||
#include <thread> | ||
|
||
IGL_INLINE unsigned int igl::default_num_threads(unsigned int user_num_threads) { | ||
// Thread-safe initialization using Meyers' singleton | ||
class MySingleton { | ||
public: | ||
static MySingleton &instance(unsigned int force_num_threads) { | ||
static MySingleton instance(force_num_threads); | ||
return instance; | ||
} | ||
|
||
unsigned int get_num_threads() const { return m_num_threads; } | ||
|
||
private: | ||
MySingleton(unsigned int force_num_threads) { | ||
// User-defined default | ||
if (force_num_threads) { | ||
m_num_threads = force_num_threads; | ||
return; | ||
} | ||
// Set from env var | ||
if (char *env_str = getenv("IGL_NUM_THREADS")) { | ||
const int env_num_thread = atoi(env_str); | ||
if (env_num_thread > 0) { | ||
m_num_threads = static_cast<unsigned int>(env_num_thread); | ||
return; | ||
} | ||
} | ||
// Guess from hardware | ||
const unsigned int hw_num_threads = std::thread::hardware_concurrency(); | ||
if (hw_num_threads) { | ||
m_num_threads = hw_num_threads; | ||
return; | ||
} | ||
// Fallback when std::thread::hardware_concurrency doesn't work | ||
m_num_threads = 8u; | ||
} | ||
|
||
unsigned int m_num_threads = 0; | ||
}; | ||
|
||
return MySingleton::instance(user_num_threads).get_num_threads(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// This file is part of libigl, a simple c++ geometry processing library. | ||
// | ||
// Copyright (C) 2021 Jérémie Dumas <[email protected]> | ||
// | ||
// This Source Code Form is subject to the terms of the Mozilla Public License | ||
// v. 2.0. If a copy of the MPL was not distributed with this file, You can | ||
// obtain one at http://mozilla.org/MPL/2.0/. | ||
#ifndef IGL_DEFAULT_NUM_THREADS_H | ||
#define IGL_DEFAULT_NUM_THREADS_H | ||
#include "igl_inline.h" | ||
|
||
namespace igl | ||
{ | ||
|
||
/// | ||
/// Returns the default number of threads used in libigl. The value returned by the first call to | ||
/// this function is cached. The following strategy is used to determine the default number of | ||
/// threads: | ||
/// 1. User-provided argument force_num_threads if != 0. | ||
/// 2. Environment variable IGL_NUM_THREADS if > 0. | ||
/// 3. Hardware concurrency if != 0. | ||
/// 4. A fallback value of 8 is used otherwise. | ||
/// | ||
/// @note It is safe to call this method from multiple threads. | ||
/// | ||
/// @param[in] force_num_threads User-provided default value. | ||
/// | ||
/// @return Default number of threads. | ||
/// | ||
IGL_INLINE unsigned int default_num_threads(unsigned int force_num_threads = 0); | ||
|
||
} // namespace igl | ||
|
||
#ifndef IGL_STATIC_LIBRARY | ||
#include "default_num_threads.cpp" | ||
#endif | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,9 @@ | ||
// This file is part of libigl, a simple c++ geometry processing library. | ||
// | ||
// | ||
// Copyright (C) 2016 Alec Jacobson <[email protected]> | ||
// | ||
// This Source Code Form is subject to the terms of the Mozilla Public License | ||
// v. 2.0. If a copy of the MPL was not distributed with this file, You can | ||
// | ||
// This Source Code Form is subject to the terms of the Mozilla Public License | ||
// v. 2.0. If a copy of the MPL was not distributed with this file, You can | ||
// obtain one at http://mozilla.org/MPL/2.0/. | ||
#ifndef IGL_PARALLEL_FOR_H | ||
#define IGL_PARALLEL_FOR_H | ||
|
@@ -19,7 +19,7 @@ namespace igl | |
// for loop. If the inner block of a for-loop can be rewritten/encapsulated in | ||
// a single (anonymous/lambda) function call `func` so that the serial code | ||
// looks like: | ||
// | ||
// | ||
// for(int i = 0;i<loop_size;i++) | ||
// { | ||
// func(i); | ||
|
@@ -38,13 +38,13 @@ namespace igl | |
// Returns true iff thread pool was invoked | ||
template<typename Index, typename FunctionType > | ||
inline bool parallel_for( | ||
const Index loop_size, | ||
const Index loop_size, | ||
const FunctionType & func, | ||
const size_t min_parallel=0); | ||
// PARALLEL_FOR Functional implementation of an open-mp style, parallel for | ||
// loop with accumulation. For example, serial code separated into n chunks | ||
// (each to be parallelized with a thread) might look like: | ||
// | ||
// | ||
// Eigen::VectorXd S; | ||
// const auto & prep_func = [&S](int n){ S = Eigen:VectorXd::Zero(n); }; | ||
// const auto & func = [&X,&S](int i, int t){ S(t) += X(i); }; | ||
|
@@ -59,13 +59,13 @@ namespace igl | |
// { | ||
// accum_func(t); | ||
// } | ||
// | ||
// | ||
// Inputs: | ||
// loop_size number of iterations. I.e. for(int i = 0;i<loop_size;i++) ... | ||
// prep_func function handle taking n >= number of threads as only | ||
// argument | ||
// argument | ||
// func function handle taking iteration index i and thread id t as only | ||
// arguments to compute inner block of for loop I.e. | ||
// arguments to compute inner block of for loop I.e. | ||
// for(int i ...){ func(i,t); } | ||
// accum_func function handle taking thread index as only argument, to be | ||
// called after all calls of func, e.g., for serial accumulation across | ||
|
@@ -74,13 +74,13 @@ namespace igl | |
// thread pooling should be attempted {0} | ||
// Returns true iff thread pool was invoked | ||
template< | ||
typename Index, | ||
typename PrepFunctionType, | ||
typename FunctionType, | ||
typename AccumFunctionType | ||
typename Index, | ||
typename PrepFunctionType, | ||
typename FunctionType, | ||
typename AccumFunctionType | ||
> | ||
inline bool parallel_for( | ||
const Index loop_size, | ||
const Index loop_size, | ||
const PrepFunctionType & prep_func, | ||
const FunctionType & func, | ||
const AccumFunctionType & accum_func, | ||
|
@@ -89,6 +89,8 @@ namespace igl | |
|
||
// Implementation | ||
|
||
#include "default_num_threads.h" | ||
|
||
#include <cmath> | ||
#include <cassert> | ||
#include <thread> | ||
|
@@ -97,7 +99,7 @@ namespace igl | |
|
||
template<typename Index, typename FunctionType > | ||
inline bool igl::parallel_for( | ||
const Index loop_size, | ||
const Index loop_size, | ||
const FunctionType & func, | ||
const size_t min_parallel) | ||
{ | ||
|
@@ -110,12 +112,12 @@ inline bool igl::parallel_for( | |
} | ||
|
||
template< | ||
typename Index, | ||
typename Index, | ||
typename PreFunctionType, | ||
typename FunctionType, | ||
typename FunctionType, | ||
typename AccumFunctionType> | ||
inline bool igl::parallel_for( | ||
const Index loop_size, | ||
const Index loop_size, | ||
const PreFunctionType & prep_func, | ||
const FunctionType & func, | ||
const AccumFunctionType & accum_func, | ||
|
@@ -125,14 +127,12 @@ inline bool igl::parallel_for( | |
if(loop_size==0) return false; | ||
// Estimate number of threads in the pool | ||
// http://ideone.com/Z7zldb | ||
const static size_t sthc = std::thread::hardware_concurrency(); | ||
const size_t nthreads = | ||
#ifdef IGL_PARALLEL_FOR_FORCE_SERIAL | ||
0; | ||
const size_t nthreads = 1; | ||
#else | ||
loop_size<min_parallel?0:(sthc==0?8:sthc); | ||
const size_t nthreads = igl::default_num_threads(); | ||
#endif | ||
if(nthreads==0) | ||
if(loop_size<min_parallel || nthreads<=1) | ||
{ | ||
// serial | ||
prep_func(1); | ||
|
@@ -142,10 +142,10 @@ inline bool igl::parallel_for( | |
}else | ||
{ | ||
// Size of a slice for the range functions | ||
Index slice = | ||
Index slice = | ||
std::max( | ||
(Index)std::round((loop_size+1)/static_cast<double>(nthreads)),(Index)1); | ||
|
||
// [Helper] Inner loop | ||
const auto & range = [&func](const Index k1, const Index k2, const size_t t) | ||
{ | ||
|
@@ -181,7 +181,7 @@ inline bool igl::parallel_for( | |
return true; | ||
} | ||
} | ||
|
||
//#ifndef IGL_STATIC_LIBRARY | ||
//#include "parallel_for.cpp" | ||
//#endif | ||
|