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