Impala
Impalaistheopensource,nativeanalyticdatabaseforApacheHadoop.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
mem-pool-test.cc
Go to the documentation of this file.
1 // Copyright 2012 Cloudera Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <string>
16 #include <gtest/gtest.h>
17 
18 #include "runtime/mem-pool.h"
19 #include "runtime/mem-tracker.h"
20 
21 #include "common/names.h"
22 
23 namespace impala {
24 
25 TEST(MemPoolTest, Basic) {
27  MemPool p(&tracker);
28  MemPool p2(&tracker);
29  MemPool p3(&tracker);
30 
31  for (int iter = 0; iter < 2; ++iter) {
32  // allocate a total of 24K in 32-byte pieces (for which we only request 25 bytes)
33  for (int i = 0; i < 768; ++i) {
34  // pads to 32 bytes
35  p.Allocate(25);
36  }
37  // we handed back 24K
38  EXPECT_EQ(p.total_allocated_bytes(), 24 * 1024);
39  // .. and allocated 28K of chunks (4, 8, 16)
40  EXPECT_EQ(p.GetTotalChunkSizes(), 28 * 1024);
41 
42  // we're passing on the first two chunks, containing 12K of data; we're left with
43  // one chunk of 16K containing 12K of data
44  p2.AcquireData(&p, true);
45  EXPECT_EQ(p.total_allocated_bytes(), 12 * 1024);
46  EXPECT_EQ(p.GetTotalChunkSizes(), 16 * 1024);
47 
48  // we allocate 8K, for which there isn't enough room in the current chunk,
49  // so another one is allocated (32K)
50  p.Allocate(8 * 1024);
51  EXPECT_EQ(p.GetTotalChunkSizes(), (16 + 32) * 1024);
52 
53  // we allocate 65K, which doesn't fit into the current chunk or the default
54  // size of the next allocated chunk (64K)
55  p.Allocate(65 * 1024);
56  EXPECT_EQ(p.total_allocated_bytes(), (12 + 8 + 65) * 1024);
57  if (iter == 0) {
58  EXPECT_EQ(p.peak_allocated_bytes(), (12 + 8 + 65) * 1024);
59  } else {
60  EXPECT_EQ(p.peak_allocated_bytes(), (1 + 120 + 33) * 1024);
61  }
62  EXPECT_EQ(p.GetTotalChunkSizes(), (16 + 32 + 65) * 1024);
63 
64  // Clear() resets allocated data, but doesn't remove any chunks
65  p.Clear();
66  EXPECT_EQ(p.total_allocated_bytes(), 0);
67  if (iter == 0) {
68  EXPECT_EQ(p.peak_allocated_bytes(), (12 + 8 + 65) * 1024);
69  } else {
70  EXPECT_EQ(p.peak_allocated_bytes(), (1 + 120 + 33) * 1024);
71  }
72  EXPECT_EQ(p.GetTotalChunkSizes(), (16 + 32 + 65) * 1024);
73 
74  // next allocation reuses existing chunks
75  p.Allocate(1024);
76  EXPECT_EQ(p.total_allocated_bytes(), 1024);
77  if (iter == 0) {
78  EXPECT_EQ(p.peak_allocated_bytes(), (12 + 8 + 65) * 1024);
79  } else {
80  EXPECT_EQ(p.peak_allocated_bytes(), (1 + 120 + 33) * 1024);
81  }
82  EXPECT_EQ(p.GetTotalChunkSizes(), (16 + 32 + 65) * 1024);
83 
84  // ... unless it doesn't fit into any available chunk
85  p.Allocate(120 * 1024);
86  EXPECT_EQ(p.total_allocated_bytes(), (1 + 120) * 1024);
87  if (iter == 0) {
88  EXPECT_EQ(p.peak_allocated_bytes(), (1 + 120) * 1024);
89  } else {
90  EXPECT_EQ(p.peak_allocated_bytes(), (1 + 120 + 33) * 1024);
91  }
92  EXPECT_EQ(p.GetTotalChunkSizes(), (130 + 16 + 32 + 65) * 1024);
93 
94  // ... Try another chunk that fits into an existing chunk
95  p.Allocate(33 * 1024);
96  EXPECT_EQ(p.total_allocated_bytes(), (1 + 120 + 33) * 1024);
97  EXPECT_EQ(p.GetTotalChunkSizes(), (130 + 16 + 32 + 65) * 1024);
98 
99  // we're releasing 3 chunks, which get added to p2
100  p2.AcquireData(&p, false);
101  EXPECT_EQ(p.total_allocated_bytes(), 0);
102  EXPECT_EQ(p.peak_allocated_bytes(), (1 + 120 + 33) * 1024);
103  EXPECT_EQ(p.GetTotalChunkSizes(), 0);
104 
105  p3.AcquireData(&p2, true); // we're keeping the 65k chunk
106  EXPECT_EQ(p2.total_allocated_bytes(), 33 * 1024);
107  EXPECT_EQ(p2.GetTotalChunkSizes(), 65 * 1024);
108 
109  p.FreeAll();
110  p2.FreeAll();
111  p3.FreeAll();
112  }
113 }
114 
115 TEST(MemPoolTest, Offsets) {
117  MemPool p(&tracker);
118  uint8_t* data[1024];
119  int offset = 0;
120  // test GetCurrentOffset()
121  for (int i = 0; i < 1024; ++i) {
122  EXPECT_EQ(offset, p.GetCurrentOffset());
123  data[i] = p.Allocate(8);
124  offset += 8;
125  }
126 
127  // test GetOffset()
128  offset = 0;
129  for (int i = 0; i < 1024; ++i) {
130  EXPECT_EQ(offset, p.GetOffset(data[i]));
131  offset += 8;
132  }
133 
134  // test GetDataPtr
135  offset = 0;
136  for (int i = 0; i < 1024; ++i) {
137  EXPECT_EQ(data[i], p.GetDataPtr(offset));
138  offset += 8;
139  }
140  p.FreeAll();
141 }
142 
143 // Test that we can keep an allocated chunk and a free chunk.
144 // This case verifies that when chunks are acquired by another memory pool the
145 // remaining chunks are consistent if there were more than one used chunk and some
146 // free chunks.
149  MemPool p(&tracker);
150  p.Allocate(4*1024);
151  p.Allocate(8*1024);
152  p.Allocate(16*1024);
153  EXPECT_EQ(p.total_allocated_bytes(), (4 + 8 + 16) * 1024);
154  EXPECT_EQ(p.GetTotalChunkSizes(), (4 + 8 + 16) * 1024);
155  p.Clear();
156  EXPECT_EQ(p.total_allocated_bytes(), 0);
157  EXPECT_EQ(p.GetTotalChunkSizes(), (4 + 8 + 16) * 1024);
158  p.Allocate(1*1024);
159  p.Allocate(4*1024);
160  EXPECT_EQ(p.total_allocated_bytes(), (1 + 4) * 1024);
161  EXPECT_EQ(p.GetTotalChunkSizes(), (4 + 8 + 16) * 1024);
162  MemPool p2(&tracker);
163  p2.AcquireData(&p, true);
164  EXPECT_EQ(p.total_allocated_bytes(), 4 * 1024);
165  EXPECT_EQ(p.GetTotalChunkSizes(), (8 + 16) * 1024);
166  EXPECT_EQ(p2.total_allocated_bytes(), 1 * 1024);
167  EXPECT_EQ(p2.GetTotalChunkSizes(), 4 * 1024);
168 
169  p.FreeAll();
170  p2.FreeAll();
171 }
172 
173 // Tests that we can return partial allocations.
174 TEST(MemPoolTest, ReturnPartial) {
176  MemPool p(&tracker);
177  uint8_t* ptr = p.Allocate(1024);
178  EXPECT_EQ(p.total_allocated_bytes(), 1024);
179  memset(ptr, 0, 1024);
180  p.ReturnPartialAllocation(1024);
181 
182  uint8_t* ptr2 = p.Allocate(1024);
183  EXPECT_EQ(p.total_allocated_bytes(), 1024);
184  EXPECT_TRUE(ptr == ptr2);
185  p.ReturnPartialAllocation(1016);
186 
187  ptr2 = p.Allocate(1016);
188  EXPECT_EQ(p.total_allocated_bytes(), 1024);
189  EXPECT_TRUE(ptr2 == ptr + 8);
191  memset(ptr2, 1, 1016 - 512);
192 
193  uint8_t* ptr3 = p.Allocate(512);
194  EXPECT_EQ(p.total_allocated_bytes(), 1024);
195  EXPECT_TRUE(ptr3 == ptr + 512);
196  memset(ptr3, 2, 512);
197 
198  for (int i = 0; i < 8; ++i) {
199  EXPECT_EQ(ptr[i], 0);
200  }
201  for (int i = 8; i < 512; ++i) {
202  EXPECT_EQ(ptr[i], 1);
203  }
204  for (int i = 512; i < 1024; ++i) {
205  EXPECT_EQ(ptr[i], 2);
206  }
207 
208  p.FreeAll();
209 }
210 
211 // Utility class to call private functions on MemPool.
212 class MemPoolTest {
213  public:
214  static bool CheckIntegrity(MemPool* pool, bool current_chunk_empty) {
215  return pool->CheckIntegrity(current_chunk_empty);
216  }
217 };
218 
219 TEST(MemPoolTest, Limits) {
220  MemTracker limit3(320);
221  MemTracker limit1(160, -1, "", &limit3);
222  MemTracker limit2(240, -1, "", &limit3);
223 
224  MemPool* p1 = new MemPool(&limit1, 80);
225  EXPECT_FALSE(limit1.AnyLimitExceeded());
226 
227  MemPool* p2 = new MemPool(&limit2, 80);
228  EXPECT_FALSE(limit2.AnyLimitExceeded());
229 
230  // p1 exceeds a non-shared limit
231  p1->Allocate(80);
232  EXPECT_FALSE(limit1.LimitExceeded());
233  EXPECT_EQ(limit1.consumption(), 80);
234  EXPECT_FALSE(limit3.LimitExceeded());
235  EXPECT_EQ(limit3.consumption(), 80);
236 
237  p1->Allocate(88);
238  EXPECT_TRUE(limit1.LimitExceeded());
239  EXPECT_EQ(limit1.consumption(), 168);
240  EXPECT_FALSE(limit3.LimitExceeded());
241  EXPECT_EQ(limit3.consumption(), 168);
242 
243  // p2 exceeds a shared limit
244  p2->Allocate(80);
245  EXPECT_FALSE(limit2.LimitExceeded());
246  EXPECT_EQ(limit2.consumption(), 80);
247  EXPECT_FALSE(limit3.LimitExceeded());
248  EXPECT_EQ(limit3.consumption(), 248);
249 
250  p2->Allocate(80);
251  EXPECT_FALSE(limit2.LimitExceeded());
252  EXPECT_EQ(limit2.consumption(), 160);
253  EXPECT_TRUE(limit3.LimitExceeded());
254  EXPECT_EQ(limit3.consumption(), 328);
255 
256  // deleting pools reduces consumption
257  p1->FreeAll();
258  delete p1;
259  EXPECT_EQ(limit1.consumption(), 0);
260  EXPECT_EQ(limit2.consumption(), 160);
261  EXPECT_EQ(limit3.consumption(), 160);
262 
263  // Allocate 160 bytes from 240 byte limit.
264  p2->FreeAll();
265  EXPECT_FALSE(limit2.LimitExceeded());
266  uint8_t* result = p2->TryAllocate(160);
267  DCHECK(result != NULL);
268  DCHECK(MemPoolTest::CheckIntegrity(p2, false));
269 
270  // Try To allocate another 160 bytes, this should fail.
271  result = p2->TryAllocate(160);
272  DCHECK(result == NULL);
273  DCHECK(MemPoolTest::CheckIntegrity(p2, false));
274 
275  // Try To allocate 20 bytes, this should succeed. TryAllocate() should leave the
276  // pool in a functional state..
277  result = p2->TryAllocate(20);
278  DCHECK(result != NULL);
279  DCHECK(MemPoolTest::CheckIntegrity(p2, false));
280 
281 
282  p2->FreeAll();
283  delete p2;
284 }
285 
286 }
287 
288 int main(int argc, char **argv) {
289  ::testing::InitGoogleTest(&argc, argv);
290  return RUN_ALL_TESTS();
291 }
bool CheckIntegrity(bool current_chunk_empty)
Definition: mem-pool.cc:265
int main(int argc, char **argv)
int64_t consumption() const
Returns the memory consumed in bytes.
Definition: mem-tracker.h:298
int64_t peak_allocated_bytes() const
Definition: mem-pool.h:149
uint8_t * GetDataPtr(int offset)
Definition: mem-pool.h:294
MemTracker tracker
int64_t total_allocated_bytes() const
Definition: mem-pool.h:148
TEST(AtomicTest, Basic)
Definition: atomic-test.cc:28
int64_t GetTotalChunkSizes() const
Return sum of chunk_sizes_.
Definition: mem-pool.cc:257
void AcquireData(MemPool *src, bool keep_current)
Definition: mem-pool.cc:161
void Clear()
Makes all allocated chunks available for re-use, but doesn't delete any chunks.
Definition: mem-pool.h:118
void ReturnPartialAllocation(int byte_size)
Definition: mem-pool.h:107
void FreeAll()
Definition: mem-pool.cc:73
ObjectPool pool
int GetCurrentOffset() const
Definition: mem-pool.h:163
This class is thread-safe.
Definition: mem-tracker.h:61
uint8_t offset[7 *64-sizeof(uint64_t)]
int GetOffset(uint8_t *data)
Definition: mem-pool.h:283
static bool CheckIntegrity(MemPool *pool, bool current_chunk_empty)
uint8_t * Allocate(int size)
Definition: mem-pool.h:92