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_openmp.cpp$$
15 $spell
16 openmp
17 CppAD
18 $$
19
20 $section A Simple OpenMP AD: Example and Test$$
21
22
23 $head Purpose$$
24 This example demonstrates how CppAD can be used in a
25 OpenMP 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 <omp.h>
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 // used to inform CppAD when we are in parallel execution mode
in_parallel(void)52 bool in_parallel(void)
53 { return omp_in_parallel() != 0; }
54 // ------------------------------------------------------------------
55 // used to inform CppAD of the current thread number
thread_number(void)56 size_t thread_number(void)
57 { return static_cast<size_t>( omp_get_thread_num() ); }
58 // ------------------------------------------------------------------
59 // structure with information for one thread
60 typedef struct {
61 // false if an error occurs, true otherwise (worker output)
62 bool ok;
63 } thread_one_t;
64 // vector with information for all threads
65 thread_one_t thread_all_[NUMBER_THREADS];
66 // ------------------------------------------------------------------
67 // function that calls all the workers
68 bool worker(problem_specific* info);
run_all_workers(size_t num_threads,problem_specific * info_all[])69 bool run_all_workers(size_t num_threads, problem_specific* info_all[])
70 { bool ok = true;
71
72 // initialize thread_all_
73 int thread_num, int_num_threads = int(num_threads);
74 for(thread_num = 0; thread_num < int_num_threads; thread_num++)
75 { // initialize as false to make sure gets called for all threads
76 thread_all_[thread_num].ok = false;
77 }
78
79 // turn off dynamic thread adjustment
80 omp_set_dynamic(0);
81
82 // set the number of OpenMP threads
83 omp_set_num_threads( int_num_threads );
84
85 // setup for using CppAD::AD<double> in parallel
86 thread_alloc::parallel_setup(
87 num_threads, in_parallel, thread_number
88 );
89 thread_alloc::hold_memory(true);
90 CppAD::parallel_ad<double>();
91
92 // execute worker in parallel
93 # pragma omp parallel for
94 for(thread_num = 0; thread_num < int_num_threads; thread_num++)
95 thread_all_[thread_num].ok = worker(info_all[thread_num]);
96 // end omp parallel for
97
98 // set the number of OpenMP threads to one
99 omp_set_num_threads(1);
100
101 // now inform CppAD that there is only one thread
102 thread_alloc::parallel_setup(1, nullptr, nullptr);
103 thread_alloc::hold_memory(false);
104 CppAD::parallel_ad<double>();
105
106 // check to ok flag returned by during calls to work by other threads
107 for(thread_num = 1; thread_num < int_num_threads; thread_num++)
108 ok &= thread_all_[thread_num].ok;
109
110 return ok;
111 }
112 // =====================================================================
113 // End of General purpose code
114 // =====================================================================
115 // function that does the work for one thread
worker(problem_specific * info)116 bool worker(problem_specific* info)
117 { using CppAD::NearEqual;
118 using CppAD::AD;
119 bool ok = true;
120
121 // CppAD::vector uses the CppAD fast multi-threading allocator
122 CppAD::vector< AD<double> > ax(1), ay(1);
123 ax[0] = info->x;
124 Independent(ax);
125 ay[0] = sqrt( ax[0] * ax[0] );
126 CppAD::ADFun<double> f(ax, ay);
127
128 // Check function value corresponds to the identity
129 double eps = 10. * CppAD::numeric_limits<double>::epsilon();
130 ok &= NearEqual(ay[0], ax[0], eps, eps);
131
132 // Check derivative value corresponds to the identity.
133 CppAD::vector<double> d_x(1), d_y(1);
134 d_x[0] = 1.;
135 d_y = f.Forward(1, d_x);
136 ok &= NearEqual(d_x[0], 1., eps, eps);
137
138 return ok;
139 }
140 }
simple_ad(void)141 bool simple_ad(void)
142 { bool ok = true;
143 size_t num_threads = NUMBER_THREADS;
144
145 // Check that no memory is in use or avialable at start
146 // (using thread_alloc in sequential mode)
147 size_t thread_num;
148 for(thread_num = 0; thread_num < num_threads; thread_num++)
149 { ok &= thread_alloc::inuse(thread_num) == 0;
150 ok &= thread_alloc::available(thread_num) == 0;
151 }
152
153 // initialize info_all
154 problem_specific *info, *info_all[NUMBER_THREADS];
155 for(thread_num = 0; thread_num < num_threads; thread_num++)
156 { // problem specific information
157 size_t min_bytes(sizeof(info)), cap_bytes;
158 void* v_ptr = thread_alloc::get_memory(min_bytes, cap_bytes);
159 info = static_cast<problem_specific*>(v_ptr);
160 info->x = double(thread_num) + 1.;
161 info_all[thread_num] = info;
162 }
163
164 ok &= run_all_workers(num_threads, info_all);
165
166 // go down so that free memory for other threads before memory for master
167 thread_num = num_threads;
168 while(thread_num--)
169 { // delete problem specific information
170 void* v_ptr = static_cast<void*>( info_all[thread_num] );
171 thread_alloc::return_memory( v_ptr );
172 // check that there is no longer any memory inuse by this thread
173 ok &= thread_alloc::inuse(thread_num) == 0;
174 // return all memory being held for future use by this thread
175 thread_alloc::free_available(thread_num);
176 }
177
178 return ok;
179 }
180 // END C++
181