2020// / \brief Example showing the usage of tasking
2121
2222#include " PTL/PTL.hh"
23+ #include " PTL/ThreadPool.hh"
24+ #include " PTL/Threading.hh"
2325
2426#include < chrono>
2527#include < condition_variable>
2628#include < iostream>
29+ #include < memory>
2730#include < mutex>
2831#include < random>
32+ #include < stdexcept>
2933#include < thread>
3034#include < vector>
3135
@@ -34,7 +38,7 @@ using lock_t = std::unique_lock<mutex_t>;
3438
3539using namespace PTL ;
3640
37- static std::mt19937 rng;
41+ static thread_local std::mt19937 rng{} ;
3842
3943// ============================================================================//
4044
7579random_entry (const std::vector<Tp>& v)
7680{
7781 std::uniform_int_distribution<std::mt19937::result_type> dist (0 , v.size () - 1 );
78- AutoLock lk{ TypeMutex<decltype (rng)>() };
7982 return v.at (dist (rng));
8083}
8184
8588main (int argc, char ** argv)
8689{
8790 ConsumeParameters (argc, argv);
88- rng.seed (std::random_device () ());
91+ rng.seed (std::random_device{} ());
8992 Threading::SetThreadId (0 );
9093 Backtrace::Enable ();
9194
@@ -97,20 +100,74 @@ main(int argc, char** argv)
97100 if (nthreads == 0 )
98101 nthreads = 1 ;
99102
100- std::cout << " [" << argv[ 0 ] << " ]> "
103+ std::cout << " [ptl-minimal ]> "
101104 << " Number of threads: " << nthreads << std::endl;
102105 Timer total_timer;
103106 total_timer.Start ();
104107
105108 // Construct the default run manager
106- bool use_tbb = GetEnv (" PTL_USE_TBB" , false );
107- auto run_manager = TaskRunManager (use_tbb);
108- run_manager.Initialize (nthreads);
109+ bool _use_tbb = GetEnv (" PTL_USE_TBB" , false );
110+ bool _pin = GetEnv (" PTL_PIN_THREADS" , true );
111+
112+ std::atomic<unsigned > ninit{ 0 };
113+ std::atomic<unsigned > nfini{ 0 };
114+ auto _init = [&ninit]() {
115+ rng.seed (std::random_device{}());
116+ ++ninit;
117+ AutoLock _lk{ TypeMutex<decltype (std::cout)>() };
118+ printf (" [ptl-minimal]> Thread %2i started\n " , Threading::GetThreadId ());
119+ };
120+ auto _fini = [&nfini]() {
121+ ++nfini;
122+ AutoLock _lk{ TypeMutex<decltype (std::cout)>() };
123+ printf (" [ptl-minimal]> Thread %2i finished\n " , Threading::GetThreadId ());
124+ };
125+
126+ PTL ::ThreadPool::Config _config{};
127+ _config.use_tbb = _use_tbb;
128+ _config.use_affinity = _pin;
129+ _config.verbose = 3 ;
130+ _config.pool_size = nthreads;
131+ _config.initializer = _init;
132+ _config.finalizer = _fini;
133+ _config.set_affinity = [](intmax_t i) {
134+ auto _orig = ThreadPool::affinity_functor ()(i);
135+ static intmax_t idx = 0 ;
136+ static intmax_t ncores = Threading::GetNumberOfCores ();
137+ static intmax_t ncpus = Threading::GetNumberOfPhysicalCpus ();
138+ static intmax_t nincr = std::max<intmax_t >(ncores / ncpus, 1 );
139+ auto _idx = idx + nincr;
140+ idx += nincr;
141+ if (_idx % ncores == 0 )
142+ idx++;
143+ auto _v = (_idx - nincr) % ncores;
144+ AutoLock _lk{ TypeMutex<decltype (std::cout)>() };
145+ printf (" [ptl-minimal]> Thread %2i was pinned to CPU %2i instead of %2i...\n " ,
146+ (int ) i, (int ) _v, (int ) _orig);
147+ return _v;
148+ };
149+
150+ auto tp = std::unique_ptr<PTL ::ThreadPool>{ new PTL ::ThreadPool{ _config } };
151+
152+ if (!tp->is_initialized ())
153+ throw std::runtime_error (" ThreadPool is not initialized" );
154+ if (!_use_tbb && !tp->is_alive ())
155+ throw std::runtime_error (" ThreadPool is not alive" );
156+ if (_use_tbb && tp->is_alive ())
157+ throw std::runtime_error (" ThreadPool is alive and shouldn't be" );
158+ if (_use_tbb && !tp->is_tbb_threadpool ())
159+ throw std::runtime_error (" ThreadPool is not a TBB threadpool (should be)" );
160+ if (!_use_tbb && tp->is_tbb_threadpool ())
161+ throw std::runtime_error (" ThreadPool is a TBB threadpool (should not be)" );
162+ if (!tp->is_main ())
163+ throw std::runtime_error (" ThreadPool does not think it is on main thread" );
109164
110165 // the TaskManager is a utility that wraps the function calls into tasks for the
111- // ThreadPool
112- TaskManager* task_manager = run_manager.GetTaskManager ();
113- auto * tp = task_manager->thread_pool ();
166+ // ThreadPool and helps manage the lifetime of the threadpool (it will stop the
167+ // threads in the thread pool when it's destructor is called unless false
168+ // is passed as second parameter)
169+ TaskManager task_manager{ tp.get (), false };
170+
114171 std::set<std::thread::id> tids{};
115172
116173 // ------------------------------------------------------------------------//
@@ -123,7 +180,8 @@ main(int argc, char** argv)
123180 tp->execute_on_all_threads ([&tids, &_all_exec]() {
124181 ++_all_exec;
125182 std::stringstream ss;
126- ss << " thread " << std::setw (4 ) << PTL::Threading::GetThreadId () << " executed\n " ;
183+ ss << " [ptl-minimal]> Thread " << std::setw (2 ) << PTL::Threading::GetThreadId ()
184+ << " executed\n " ;
127185 AutoLock lk{ TypeMutex<decltype (std::cout)>() };
128186 std::cout << ss.str ();
129187 tids.insert (std::this_thread::get_id ());
@@ -136,7 +194,8 @@ main(int argc, char** argv)
136194 std::to_string (_all_exec.load ()) + " vs. " + std::to_string (_size));
137195 else
138196 {
139- printf (" Successful execution on every thread: %i\n " , (int ) _all_exec);
197+ printf (" [ptl-minimal]> Successful execution on every thread: %i\n " ,
198+ (int ) _all_exec);
140199 }
141200
142201 // ------------------------------------------------------------------------//
@@ -155,7 +214,8 @@ main(int argc, char** argv)
155214 tp->execute_on_specific_threads (tids, [&_specific_exec]() {
156215 ++_specific_exec;
157216 std::stringstream ss;
158- ss << " thread " << std::setw (4 ) << PTL::Threading::GetThreadId () << " executed [specific]\n " ;
217+ ss << " [ptl-minimal]> Thread " << std::setw (2 ) << PTL::Threading::GetThreadId ()
218+ << " executed [specific]\n " ;
159219 AutoLock lk{ TypeMutex<decltype (std::cout)>() };
160220 std::cout << ss.str ();
161221 });
@@ -166,7 +226,8 @@ main(int argc, char** argv)
166226 std::to_string (_specific_exec.load ()) + " vs. " + std::to_string (_target_sz));
167227 else
168228 {
169- printf (" Successful execution on subset of threads: %i\n " , (int ) _specific_exec);
229+ printf (" [ptl-minimal]> Successful execution on subset of threads: %i\n " ,
230+ (int ) _specific_exec);
170231 }
171232
172233 // ------------------------------------------------------------------------//
@@ -175,41 +236,40 @@ main(int argc, char** argv)
175236 // //
176237 // ------------------------------------------------------------------------//
177238 {
178- long nfib = std::max<long >(GetEnv<long >(" FIBONACCI" , 30 ), 30 );
179- long nloop = 100 ;
180- long ndiv = 4 ;
181- long npart = nloop / ndiv;
239+ long nfib = std::max<long >(GetEnv<long >(" FIBONACCI" , 30 ), 30 );
240+ long nloop = 100 ;
241+ long ndiv = 4 ;
242+ long npart = nloop / ndiv;
182243 long expected = (fibonacci (nfib + 0 ) * npart) + (fibonacci (nfib + 1 ) * npart) +
183244 (fibonacci (nfib + 2 ) * npart) + (fibonacci (nfib + 3 ) * npart);
184245 auto join = [](long & lhs, long rhs) {
185246 std::stringstream ss;
186- ss << " thread " << std::setw (4 ) << PTL::Threading::GetThreadId () << " adding "
187- << rhs << " to " << lhs << std::endl;
247+ ss << " [ptl-minimal]> Thread " << std::setw (2 )
248+ << PTL::Threading::GetThreadId () << " adding " << rhs << " to " << lhs
249+ << std::endl;
188250 {
189251 AutoLock lk{ TypeMutex<decltype (std::cout)>() };
190252 std::cout << ss.str ();
191253 }
192254 return lhs += rhs;
193255 };
194256
195- auto entry = [](uint64_t n) {
257+ auto entry = [](uint64_t n) {
196258 std::vector<double > v (n * 100 , 0 );
197259 for (auto & itr : v)
198- {
199- AutoLock lk{ TypeMutex<decltype (rng)>() };
200260 itr = std::generate_canonical<double , 12 >(rng);
201- }
202- auto e = random_entry (v);
261+ auto e = random_entry (v);
203262 std::stringstream ss;
204- ss << " [" << n << " ]> random entry from thread " << std::setw (4 )
205- << PTL::Threading::GetThreadId () << " was : " << std::setw (8 )
206- << std::setprecision (6 ) << std::fixed << e << std::endl;
263+ ss << " [ptl-minimal][" << std::setw (4 ) << n << " ]> Random entry from thread "
264+ << std::setw (2 ) << PTL::Threading::GetThreadId ()
265+ << " was : " << std::setw (8 ) << std::setprecision (6 ) << std::fixed << e
266+ << std::endl;
207267 AutoLock lk{ TypeMutex<decltype (std::cout)>() };
208268 std::cout << ss.str ();
209269 };
210270
211- TaskGroup<long > tgf ( join);
212- TaskGroup<void > tgv;
271+ TaskGroup<long > tgf{ join, tp. get () }; // uses existing thread-pool
272+ TaskGroup<void > tgv{ tp. get () }; // uses existing thread-pool
213273 for (long i = 0 ; i < nloop; ++i)
214274 {
215275 tgf.exec (fibonacci, nfib + (i % ndiv));
@@ -220,7 +280,8 @@ main(int argc, char** argv)
220280
221281 auto ret = tgf.join ();
222282 tgv.join ();
223- std::cout << " fibonacci(" << nfib << " ) * " << nloop << " = " << ret << std::endl;
283+ std::cout << " [ptl-minimal]> fibonacci(" << nfib << " ) * " << nloop << " = "
284+ << ret << std::endl;
224285 std::cout << std::endl;
225286 if (expected != ret)
226287 {
@@ -245,7 +306,7 @@ main(int argc, char** argv)
245306 _at.Start ();
246307 for (decltype (nthreads) i = 0 ; i < nthreads; ++i)
247308 {
248- auto fib_async = task_manager-> async <int64_t >(fibonacci, nfib);
309+ auto fib_async = task_manager. async <int64_t >(fibonacci, nfib);
249310 _futures.emplace_back (fib_async->get_future ());
250311 _asyncs.emplace_back (fib_async);
251312 }
@@ -258,10 +319,10 @@ main(int argc, char** argv)
258319 _at.Stop ();
259320 for (decltype (nthreads) i = 0 ; i < nthreads; ++i)
260321 {
261- std::cout << " [async test][" << i << " ] fibonacci(" << nfib
322+ std::cout << " [ptl-minimal][ async test][" << i << " ] fibonacci(" << nfib
262323 << " ) = " << _values[i] << std::endl;
263324 }
264- std::cout << " [async test] " << _at << std::endl;
325+ std::cout << " [ptl-minimal][ async test] " << _at << std::endl;
265326 std::cout << std::endl;
266327 }
267328
@@ -273,8 +334,18 @@ main(int argc, char** argv)
273334
274335 // print the time for the calculation
275336 total_timer.Stop ();
276- std::cout << " Total time: \t " << total_timer << std::endl;
337+ std::cout << " [ptl-minimal]> Total time: \t " << total_timer << std::endl;
277338
339+ tp->resize (nthreads);
278340 tp->destroy_threadpool ();
279- task_manager->finalize ();
341+ tp->resize (0 );
342+
343+ std::cout << " [ptl-minimal]> Number of threads: " << nthreads << " \n " ;
344+ std::cout << " [ptl-minimal]> Number of thread initialization: " << ninit.load ()
345+ << " \n " ;
346+ std::cout << " [ptl-minimal]> Number of thread finalizations : " << nfini.load ()
347+ << " \n " ;
348+
349+ tp.reset ();
350+ return (2 * nthreads) - ninit - nfini;
280351}
0 commit comments