1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
// SPDX-FileCopyrightText: Bradley M. Bell <bradbell@seanet.com>
// SPDX-FileContributor: 2003-24 Bradley M. Bell
// ----------------------------------------------------------------------------
/*
{xrst_begin team_example.cpp}
{xrst_spell
bthread
openmp
pthread
}
Using a Team of AD Threads: Example and Test
############################################
Purpose
*******
This example demonstrates how use a team of threads with CppAD.
thread_team
***********
The following three implementations of the
:ref:`team_thread.hpp-name` specifications are included:
.. csv-table::
:widths: auto
team_openmp.cpp,:ref:`team_openmp.cpp-title`
team_bthread.cpp,:ref:`team_bthread.cpp-title`
team_pthread.cpp,:ref:`team_pthread.cpp-title`
Source Code
***********
{xrst_literal
// BEGIN C++
// END C++
}
{xrst_end team_example.cpp}
------------------------------------------------------------------------------
*/
// BEGIN C++
# include <cppad/cppad.hpp>
# include "team_thread.hpp"
# define NUMBER_THREADS 4
namespace {
using CppAD::thread_alloc;
// structure with information for one thread
typedef struct {
// function argument (worker input)
double x;
// false if an error occurs, true otherwise (worker output)
bool ok;
} work_one_t;
// vector with information for all threads
// (use pointers instead of values to avoid false sharing)
work_one_t* work_all_[NUMBER_THREADS];
// --------------------------------------------------------------------
// function that does the work for one thread
void worker(void)
{ using CppAD::NearEqual;
using CppAD::AD;
bool ok = true;
size_t thread_num = thread_alloc::thread_num();
// CppAD::vector uses the CppAD fast multi-threading allocator
CppAD::vector< AD<double> > ax(1), ay(1);
ax[0] = work_all_[thread_num]->x;
Independent(ax);
ay[0] = sqrt( ax[0] * ax[0] );
CppAD::ADFun<double> f(ax, ay);
// Check function value corresponds to the identity
double eps = 10. * CppAD::numeric_limits<double>::epsilon();
ok &= NearEqual(ay[0], ax[0], eps, eps);
// Check derivative value corresponds to the identity.
CppAD::vector<double> d_x(1), d_y(1);
d_x[0] = 1.;
d_y = f.Forward(1, d_x);
ok &= NearEqual(d_x[0], 1., eps, eps);
// pass back ok information for this thread
work_all_[thread_num]->ok = ok;
}
}
// This test routine is only called by the master thread (thread_num = 0).
bool team_example(void)
{ bool ok = true;
size_t num_threads = NUMBER_THREADS;
// Check that no memory is in use or avialable at start
// (using thread_alloc in sequential mode)
size_t thread_num;
for(thread_num = 0; thread_num < num_threads; thread_num++)
{ ok &= thread_alloc::inuse(thread_num) == 0;
ok &= thread_alloc::available(thread_num) == 0;
}
// initialize work_all_
for(thread_num = 0; thread_num < num_threads; thread_num++)
{ // allocate separate memory for this thread to avoid false sharing
size_t min_bytes(sizeof(work_one_t)), cap_bytes;
void* v_ptr = thread_alloc::get_memory(min_bytes, cap_bytes);
work_all_[thread_num] = static_cast<work_one_t*>(v_ptr);
// in case this thread's worker does not get called
work_all_[thread_num]->ok = false;
// parameter that defines the work for this thread
work_all_[thread_num]->x = double(thread_num) + 1.;
}
ok &= team_create(num_threads);
ok &= team_work(worker);
ok &= team_destroy();
// go down so that free memrory for other threads before memory for master
thread_num = num_threads;
while(thread_num--)
{ // check that this thread was ok with the work it did
ok &= work_all_[thread_num]->ok;
// delete problem specific information
void* v_ptr = static_cast<void*>( work_all_[thread_num] );
thread_alloc::return_memory( v_ptr );
// check that there is no longer any memory inuse by this thread
// (for general applications, the master might still be using memory)
ok &= thread_alloc::inuse(thread_num) == 0;
// return all memory being held for future use by this thread
thread_alloc::free_available(thread_num);
}
return ok;
}
// END C++
|