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 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
// SPDX-FileCopyrightText: Bradley M. Bell <bradbell@seanet.com>
// SPDX-FileContributor: 2003-22 Bradley M. Bell
// ----------------------------------------------------------------------------
/*
{xrst_begin thread_alloc.cpp}
Fast Multi-Threading Memory Allocator: Example and Test
#######################################################
{xrst_literal
// BEGIN C++
// END C++
}
{xrst_end thread_alloc.cpp}
*/
// BEGIN C++
# include <cppad/utility/thread_alloc.hpp>
# include <vector>
# include <limits>
namespace { // Begin empty namespace
bool raw_allocate(void)
{ bool ok = true;
using CppAD::thread_alloc;
size_t thread;
// check that no memory is initilaly inuse
ok &= thread_alloc::free_all();
// amount of static memory used by thread zero
size_t static_inuse = 0;
// repeatedly allocate enough memory for at least two size_t values.
size_t min_size_t = 2;
size_t min_bytes = min_size_t * sizeof(size_t);
size_t n_outer = 10;
size_t n_inner = 5;
for(size_t i = 0; i < n_outer; i++)
{ // Do not use CppAD::vector here because its use of thread_alloc
// complicates the inuse and available results.
std::vector<void*> v_ptr(n_inner);
// cap_bytes will be set by get_memory
size_t cap_bytes = 0; // set here to avoid MSC warning
for(size_t j = 0; j < n_inner; j++)
{ // allocate enough memory for min_size_t size_t objects
v_ptr[j] = thread_alloc::get_memory(min_bytes, cap_bytes);
size_t* ptr = reinterpret_cast<size_t*>(v_ptr[j]);
// determine the number of size_t values we have obtained
size_t cap_size_t = cap_bytes / sizeof(size_t);
ok &= min_size_t <= cap_size_t;
// use placement new to call the size_t copy constructor
for(size_t k = 0; k < cap_size_t; k++)
new(ptr + k) size_t(i + j + k);
// check that the constructor worked
for(size_t k = 0; k < cap_size_t; k++)
ok &= ptr[k] == (i + j + k);
}
// check that n_inner * cap_bytes are inuse and none are available
thread = thread_alloc::thread_num();
ok &= thread_alloc::inuse(thread) == n_inner*cap_bytes + static_inuse;
ok &= thread_alloc::available(thread) == 0;
// return the memrory to thread_alloc
for(size_t j = 0; j < n_inner; j++)
thread_alloc::return_memory(v_ptr[j]);
// check that now n_inner * cap_bytes are now available
// and none are in use
ok &= thread_alloc::inuse(thread) == static_inuse;
ok &= thread_alloc::available(thread) == n_inner * cap_bytes;
}
thread_alloc::free_available(thread);
// check that the tests have not held onto memory
ok &= thread_alloc::free_all();
return ok;
}
class my_char {
public:
char ch_ ;
my_char(void) : ch_(' ')
{ }
my_char(const my_char& my_ch) : ch_(my_ch.ch_)
{ }
};
bool type_allocate(void)
{ bool ok = true;
using CppAD::thread_alloc;
size_t i;
// check initial memory values
size_t thread = thread_alloc::thread_num();
ok &= thread == 0;
ok &= thread_alloc::free_all();
size_t static_inuse = 0;
// initial allocation of an array
size_t size_min = 3;
size_t size_one;
my_char *array_one =
thread_alloc::create_array<my_char>(size_min, size_one);
// check the values and change them to null 'x'
for(i = 0; i < size_one; i++)
{ ok &= array_one[i].ch_ == ' ';
array_one[i].ch_ = 'x';
}
// now create a longer array
size_t size_two;
my_char *array_two =
thread_alloc::create_array<my_char>(2 * size_min, size_two);
// check the values in array one
for(i = 0; i < size_one; i++)
ok &= array_one[i].ch_ == 'x';
// check the values in array two
for(i = 0; i < size_two; i++)
ok &= array_two[i].ch_ == ' ';
// check the amount of inuse and available memory
// (an extra size_t value is used for each memory block).
size_t check = static_inuse + sizeof(my_char)*(size_one + size_two);
ok &= thread_alloc::inuse(thread) - check < sizeof(my_char);
ok &= thread_alloc::available(thread) == 0;
// delete the arrays
thread_alloc::delete_array(array_one);
thread_alloc::delete_array(array_two);
ok &= thread_alloc::inuse(thread) == static_inuse;
check = sizeof(my_char)*(size_one + size_two);
ok &= thread_alloc::available(thread) - check < sizeof(my_char);
// free the memory for use by this thread
thread_alloc::free_available(thread);
// check that the tests have not held onto memory
ok &= thread_alloc::free_all();
return ok;
}
} // End empty namespace
bool check_alignment(void)
{ bool ok = true;
using CppAD::thread_alloc;
// number of binary digits in a size_t value
size_t n_digit = std::numeric_limits<size_t>::digits;
// must be a multiple of 8
ok &= (n_digit % 8) == 0;
// number of bytes in a size_t value
size_t n_byte = n_digit / 8;
// check raw allocation -------------------------------------------------
size_t min_bytes = 1;
size_t cap_bytes;
void* v_ptr = thread_alloc::get_memory(min_bytes, cap_bytes);
// convert to a size_t value
size_t v_size_t = reinterpret_cast<size_t>(v_ptr);
// check that it is aligned
ok &= (v_size_t % n_byte) == 0;
// return memory to available pool
thread_alloc::return_memory(v_ptr);
// check array allocation ----------------------------------------------
size_t size_min = 1;
size_t size_out;
my_char *array_ptr =
thread_alloc::create_array<my_char>(size_min, size_out);
// convert to a size_t value
size_t array_size_t = reinterpret_cast<size_t>(array_ptr);
// check that it is aligned
ok &= (array_size_t % n_byte) == 0;
// return memory to available pool
thread_alloc::delete_array(array_ptr);
return ok;
}
bool thread_alloc(void)
{ bool ok = true;
using CppAD::thread_alloc;
// check that there is only on thread
ok &= thread_alloc::num_threads() == 1;
// so thread number must be zero
ok &= thread_alloc::thread_num() == 0;
// and we are in sequential execution mode
ok &= thread_alloc::in_parallel() == false;
// Instruct thread_alloc to hold onto memory. This makes memory
// allocation faster (especially when there are multiple threads).
thread_alloc::hold_memory(true);
// run raw allocation tests
ok &= raw_allocate();
// run typed allocation tests
ok &= type_allocate();
// check alignment
ok &= check_alignment();
// return allocator to its default mode
thread_alloc::hold_memory(false);
return ok;
}
// END C++
|