Teuchos Package Browser (Single Doxygen Collection)  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Allocator.cpp
Go to the documentation of this file.
1 // @HEADER
2 // *****************************************************************************
3 // Teuchos: Common Tools Package
4 //
5 // Copyright 2004 NTESS and the Teuchos contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 
13 #include <string>
14 #include <vector>
15 
16 namespace { // (anonymous)
17 
18 
19 TEUCHOS_UNIT_TEST_TEMPLATE_1_DECL( Allocator, Test1, T )
20 {
23  using std::endl;
24 
25  Teuchos::OSTab tab0 (out);
26  out << "Test Teuchos::Details::Allocator for T = "
27  << TypeNameTraits<T>::name () << endl;
28  AllocationLogger::resetAllocationCounts ();
29 
30  typedef Teuchos::Details::Allocator<T> alloc_type;
31  alloc_type alloc;
32 
33  typedef typename alloc_type::size_type size_type;
34 
35  // At this point, we haven't allocated anything yet. The allocator
36  // does not track whatever memory it uses in its implementation.
37  TEST_EQUALITY_CONST( alloc.curAllocInBytes (), static_cast<size_type> (0) );
38 
39  // We'll use this below.
40  size_type oldMaxAlloc = 0;
41 
42  // Put the test in an inner scope, so that the std::vector gets
43  // deallocated before this test finishes. This lets us print
44  // whether deallocation succeeded.
45  {
46  const size_type numEntries = 10;
47 
48  typedef std::vector<T, alloc_type> vec_type;
49  // C++14 defines a two-argument std::vector constructor (count,
50  // alloc), but C++11 only has a three-argument constructor that
51  // takes a count and the allocator. Thus, we need some default
52  // value T. I choose 22 because it is one plus the sum of
53  // integers from 1 to 10, inclusive. It's not a default value,
54  // like zero, and it's positive, so it works if T is unsigned.
55  // It also fits exactly in float and double.
56  T val = static_cast<T> (22);
57  vec_type vec (numEntries, val, alloc);
58 
59  TEST_EQUALITY( vec.size (), numEntries );
60  TEST_EQUALITY_CONST( vec.capacity () >= vec.size (), true );
61 
62  oldMaxAlloc = alloc.maxAllocInBytes ();
63  const size_type curAlloc = alloc.curAllocInBytes ();
64  const size_type expectedCurAlloc = numEntries * sizeof (T);
65 
66  // We don't need strict equality, because the allocator may choose
67  // to allocate more memory than necessary (e.g., to stash
68  // additional information in each allocation).
69  TEST_EQUALITY_CONST( curAlloc >= expectedCurAlloc, true );
70  TEST_EQUALITY_CONST( oldMaxAlloc >= expectedCurAlloc, true );
71 
72  // Make sure that the std::vector's constructor correctly filled
73  // it using val. We have to test this because std::vector defers
74  // to alloc_type::construct for this.
75  for (size_type k = 0; k < numEntries; ++k) {
76  TEST_EQUALITY( vec[k], val );
77  }
78  }
79 
80  // At this point, alloc.curAlloc() should be zero, and
81  // alloc.maxAlloc() should not have changed.
82  const size_type newMaxAlloc = alloc.maxAllocInBytes ();
83  TEST_EQUALITY( oldMaxAlloc, newMaxAlloc );
84  TEST_EQUALITY_CONST( alloc.curAllocInBytes (), static_cast<size_type> (0) );
85 
86  out << "Done with test!" << endl;
87 }
88 
89 //
90 // Repeat Test1, but with verbose logging on.
91 //
92 TEUCHOS_UNIT_TEST_TEMPLATE_1_DECL( Allocator, Test2, T )
93 {
96  using std::endl;
97 
98  Teuchos::OSTab tab0 (out);
99  out << "Test Teuchos::Details::Allocator for T = "
100  << TypeNameTraits<T>::name () << ", with verbose logging on" << endl;
101  AllocationLogger::resetAllocationCounts ();
102 
103  typedef Teuchos::Details::Allocator<T> alloc_type;
104  // Tell the Allocator to track memory.
105  alloc_type alloc (true, true);
106 
107  typedef typename alloc_type::size_type size_type;
108 
109  // At this point, we haven't allocated anything yet. The allocator
110  // does not track whatever memory it uses in its implementation.
111  TEST_EQUALITY_CONST( alloc.curAllocInBytes (), static_cast<size_type> (0) );
112 
113  // We'll use this below.
114  size_type oldMaxAlloc = 0;
115 
116  // Put the test in an inner scope, so that the std::vector gets
117  // deallocated before this test finishes. This lets us print
118  // whether deallocation succeeded.
119  {
120  const size_type numEntries = 10;
121 
122  typedef std::vector<T, alloc_type> vec_type;
123  // C++14 defines a two-argument std::vector constructor (count,
124  // alloc), but C++11 only has a three-argument constructor that
125  // takes a count and the allocator. Thus, we need some default
126  // value T. I choose 22 because it is one plus the sum of
127  // integers from 1 to 10, inclusive. It's not a default value,
128  // like zero, and it's positive, so it works if T is unsigned.
129  // It also fits exactly in float and double.
130  T val = static_cast<T> (22);
131  vec_type vec (numEntries, val, alloc);
132 
133  TEST_EQUALITY( vec.size (), numEntries );
134  TEST_EQUALITY_CONST( vec.capacity () >= vec.size (), true );
135 
136  oldMaxAlloc = alloc.maxAllocInBytes ();
137  const size_type curAlloc = alloc.curAllocInBytes ();
138  const size_type expectedCurAlloc = numEntries * sizeof (T);
139 
140  // We don't need strict equality, because the allocator may choose
141  // to allocate more memory than necessary (e.g., to stash
142  // additional information in each allocation).
143  TEST_EQUALITY_CONST( curAlloc >= expectedCurAlloc, true );
144  TEST_EQUALITY_CONST( oldMaxAlloc >= expectedCurAlloc, true );
145 
146  // Make sure that the std::vector's constructor correctly filled
147  // it using val. We have to test this because std::vector defers
148  // to alloc_type::construct for this.
149  for (size_type k = 0; k < numEntries; ++k) {
150  TEST_EQUALITY( vec[k], val );
151  }
152  }
153 
154  // At this point, alloc.curAlloc() should be zero, and
155  // alloc.maxAlloc() should not have changed.
156  const size_type newMaxAlloc = alloc.maxAllocInBytes ();
157  TEST_EQUALITY( oldMaxAlloc, newMaxAlloc );
158  TEST_EQUALITY_CONST( alloc.curAllocInBytes (), static_cast<size_type> (0) );
159 
160  out << "Done with test!" << endl;
161 }
162 
163 //
164 // Make sure that mixing std::vector<T> instances for different T
165 // still gives the right current and max allocation numbers.
166 //
167 TEUCHOS_UNIT_TEST( Allocator, TestMixedTypes )
168 {
170  using std::endl;
171 
172  Teuchos::OSTab tab0 (out);
173  out << "Test Teuchos::Details::Allocator<T> for mixed T" << endl;
174  AllocationLogger::resetAllocationCounts ();
175 
176  typedef Teuchos::Details::Allocator<int> int_alloc_type;
177  typedef int_alloc_type::size_type size_type;
178  const size_type numEntries = 10;
179  const size_type expectedMaxAlloc = numEntries * sizeof (int) + numEntries * sizeof (double);
180 
181  // At this point, we haven't allocated anything yet.
182  TEST_EQUALITY_CONST( AllocationLogger::curAllocInBytes (), static_cast<size_type> (0) );
183 
184  {
185  std::vector<int, int_alloc_type> intVec (numEntries);
186 
187  typedef Teuchos::Details::Allocator<double> double_alloc_type;
188  std::vector<double, double_alloc_type> dblVec (numEntries);
189 
190  // Both std::vector types must report the same current and max total
191  // allocation sizes, since they share the same allocation mechanism.
192  TEST_EQUALITY( intVec.get_allocator ().curAllocInBytes (), dblVec.get_allocator ().curAllocInBytes () );
193  TEST_EQUALITY( intVec.get_allocator ().maxAllocInBytes (), dblVec.get_allocator ().maxAllocInBytes () );
194 
195 
196  TEST_EQUALITY_CONST( intVec.get_allocator ().curAllocInBytes () >= expectedMaxAlloc, true );
197  TEST_EQUALITY_CONST( intVec.get_allocator ().maxAllocInBytes () >= expectedMaxAlloc, true );
198  }
199 
200  TEST_EQUALITY_CONST( AllocationLogger::curAllocInBytes (), static_cast<size_type> (0) );
201  TEST_EQUALITY_CONST( AllocationLogger::maxAllocInBytes () >= expectedMaxAlloc, true );
202 
203  out << "Done with test!" << endl;
204 }
205 
206 //
207 // Make sure that the Allocator works for types T that do run-time
208 // allocation. std::string is a good example.
209 //
210 // This is the test that shows why you CANNOT use "return new T[n]" to
211 // implement allocate(), and "delete [] p" to implement deallocate().
212 // (Try it on a Mac and watch your debug malloc complain that you're
213 // trying to free something it never malloc'd.)
214 //
215 TEUCHOS_UNIT_TEST( Allocator, TestString )
216 {
218  using std::endl;
219 
220  Teuchos::OSTab tab0 (out);
221  out << "Test Teuchos::Details::Allocator<std::string>" << endl;
222  AllocationLogger::resetAllocationCounts ();
223 
224  typedef Teuchos::Details::Allocator<std::string> string_alloc_type;
225  typedef string_alloc_type::size_type size_type;
226  const size_type numEntries = 10;
227  // Even though std::string itself does run-time allocation inside,
228  // this is still the correct max allocation for an array of
229  // std::string.
230  const size_type expectedMaxAlloc = numEntries * sizeof (std::string);
231 
232  // At this point, we haven't allocated anything yet.
233  TEST_EQUALITY_CONST( AllocationLogger::curAllocInBytes (), static_cast<size_type> (0) );
234 
235  // First, try it without setting any of the strings.
236  {
237  std::vector<std::string, string_alloc_type> vec (numEntries);
238 
239  TEST_EQUALITY_CONST( vec.get_allocator ().curAllocInBytes () >= expectedMaxAlloc, true );
240  TEST_EQUALITY_CONST( vec.get_allocator ().maxAllocInBytes () >= expectedMaxAlloc, true );
241  }
242 
243  TEST_EQUALITY_CONST( AllocationLogger::curAllocInBytes (), static_cast<size_type> (0) );
244  TEST_EQUALITY_CONST( AllocationLogger::maxAllocInBytes () >= expectedMaxAlloc, true );
245 
246  // Next, construct the std::vector, setting all entries to a string
247  // of nonzero length.
248  {
249  string_alloc_type alloc;
250  std::string val ("I'm a little teapot, short and stout.");
251  std::vector<std::string, string_alloc_type> vec (numEntries, val, alloc);
252 
253  TEST_EQUALITY_CONST( vec.get_allocator ().curAllocInBytes () >= expectedMaxAlloc, true );
254  TEST_EQUALITY_CONST( vec.get_allocator ().maxAllocInBytes () >= expectedMaxAlloc, true );
255  }
256 
257  TEST_EQUALITY_CONST( AllocationLogger::curAllocInBytes (), static_cast<size_type> (0) );
258  TEST_EQUALITY_CONST( AllocationLogger::maxAllocInBytes () >= expectedMaxAlloc, true );
259 
260  // Next, construct the std::vector without setting any of the
261  // strings, then iterate through it and set the strings one by one
262  // to different values (that circumvents possible reference counting
263  // in std::string).
264  {
265  string_alloc_type alloc;
266  std::vector<std::string, string_alloc_type> vec (numEntries);
267 
268  for (size_type k = 0; k < numEntries; ++k) {
269  std::ostringstream os;
270  os << "Current index: " << k;
271  vec[k] = os.str ();
272  }
273 
274  TEST_EQUALITY_CONST( vec.get_allocator ().curAllocInBytes () >= expectedMaxAlloc, true );
275  TEST_EQUALITY_CONST( vec.get_allocator ().maxAllocInBytes () >= expectedMaxAlloc, true );
276  }
277 
278  TEST_EQUALITY_CONST( AllocationLogger::curAllocInBytes (), static_cast<size_type> (0) );
279  TEST_EQUALITY_CONST( AllocationLogger::maxAllocInBytes () >= expectedMaxAlloc, true );
280 
281  out << "Done with test!" << endl;
282 }
283 
284 
285 TEUCHOS_UNIT_TEST_TEMPLATE_1_INSTANT( Allocator, Test1, int )
286 TEUCHOS_UNIT_TEST_TEMPLATE_1_INSTANT( Allocator, Test1, double )
287 
288 
289 } // namespace (anonymous)
#define TEUCHOS_UNIT_TEST_TEMPLATE_1_DECL(TEST_GROUP, TEST_NAME, TYPE)
Macro for defining a templated unit test with one template parameter.
#define TEST_EQUALITY(v1, v2)
Assert the equality of v1 and v2.
#define TEUCHOS_UNIT_TEST(TEST_GROUP, TEST_NAME)
Macro for defining a (non-templated) unit test.
Optional tracking allocator for Teuchos Memory Management classes.
Logging implementation used by Allocator (see below).
Tabbing class for helping to create formated, indented output for a basic_FancyOStream object...
Unit testing support.
#define TEST_EQUALITY_CONST(v1, v2)
Assert the equality of v1 and constant v2.
Default traits class that just returns typeid(T).name().
#define TEUCHOS_UNIT_TEST_TEMPLATE_1_INSTANT(TEST_GROUP, TEST_NAME, TYPE)
Instantiate a templated unit test with one template parameter.
Defines basic traits returning the name of a type in a portable and readable way. ...
Declaration of Teuchos::Details::Allocator, a tracking and logging implementation of the C++ Standard...