1 /* --------------------------------------------------------------------------
2 CppAD: C++ Algorithmic Differentiation: Copyright (C) 2003-20 Bradley M. Bell
3 
4 CppAD is distributed under the terms of the
5              Eclipse Public License Version 2.0.
6 
7 This Source Code may also be made available under the following
8 Secondary License when the conditions for such availability set forth
9 in the Eclipse Public License, Version 2.0 are satisfied:
10       GNU General Public License, Version 2.0 or later.
11 ---------------------------------------------------------------------------- */
12 
13 /*
14 $begin simple_ad_bthread.cpp$$
15 $spell
16     bthread
17     CppAD
18 $$
19 
20 $section A Simple Boost Threading AD: Example and Test$$
21 
22 
23 $head Purpose$$
24 This example demonstrates how CppAD can be used in a
25 boost multi-threading environment.
26 
27 $head Source Code$$
28 $srcthisfile%0%// BEGIN C++%// END C++%1%$$
29 
30 $end
31 ------------------------------------------------------------------------------
32 */
33 // BEGIN C++
34 # include <cppad/cppad.hpp>
35 # include <boost/thread.hpp>
36 # define NUMBER_THREADS  4
37 
38 namespace {
39     // structure with problem specific information
40     typedef struct {
41         // function argument (worker input)
42         double          x;
43         // This structure would also have return information in it,
44         // but this example only returns the ok flag
45     } problem_specific;
46     // =====================================================================
47     // General purpose code you can copy to your application
48     // =====================================================================
49     using CppAD::thread_alloc;
50     // ------------------------------------------------------------------
51     // thread specific point to the thread number (initialize as null)
cleanup(size_t *)52     void cleanup(size_t*)
53     {   return; }
54     boost::thread_specific_ptr<size_t> thread_num_ptr_(cleanup);
55 
56     // Are we in sequential mode; i.e., other threads are waiting for
57     // master thread to set up next job ?
58     bool sequential_execution_ = true;
59 
60     // used to inform CppAD when we are in parallel execution mode
in_parallel(void)61     bool in_parallel(void)
62     {   return ! sequential_execution_; }
63 
64     // used to inform CppAD of current thread number thread_number()
thread_number(void)65     size_t thread_number(void)
66     {   // return thread_all_[thread_num].thread_num
67         return *thread_num_ptr_.get();
68     }
69     // ---------------------------------------------------------------------
70     // structure with information for one thread
71     typedef struct {
72         // number for this thread (thread specific points here)
73         size_t            thread_num;
74         // pointer to this boost thread
75         boost::thread*    bthread;
76         // false if an error occurs, true otherwise
77         bool              ok;
78         // pointer to problem specific information
79         problem_specific* info;
80     } thread_one_t;
81     // vector with information for all threads
82     thread_one_t thread_all_[NUMBER_THREADS];
83     // --------------------------------------------------------------------
84     // function that initializes the thread and then calls actual worker
85     bool worker(size_t thread_num, problem_specific* info);
run_one_worker(size_t thread_num)86     void run_one_worker(size_t thread_num)
87     {   bool ok = true;
88 
89         // The master thread should call worker directly
90         ok &= thread_num != 0;
91 
92         // This is not the master thread, so thread specific infromation
93         // has not yet been set. We use it to inform other routines
94         // of this threads number.
95         // We must do this before calling thread_alloc::thread_num().
96         thread_num_ptr_.reset(& thread_all_[thread_num].thread_num);
97 
98         // Check the value of thread_alloc::thread_num().
99         ok = thread_num == thread_alloc::thread_num();
100 
101         // Now do the work
102         ok &= worker(thread_num, thread_all_[thread_num].info);
103 
104         // pass back ok information for this thread
105         thread_all_[thread_num].ok = ok;
106 
107         // no return value
108         return;
109     }
110     // ----------------------------------------------------------------------
111     // function that calls all the workers
run_all_workers(size_t num_threads,problem_specific * info_all[])112     bool run_all_workers(size_t num_threads, problem_specific* info_all[])
113     {   bool ok = true;
114 
115         // initialize thread_all_ (execpt for pthread_id)
116         size_t thread_num;
117         for(thread_num = 0; thread_num < num_threads; thread_num++)
118         {   // pointed to by thread specific info for this thread
119             thread_all_[thread_num].thread_num = thread_num;
120             // initialize as false to make sure worker gets called by other
121             // threads. Note that thread_all_[0].ok does not get used
122             thread_all_[thread_num].ok         = false;
123             // problem specific information
124             thread_all_[thread_num].info       = info_all[thread_num];
125         }
126 
127         // master bthread number
128         thread_num_ptr_.reset(& thread_all_[0].thread_num);
129 
130         // Now thread_number() has necessary information for this thread
131         // (number zero), and while still in sequential mode,
132         // call setup for using CppAD::AD<double> in parallel mode.
133         thread_alloc::parallel_setup(
134             num_threads, in_parallel, thread_number
135         );
136         thread_alloc::hold_memory(true);
137         CppAD::parallel_ad<double>();
138 
139         // inform CppAD that we now may be in parallel execution mode
140         sequential_execution_ = false;
141 
142         // This master thread is already running, we need to create
143         // num_threads - 1 more threads
144         thread_all_[0].bthread = nullptr;
145         for(thread_num = 1; thread_num < num_threads; thread_num++)
146         {   // Create the thread with thread number equal to thread_num
147             thread_all_[thread_num].bthread =
148                 new boost::thread(run_one_worker, thread_num);
149         }
150 
151         // now call worker for the master thread
152         thread_num = thread_alloc::thread_num();
153         ok &= thread_num == 0;
154         ok &= worker(thread_num, thread_all_[thread_num].info);
155 
156         // now wait for the other threads to finish
157         for(thread_num = 1; thread_num < num_threads; thread_num++)
158         {   thread_all_[thread_num].bthread->join();
159             delete thread_all_[thread_num].bthread;
160             thread_all_[thread_num].bthread = nullptr;
161         }
162 
163         // Inform CppAD that we now are definately back to sequential mode
164         sequential_execution_ = true;
165 
166         // now inform CppAD that there is only one thread
167         thread_alloc::parallel_setup(1, nullptr, nullptr);
168         thread_alloc::hold_memory(false);
169         CppAD::parallel_ad<double>();
170 
171         // check to ok flag returned by during calls to work by other threads
172         for(thread_num = 1; thread_num < num_threads; thread_num++)
173             ok &= thread_all_[thread_num].ok;
174 
175         return ok;
176     }
177     // =====================================================================
178     // End of General purpose code
179     // =====================================================================
180     // function that does the work for one thread
worker(size_t thread_num,problem_specific * info)181     bool worker(size_t thread_num, problem_specific* info)
182     {   bool ok = true;
183 
184         // CppAD::vector uses the CppAD fast multi-threading allocator
185         CppAD::vector< CppAD::AD<double> > ax(1), ay(1);
186         ax[0] = info->x;
187         Independent(ax);
188         ay[0] = sqrt( ax[0] * ax[0] );
189         CppAD::ADFun<double> f(ax, ay);
190 
191         // Check function value corresponds to the identity
192         double eps = 10. * CppAD::numeric_limits<double>::epsilon();
193         ok        &= CppAD::NearEqual(ay[0], ax[0], eps, eps);
194 
195         // Check derivative value corresponds to the identity.
196         CppAD::vector<double> d_x(1), d_y(1);
197         d_x[0] = 1.;
198         d_y    = f.Forward(1, d_x);
199         ok    &= CppAD::NearEqual(d_x[0], 1., eps, eps);
200 
201         return ok;
202     }
203 }
simple_ad(void)204 bool simple_ad(void)
205 {   bool ok = true;
206     size_t num_threads = NUMBER_THREADS;
207 
208     // Check that no memory is in use or avialable at start
209     // (using thread_alloc in sequential mode)
210     size_t thread_num;
211     for(thread_num = 0; thread_num < num_threads; thread_num++)
212     {   ok &= thread_alloc::inuse(thread_num) == 0;
213         ok &= thread_alloc::available(thread_num) == 0;
214     }
215 
216     // initialize info_all
217     problem_specific *info, *info_all[NUMBER_THREADS];
218     for(thread_num = 0; thread_num < num_threads; thread_num++)
219     {   // problem specific information
220         size_t min_bytes(sizeof(info)), cap_bytes;
221         void*  v_ptr = thread_alloc::get_memory(min_bytes, cap_bytes);
222         info         = static_cast<problem_specific*>(v_ptr);
223         info->x      = double(thread_num) + 1.;
224         info_all[thread_num] = info;
225     }
226 
227     ok &= run_all_workers(num_threads, info_all);
228 
229     // go down so that free memory for other threads before memory for master
230     thread_num = num_threads;
231     while(thread_num--)
232     {   // delete problem specific information
233         void* v_ptr = static_cast<void*>( info_all[thread_num] );
234         thread_alloc::return_memory( v_ptr );
235         // check that there is no longer any memory inuse by this thread
236         ok &= thread_alloc::inuse(thread_num) == 0;
237         // return all memory being held for future use by this thread
238         thread_alloc::free_available(thread_num);
239     }
240 
241     return ok;
242 }
243 // END C++
244