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