1/**
2 * Copyright (c) Glow Contributors. See CONTRIBUTORS file.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "glow/CodeGen/MemoryAllocator.h"
18
19#include "gtest/gtest.h"
20
21#include <fstream>
22
23#ifndef GLOW_DATA_PATH
24#define GLOW_DATA_PATH
25#endif
26
27using namespace glow;
28
29TEST(MemAlloc, simple) {
30 MemoryAllocator MA("test", 1000);
31 void *handle = reinterpret_cast<void *>(1);
32
33 // Can't allocate huge chunks.
34 EXPECT_EQ(MA.allocate(100000, handle), MemoryAllocator::npos);
35
36 // First chunk needs to start at zero.
37 EXPECT_EQ(MA.allocate(500, handle), 0);
38
39 // Second chunk must not be zero.
40 EXPECT_NE(MA.allocate(500, handle), 0);
41}
42
43TEST(MemAlloc, holes) {
44 MemoryAllocator MA("test", 1000);
45 void *handle0 = reinterpret_cast<void *>(0);
46 void *handle1 = reinterpret_cast<void *>(1);
47 void *handle2 = reinterpret_cast<void *>(2);
48 void *handle4 = reinterpret_cast<void *>(4);
49
50 MA.allocate(10, handle0);
51 auto p1 = MA.allocate(10, handle1);
52 MA.allocate(10, handle2);
53
54 MA.deallocate(handle1);
55 auto maxMemoryUsageBefore = MA.getMaxMemoryUsage();
56 auto p4 = MA.allocate(10, handle4);
57 auto maxMemoryUsageAfter = MA.getMaxMemoryUsage();
58
59 // Check that p4 was allocated on top of the freed p1.
60 EXPECT_EQ(p4, p1);
61 // Max memory usage should not be affected, as a hole was found and used.
62 EXPECT_EQ(maxMemoryUsageBefore, maxMemoryUsageAfter);
63
64 MA.deallocate(handle0);
65 MA.deallocate(handle2);
66}
67
68/// Check some properties of the first-fit allocation strategy.
69TEST(MemAlloc, firstFitAllocation) {
70 MemoryAllocator MA("test", 1000);
71 void *handle = reinterpret_cast<void *>(10000);
72 void *handle0 = reinterpret_cast<void *>(0);
73 void *handle1 = reinterpret_cast<void *>(1);
74 void *handle2 = reinterpret_cast<void *>(2);
75 void *handle3 = reinterpret_cast<void *>(3);
76 void *handle4 = reinterpret_cast<void *>(4);
77
78 // Allocate three blocks of sizes 30, 20 and 10 and allocate blocks of size 5
79 // between them.
80 auto p0 = MA.allocate(30, handle0);
81 MA.allocate(5, handle);
82 MA.allocate(20, handle1);
83 MA.allocate(5, handle);
84 auto p2 = MA.allocate(10, handle2);
85 MA.allocate(5, handle);
86
87 // Free blocks p0, p1 and p2.
88 MA.deallocate(handle0);
89 MA.deallocate(handle1);
90 MA.deallocate(handle2);
91
92 // Try to allocate a block of size 10.
93 auto maxMemoryUsageBefore = MA.getMaxMemoryUsage();
94 auto p3 = MA.allocate(10, handle3);
95 auto maxMemoryUsageAfter = MA.getMaxMemoryUsage();
96
97 // Check that p4 was allocated on top of the freed p0, because the allocator
98 // uses the first-fit algorithm. Best-fit would have taken the block of p2.
99 EXPECT_EQ(p3, p0);
100 // Max memory usage should not be affected, as a hole was found and used.
101 EXPECT_EQ(maxMemoryUsageBefore, maxMemoryUsageAfter);
102
103 // Allocate 100 bytes. Since the first-fit cannot find any big enough hole
104 // between allocations, the allocator would allocate this block in the free
105 // space after all existing allocations.
106 maxMemoryUsageBefore = MA.getMaxMemoryUsage();
107 auto p4 = MA.allocate(100, handle4);
108 maxMemoryUsageAfter = MA.getMaxMemoryUsage();
109 EXPECT_GT(p4, p2);
110 // Max memory usage should be increased.
111 EXPECT_LT(maxMemoryUsageBefore, maxMemoryUsageAfter);
112}
113
114TEST(MemAlloc, dealloc) {
115 MemoryAllocator MA("test", 1000);
116 void *handle0 = reinterpret_cast<void *>(0);
117 void *handle1 = reinterpret_cast<void *>(1);
118 void *handle2 = reinterpret_cast<void *>(2);
119 void *handle3 = reinterpret_cast<void *>(3);
120 void *handle4 = reinterpret_cast<void *>(4);
121 void *handle5 = reinterpret_cast<void *>(5);
122 void *handle6 = reinterpret_cast<void *>(6);
123
124 auto p0 = MA.allocate(10, handle0);
125 auto p1 = MA.allocate(10, handle1);
126 auto p2 = MA.allocate(10, handle2);
127 auto p3 = MA.allocate(10, handle3);
128
129 auto p4 = MA.allocate(10, handle4);
130
131 auto p5 = MA.allocate(0, handle5);
132 auto p6 = MA.allocate(0, handle6);
133
134 EXPECT_EQ(p0, 0);
135 EXPECT_NE(p1, MemoryAllocator::npos);
136 EXPECT_NE(p2, MemoryAllocator::npos);
137 EXPECT_NE(p3, MemoryAllocator::npos);
138 EXPECT_NE(p4, MemoryAllocator::npos);
139 EXPECT_NE(p5, MemoryAllocator::npos);
140 EXPECT_NE(p6, MemoryAllocator::npos);
141
142 // Deallocate in some arbitrary order.
143 MA.deallocate(handle0);
144 MA.deallocate(handle5);
145 MA.deallocate(handle2);
146 MA.deallocate(handle1);
147 MA.deallocate(handle6);
148 MA.deallocate(handle3);
149 // Check that it is possible to deallocate using the associated handle.
150 MA.deallocate(handle4);
151#ifndef NDEBUG
152 // Check that deallocating a non-allocated or already deallocated buffer
153 // should result in an assertion failure.
154 ASSERT_DEATH_IF_SUPPORTED(MA.deallocate(handle3), "Unknown handle");
155#endif
156 // Check that after deallocating everything we start allocating from zero.
157 EXPECT_EQ(MA.allocate(10, handle0), 0);
158}
159
160TEST(MemAlloc, dealloc2) {
161 MemoryAllocator MA("test", 10000);
162 std::vector<uint64_t> allocations;
163
164 for (int i = 0; i < 100; i++) {
165 // Create odd-sized allocations.
166 const void *handle = reinterpret_cast<void *>(i);
167 auto p0 = MA.allocate(10 + i % 4, handle);
168 EXPECT_TRUE(MA.hasAddress(handle));
169 EXPECT_TRUE(MA.hasHandle(p0));
170
171 EXPECT_NE(p0, MemoryAllocator::npos);
172 allocations.emplace_back(p0);
173
174 if (allocations.size() > 20) {
175 MA.deallocate(MA.getHandle(allocations[0]));
176 allocations.erase(allocations.begin());
177 }
178 }
179 // Drain the allocator.
180 while (!allocations.empty()) {
181 MA.deallocate(MA.getHandle(allocations[0]));
182 allocations.erase(allocations.begin());
183 }
184
185 // Check that after deallocating everything we start allocating from zero.
186 const void *handle = reinterpret_cast<void *>(0);
187 EXPECT_EQ(MA.allocate(10, handle), 0);
188}
189
190TEST(MemAlloc, allocateToTheMax) {
191 MemoryAllocator MA("test", 128);
192 void *handle0 = reinterpret_cast<void *>(0);
193 void *handle1 = reinterpret_cast<void *>(1);
194 auto p0 = MA.allocate(64, handle0);
195 auto p1 = MA.allocate(64, handle1);
196
197 EXPECT_EQ(p0, 0);
198 EXPECT_NE(p1, MemoryAllocator::npos);
199
200 MA.deallocate(handle0);
201 MA.deallocate(handle1);
202
203 EXPECT_EQ(MA.getMaxMemoryUsage(), 128);
204}
205
206TEST(MemAlloc, testContains) {
207 MemoryAllocator MA("test", 1000);
208 void *handle = reinterpret_cast<void *>(0);
209
210 EXPECT_EQ(MA.allocate(200, handle), 0);
211
212 // Offset 100 should be inside an allocated block.
213 EXPECT_TRUE(MA.contains(100));
214 // Offset 300 should not be inside an allocated block.
215 EXPECT_FALSE(MA.contains(300));
216}
217
218TEST(MemAlloc, testHandles) {
219 MemoryAllocator MA("test", 1000);
220 // Define a set of handles to be used.
221 void *handle1 = reinterpret_cast<void *>(1);
222 void *handle2 = reinterpret_cast<void *>(2);
223 (void)handle2;
224 void *handle3 = reinterpret_cast<void *>(3);
225 // Allocate a block of memory p1, but do not associate any handle with it.
226 auto p1 = MA.allocate(10, handle1);
227 // Check that handle1 is associated with the allocated address p1.
228 EXPECT_EQ(MA.getHandle(p1), handle1);
229 // Check that the address p1 is associated with the handle handle1.
230 EXPECT_EQ(MA.getAddress(handle1), p1);
231
232 // Allocate a block of memory p3 and associate a handle handle3 with it.
233 auto p3 = MA.allocate(10, handle3);
234 // The associated handle of p3 should be handle3.
235 EXPECT_EQ(MA.getHandle(p3), handle3);
236 // The address associated with handle3 should be p3.
237 EXPECT_EQ(MA.getAddress(handle3), p3);
238 // Deallocate the memory.
239 MA.deallocate(handle3);
240 // Check that after deallocation there is no handle is associated with the
241 // allocated address.
242 EXPECT_FALSE(MA.hasHandle(p3));
243 // Check that after deallocation there is no address is associated with
244 // handle3.
245 EXPECT_FALSE(MA.hasAddress(handle3));
246
247 MA.reset();
248 p1 = MA.allocate(10, handle1);
249#ifndef NDEBUG
250 // Deallocating handle2 should result in an assertion failure.
251 ASSERT_DEATH_IF_SUPPORTED(MA.deallocate(handle2), "Unknown handle");
252#endif
253}
254
255TEST(MemAlloc, testEviction) {
256 MemoryAllocator MA("test", 1024);
257 // Define a set of handles to be used.
258 void *handle1 = reinterpret_cast<void *>(1);
259 void *handle2 = reinterpret_cast<void *>(2);
260 void *handle3 = reinterpret_cast<void *>(3);
261 void *handle4 = reinterpret_cast<void *>(4);
262 void *handle5 = reinterpret_cast<void *>(5);
263 std::vector<const void *> evicted;
264
265 // Allocation of 500 from 1000 bytes should not trigger any eviction.
266 auto p1 = MA.allocate(500, handle1, {}, evicted);
267 EXPECT_NE(p1, MA.npos);
268 EXPECT_TRUE(evicted.empty());
269
270 // Allocation of 400 from remaining 500 bytes should not trigger any eviction.
271 auto p2 = MA.allocate(400, handle2, {}, evicted);
272 EXPECT_NE(p2, MA.npos);
273 EXPECT_TRUE(evicted.empty());
274
275 // Allocation of 400 from remaining 100 bytes should trigger the eviction.
276 auto p3 = MA.allocate(400, handle3, {}, evicted);
277 // The allocation should be successful.
278 EXPECT_NE(p3, MA.npos);
279 EXPECT_EQ(evicted.size(), 1);
280
281 // Allocation of 2000 bytes is impossible. It should not should trigger any
282 // eviction.
283 evicted.clear();
284 auto p4 = MA.allocate(2000, handle4, {}, evicted);
285 EXPECT_EQ(p4, MA.npos);
286 EXPECT_EQ(evicted.size(), 0);
287
288 // Allocation of 1024 bytes only possible if all other allocated blocks are
289 // evicted.
290 evicted.clear();
291 auto p5 = MA.allocate(1024, handle5, {}, evicted);
292 EXPECT_NE(p5, MA.npos);
293 EXPECT_EQ(evicted.size(), 2);
294
295 // Check how eviction works with a non-empty doNotEvict set.
296 MA.reset();
297 evicted.clear();
298 // Allocate 3 blocks, 256 bytes each.
299 p1 = MA.allocate(256, handle1, {}, evicted);
300 EXPECT_EQ(p1, 0);
301 p2 = MA.allocate(256, handle2, {}, evicted);
302 EXPECT_EQ(p2, 256);
303 p3 = MA.allocate(256, handle3, {}, evicted);
304 EXPECT_EQ(p3, 512);
305 // No blocks should be evicted until now.
306 EXPECT_EQ(evicted.size(), 0);
307 // Try to allocate a block of size 512. Without a doNotEvict set and using a
308 // first-fit eviction strategy, the allocator would have to evict blocks p1
309 // and p2 to satisfy this request. But due to providing a doNotEvict set which
310 // forbids the eviction of p1, the allocator should evict p2 and p3 and
311 // allocate the 512 bytes at the same address as p2.
312 std::set<const void *> doNotEvict{handle1};
313 p4 = MA.allocate(512, handle4, doNotEvict, evicted);
314 EXPECT_EQ(p4, p2);
315 EXPECT_EQ(evicted.size(), 2);
316 EXPECT_EQ(evicted[0], handle2);
317 EXPECT_EQ(evicted[1], handle3);
318}
319
320TEST(MemAlloc, testGetSize) {
321 MemoryAllocator MA("test", 1024);
322 void *handle0 = reinterpret_cast<void *>(0);
323 void *handle1 = reinterpret_cast<void *>(1);
324 // Allocate two memory blocks and checks that the size of the allocated blocks
325 // is reported correctly.
326 MA.allocate(10, handle0);
327 EXPECT_EQ(MA.getSize(handle0), 10);
328 MA.allocate(200, handle1);
329 EXPECT_EQ(MA.getSize(handle1), 200);
330}
331
332TEST(MemAlloc, testGetMemorySize) {
333 MemoryAllocator MA1("test1", 1024);
334 EXPECT_EQ(MA1.getMemorySize(), 1024);
335 MemoryAllocator MA2("test1", 102);
336 EXPECT_EQ(MA2.getMemorySize(), 102);
337}
338
339TEST(MemAlloc, testAlignment) {
340 MemoryAllocator MA1("test1", 1024, 128);
341 MemoryAllocator MA2("test2", 1024, 256);
342
343 void *handle0 = reinterpret_cast<void *>(0);
344 void *handle1 = reinterpret_cast<void *>(1);
345 void *handle2 = reinterpret_cast<void *>(2);
346 void *handle3 = reinterpret_cast<void *>(3);
347
348 // Both allocators start at zero.
349 auto p0 = MA1.allocate(10, handle0);
350 auto p1 = MA2.allocate(10, handle1);
351 EXPECT_EQ(p0, 0);
352 EXPECT_EQ(p1, 0);
353
354 // Second allocation starts at the alignment boundary.
355 auto p2 = MA1.allocate(10, handle2);
356 auto p3 = MA2.allocate(10, handle3);
357 EXPECT_EQ(p2, 128);
358 EXPECT_EQ(p3, 256);
359}
360
361/// ----------------------------------------------------------------------------
362/// Allocate all segments at once
363/// ----------------------------------------------------------------------------
364/// Test efficiency for allocateAll.
365TEST(MemAlloc, testAllocateAllEfficiency) {
366
367 void *handle0 = reinterpret_cast<void *>(0);
368 void *handle1 = reinterpret_cast<void *>(1);
369 void *handle2 = reinterpret_cast<void *>(2);
370
371 // Simple allocate.
372 MemoryAllocator MA1("test", 0, 10);
373 MA1.allocate(10, handle0);
374 MA1.allocate(10, handle1);
375 MA1.deallocate(handle0);
376 MA1.allocate(20, handle2);
377 MA1.deallocate(handle1);
378 MA1.deallocate(handle2);
379 EXPECT_EQ(MA1.getMaxMemoryUsage(), 40);
380 EXPECT_FLOAT_EQ(MA1.getAllocationEfficiency(), 0.75);
381
382 // Allocate all.
383 MemoryAllocator MA2("test", 0, 10);
384 std::list<Allocation> allocList;
385 allocList.emplace_back(handle0, true, 10);
386 allocList.emplace_back(handle1, true, 10);
387 allocList.emplace_back(handle0, false, 0);
388 allocList.emplace_back(handle2, true, 20);
389 allocList.emplace_back(handle1, false, 0);
390 allocList.emplace_back(handle2, false, 0);
391 MA2.allocateAll(allocList);
392 EXPECT_EQ(MA2.getMaxMemoryUsage(), 30);
393 EXPECT_FLOAT_EQ(MA2.getAllocationEfficiency(), 1.00);
394}
395
396/// Test alignment for allocateAll.
397TEST(MemAlloc, testAllocateAllAlignment) {
398 MemoryAllocator MA("test", 192, 64);
399
400 void *handle0 = reinterpret_cast<void *>(0);
401 void *handle1 = reinterpret_cast<void *>(1);
402
403 std::list<Allocation> allocList;
404 allocList.emplace_back(handle0, true, 65);
405 allocList.emplace_back(handle1, true, 64);
406 allocList.emplace_back(handle0, false, 0);
407 allocList.emplace_back(handle1, false, 0);
408 uint64_t usedSize = MA.allocateAll(allocList);
409
410 EXPECT_EQ(usedSize, 192);
411 EXPECT_EQ(MA.getSize(handle0), 65);
412 EXPECT_EQ(MA.getSize(handle1), 64);
413 EXPECT_EQ(MA.getAddress(handle0), 0);
414 EXPECT_EQ(MA.getAddress(handle1), 128);
415 EXPECT_EQ(MA.getSegment(handle0).size(), 65);
416 EXPECT_EQ(MA.getSegment(handle1).size(), 64);
417 EXPECT_EQ(MA.getSegment(handle0).begin, 0);
418 EXPECT_EQ(MA.getSegment(handle1).begin, 128);
419 EXPECT_EQ(MA.getMaxMemoryUsage(), 192);
420 EXPECT_FLOAT_EQ(MA.getAllocationEfficiency(), 1.0);
421}
422
423/// Test invalid number of allocations for allocateAll.
424TEST(MemAlloc, testAllocateAllInvalidNumAllocs) {
425 MemoryAllocator MA("test", 0, 64);
426 void *handle = reinterpret_cast<void *>(0);
427 std::list<Allocation> allocList;
428 allocList.emplace_back(handle, true, 10);
429#ifndef NDEBUG
430 ASSERT_DEATH_IF_SUPPORTED(MA.allocateAll(allocList),
431 "Allocations are invalid!");
432#endif
433}
434
435/// Test invalid FREE before ALLOC for allocateAll.
436TEST(MemAlloc, testAllocateAllInvalidFreeBeforeAlloc) {
437 MemoryAllocator MA("test", 0, 64);
438 void *handle = reinterpret_cast<void *>(0);
439 std::list<Allocation> allocList;
440 allocList.emplace_back(handle, false, 0);
441 allocList.emplace_back(handle, true, 10);
442#ifndef NDEBUG
443 ASSERT_DEATH_IF_SUPPORTED(MA.allocateAll(allocList),
444 "Allocations are invalid!");
445#endif
446}
447
448/// Test invalid handles for allocateAll.
449TEST(MemAlloc, testAllocateAllInvalidHandles1) {
450 MemoryAllocator MA("test", 0, 64);
451 void *handle = reinterpret_cast<void *>(0);
452 std::list<Allocation> allocList;
453 allocList.emplace_back(handle, true, 10);
454 allocList.emplace_back(handle, true, 10);
455 allocList.emplace_back(handle, false, 0);
456 allocList.emplace_back(handle, false, 0);
457#ifndef NDEBUG
458 ASSERT_DEATH_IF_SUPPORTED(MA.allocateAll(allocList),
459 "Allocations are invalid!");
460#endif
461}
462
463/// Test invalid handles for allocateAll.
464TEST(MemAlloc, testAllocateAllInvalidHandles2) {
465 MemoryAllocator MA("test", 0, 64);
466 void *handle0 = reinterpret_cast<void *>(0);
467 void *handle1 = reinterpret_cast<void *>(0);
468 std::list<Allocation> allocList;
469 allocList.emplace_back(handle0, true, 10);
470 allocList.emplace_back(handle0, true, 10);
471 allocList.emplace_back(handle1, false, 0);
472 allocList.emplace_back(handle1, false, 0);
473#ifndef NDEBUG
474 ASSERT_DEATH_IF_SUPPORTED(MA.allocateAll(allocList),
475 "Allocations are invalid!");
476#endif
477}
478
479/// Test allocating segment of size 0 with allocateAll.
480TEST(MemAlloc, testAllocateAllSize0) {
481 MemoryAllocator MA("test", 0, 64);
482 void *handle = reinterpret_cast<void *>(0);
483 std::list<Allocation> allocList;
484 allocList.emplace_back(handle, true, 0);
485 allocList.emplace_back(handle, false, 0);
486 uint64_t usedSize = MA.allocateAll(allocList);
487 EXPECT_EQ(usedSize, 0);
488 EXPECT_EQ(MA.getSize(handle), 0);
489 EXPECT_EQ(MA.getAddress(handle), 0);
490 EXPECT_EQ(MA.getSegment(handle).size(), 0);
491 EXPECT_EQ(MA.getSegment(handle).begin, 0);
492}
493
494/// Test allocating multiple segments of size 0 with allocateAll.
495TEST(MemAlloc, testAllocateAllMultipleSize0) {
496 void *handle0 = reinterpret_cast<void *>(0);
497 void *handle1 = reinterpret_cast<void *>(1);
498 void *handle2 = reinterpret_cast<void *>(2);
499 void *handle3 = reinterpret_cast<void *>(3);
500 void *handle4 = reinterpret_cast<void *>(4);
501 void *handle5 = reinterpret_cast<void *>(5);
502
503 MemoryAllocator MA("test", 0, 10);
504 std::list<Allocation> allocList;
505 allocList.emplace_back(handle5, true, 0);
506 allocList.emplace_back(handle0, true, 10);
507 allocList.emplace_back(handle3, true, 0);
508 allocList.emplace_back(handle1, true, 10);
509 allocList.emplace_back(handle4, true, 0);
510 allocList.emplace_back(handle0, false, 0);
511 allocList.emplace_back(handle2, true, 20);
512 allocList.emplace_back(handle3, false, 0);
513 allocList.emplace_back(handle1, false, 0);
514 allocList.emplace_back(handle4, false, 0);
515 allocList.emplace_back(handle2, false, 0);
516 allocList.emplace_back(handle5, false, 0);
517
518 uint64_t usedSize = MA.allocateAll(allocList);
519 EXPECT_EQ(usedSize, 30);
520 EXPECT_EQ(MA.getMaxMemoryUsage(), 30);
521 EXPECT_FLOAT_EQ(MA.getAllocationEfficiency(), 1.00);
522 EXPECT_EQ(MA.getSize(handle3), 0);
523 EXPECT_EQ(MA.getAddress(handle3), 0);
524 EXPECT_EQ(MA.getSegment(handle3).size(), 0);
525 EXPECT_EQ(MA.getSegment(handle3).begin, 0);
526 EXPECT_EQ(MA.getSize(handle4), 0);
527 EXPECT_EQ(MA.getAddress(handle4), 0);
528 EXPECT_EQ(MA.getSegment(handle4).size(), 0);
529 EXPECT_EQ(MA.getSegment(handle4).begin, 0);
530 EXPECT_EQ(MA.getSize(handle5), 0);
531 EXPECT_EQ(MA.getAddress(handle5), 0);
532 EXPECT_EQ(MA.getSegment(handle5).size(), 0);
533 EXPECT_EQ(MA.getSegment(handle5).begin, 0);
534}
535
536/// Test empty allocs for allocateAll.
537TEST(MemAlloc, testAllocateAllEmptyAlloc) {
538 MemoryAllocator MA("test", 0, 64);
539 std::list<Allocation> allocList;
540 uint64_t usedSize = MA.allocateAll(allocList);
541 EXPECT_EQ(usedSize, 0);
542}
543
544/// Test memory overflow for allocateAll.
545TEST(MemAlloc, testAllocateAllMemOverflow) {
546 MemoryAllocator MA("test", 10, 64);
547 void *handle = reinterpret_cast<void *>(0);
548 std::list<Allocation> allocList;
549 allocList.emplace_back(handle, true, 100);
550 allocList.emplace_back(handle, false, 0);
551 uint64_t usedSize = MA.allocateAll(allocList);
552 EXPECT_EQ(usedSize, MemoryAllocator::npos);
553#ifndef NDEBUG
554 ASSERT_DEATH_IF_SUPPORTED(MA.getSize(handle), "Unknown handle");
555#endif
556}
557
558/// Utility function to test allocation for a given model using \p alignment
559/// and the allocations \p allocs. The expected memory usage and efficiency
560/// are \p expectedUsedSize and \p expectedEfficiency.
561static void testAllocateAllForModel(size_t alignment,
562 const std::list<Allocation> &allocs,
563 uint64_t expectedUsedSize,
564 float expectedEfficiency) {
565 MemoryAllocator MA("mem", 0, alignment);
566 uint64_t usedSize = MA.allocateAll(allocs);
567 EXPECT_EQ(usedSize, expectedUsedSize);
568 for (const auto &alloc : allocs) {
569 if (alloc.alloc) {
570 EXPECT_EQ(MA.getSize(alloc.handle), alloc.size);
571 }
572 };
573 EXPECT_FLOAT_EQ(MA.getAllocationEfficiency(), expectedEfficiency);
574}
575
576/// Test memory allocation for multiple models using a text file with the
577/// following format:
578/// MODEL <model_name>
579/// ALIGN <alignment>
580/// ALLOC <id> <size>
581/// FREE <id>
582/// MEM <expected_memory_usage>
583/// EFF <expected_efficiency>
584TEST(MemAlloc, testAllocateAllForModels) {
585 std::string modelName;
586 unsigned modelIdx = 1;
587 size_t alignment;
588 uint64_t expectedUsedSize;
589 float expectedEfficiency;
590 std::list<Allocation> allocs;
591 std::ifstream fs;
592 std::string filename(GLOW_DATA_PATH
593 "tests/unittests/MemoryAllocatorTestModels.txt");
594 fs.open(filename);
595 assert(fs.is_open() && "Error opening file!");
596 std::string line;
597 while (std::getline(fs, line)) {
598 // Skip comments.
599 if (line.front() == '#') {
600 continue;
601 }
602 // Read model parameters.
603 std::stringstream ss(line);
604 std::string key;
605 ss >> key;
606 if (key == "MODEL") {
607 ss >> modelName;
608 } else if (key == "ALIGN") {
609 ss >> alignment;
610 } else if (key == "ALLOC") {
611 size_t id;
612 uint64_t size;
613 ss >> id >> size;
614 allocs.emplace_back(id, /* alloc */ true, size);
615 } else if (key == "FREE") {
616 size_t id;
617 ss >> id;
618 allocs.emplace_back(id, /* alloc */ false, 0);
619 } else if (key == "MEM") {
620 ss >> expectedUsedSize;
621 } else if (key == "EFF") {
622 ss >> expectedEfficiency;
623 }
624 // Test allocation for model.
625 if (key == "EFF") {
626 std::cout << "[" << modelIdx++
627 << "] Testing memory allocation for model: " << modelName
628 << "\n";
629 testAllocateAllForModel(alignment, allocs, expectedUsedSize,
630 expectedEfficiency);
631 allocs.clear();
632 }
633 }
634 fs.close();
635}
636
637/// Test allocating multiple functions with memory reusage for allocateAll.
638TEST(MemAlloc, testAllocateAllMultipleFunctionsWithReuse) {
639
640 // Allocation sequence for 1st function.
641 void *handle0 = reinterpret_cast<void *>(0);
642 void *handle1 = reinterpret_cast<void *>(1);
643 std::list<Allocation> allocList1;
644 allocList1.emplace_back(handle0, true, 9);
645 allocList1.emplace_back(handle1, true, 9);
646 allocList1.emplace_back(handle0, false, 0);
647 allocList1.emplace_back(handle1, false, 0);
648
649 // Allocation sequence for 2nd function.
650 void *handle2 = reinterpret_cast<void *>(2);
651 void *handle3 = reinterpret_cast<void *>(3);
652 std::list<Allocation> allocList2;
653 allocList2.emplace_back(handle2, true, 19);
654 allocList2.emplace_back(handle3, true, 19);
655 allocList2.emplace_back(handle2, false, 0);
656 allocList2.emplace_back(handle3, false, 0);
657
658 // Allocate all for both functions.
659 MemoryAllocator MA("test", 0, 10);
660 uint64_t usedSize1 = MA.allocateAll(allocList1);
661 uint64_t usedSize2 = MA.allocateAll(allocList2, /* reuseMemory */ true);
662
663 EXPECT_EQ(usedSize1, 20);
664 EXPECT_EQ(usedSize2, 40);
665 EXPECT_EQ(MA.getSize(handle0), 9);
666 EXPECT_EQ(MA.getSize(handle1), 9);
667 EXPECT_EQ(MA.getSize(handle2), 19);
668 EXPECT_EQ(MA.getSize(handle3), 19);
669 EXPECT_EQ(MA.getSegment(handle0).size(), 9);
670 EXPECT_EQ(MA.getSegment(handle1).size(), 9);
671 EXPECT_EQ(MA.getSegment(handle2).size(), 19);
672 EXPECT_EQ(MA.getSegment(handle3).size(), 19);
673 EXPECT_EQ(MA.getAddress(handle0), 0);
674 EXPECT_EQ(MA.getAddress(handle1), 10);
675 EXPECT_EQ(MA.getAddress(handle2), 0);
676 EXPECT_EQ(MA.getAddress(handle3), 20);
677 EXPECT_EQ(MA.getSegment(handle0).begin, 0);
678 EXPECT_EQ(MA.getSegment(handle1).begin, 10);
679 EXPECT_EQ(MA.getSegment(handle2).begin, 0);
680 EXPECT_EQ(MA.getSegment(handle3).begin, 20);
681 EXPECT_EQ(MA.getMaxMemoryUsage(), 40);
682 EXPECT_FLOAT_EQ(MA.getAllocationEfficiency(), 1.0);
683}
684
685/// Test allocating multiple functions without memory reusage for allocateAll.
686TEST(MemAlloc, testAllocateAllMultipleFunctionsWithoutReuse) {
687
688 // Allocation sequence for 1st function.
689 void *handle0 = reinterpret_cast<void *>(0);
690 void *handle1 = reinterpret_cast<void *>(1);
691 std::list<Allocation> allocList1;
692 allocList1.emplace_back(handle0, true, 9);
693 allocList1.emplace_back(handle1, true, 9);
694 allocList1.emplace_back(handle0, false, 0);
695 allocList1.emplace_back(handle1, false, 0);
696
697 // Allocation sequence for 2nd function.
698 void *handle2 = reinterpret_cast<void *>(2);
699 void *handle3 = reinterpret_cast<void *>(3);
700 std::list<Allocation> allocList2;
701 allocList2.emplace_back(handle2, true, 19);
702 allocList2.emplace_back(handle3, true, 19);
703 allocList2.emplace_back(handle2, false, 0);
704 allocList2.emplace_back(handle3, false, 0);
705
706 // Allocate all for both functions.
707 MemoryAllocator MA("test", 0, 10);
708 uint64_t usedSize1 = MA.allocateAll(allocList1);
709 uint64_t usedSize2 = MA.allocateAll(allocList2, /* reuseMemory */ false);
710
711 EXPECT_EQ(usedSize1, 20);
712 EXPECT_EQ(usedSize2, 60);
713 EXPECT_EQ(MA.getSize(handle0), 9);
714 EXPECT_EQ(MA.getSize(handle1), 9);
715 EXPECT_EQ(MA.getSize(handle2), 19);
716 EXPECT_EQ(MA.getSize(handle3), 19);
717 EXPECT_EQ(MA.getSegment(handle0).size(), 9);
718 EXPECT_EQ(MA.getSegment(handle1).size(), 9);
719 EXPECT_EQ(MA.getSegment(handle2).size(), 19);
720 EXPECT_EQ(MA.getSegment(handle3).size(), 19);
721 EXPECT_EQ(MA.getAddress(handle0), 0);
722 EXPECT_EQ(MA.getAddress(handle1), 10);
723 EXPECT_EQ(MA.getAddress(handle2), 20);
724 EXPECT_EQ(MA.getAddress(handle3), 40);
725 EXPECT_EQ(MA.getSegment(handle0).begin, 0);
726 EXPECT_EQ(MA.getSegment(handle1).begin, 10);
727 EXPECT_EQ(MA.getSegment(handle2).begin, 20);
728 EXPECT_EQ(MA.getSegment(handle3).begin, 40);
729 EXPECT_EQ(MA.getMaxMemoryUsage(), 60);
730 EXPECT_FLOAT_EQ(MA.getAllocationEfficiency(), 1.0);
731}
732
733/// Test allocating multiple functions without memory reusage and in which the
734/// allocation for 2nd function fails due to unsufficient memory.
735TEST(MemAlloc, testAllocateAllMultipleFunctionsWithoutReuseFail) {
736
737 // Allocation sequence for 1st function.
738 void *handle0 = reinterpret_cast<void *>(0);
739 void *handle1 = reinterpret_cast<void *>(1);
740 std::list<Allocation> allocList1;
741 allocList1.emplace_back(handle0, true, 9);
742 allocList1.emplace_back(handle1, true, 9);
743 allocList1.emplace_back(handle0, false, 0);
744 allocList1.emplace_back(handle1, false, 0);
745
746 // Allocation sequence for 2nd function.
747 void *handle2 = reinterpret_cast<void *>(2);
748 void *handle3 = reinterpret_cast<void *>(3);
749 std::list<Allocation> allocList2;
750 allocList2.emplace_back(handle2, true, 19);
751 allocList2.emplace_back(handle3, true, 19);
752 allocList2.emplace_back(handle2, false, 0);
753 allocList2.emplace_back(handle3, false, 0);
754
755 // Allocate all for both functions.
756 MemoryAllocator MA("test", 20, 10);
757 uint64_t usedSize1 = MA.allocateAll(allocList1);
758 uint64_t usedSize2 = MA.allocateAll(allocList2, /* reuseMemory */ false);
759
760 EXPECT_EQ(usedSize1, 20);
761 EXPECT_EQ(usedSize2, MemoryAllocator::npos);
762 EXPECT_EQ(MA.getSize(handle0), 9);
763 EXPECT_EQ(MA.getSize(handle1), 9);
764 EXPECT_EQ(MA.getSegment(handle0).size(), 9);
765 EXPECT_EQ(MA.getSegment(handle1).size(), 9);
766 EXPECT_EQ(MA.getAddress(handle0), 0);
767 EXPECT_EQ(MA.getAddress(handle1), 10);
768 EXPECT_EQ(MA.getSegment(handle0).begin, 0);
769 EXPECT_EQ(MA.getSegment(handle1).begin, 10);
770 EXPECT_EQ(MA.getMaxMemoryUsage(), 20);
771 EXPECT_FLOAT_EQ(MA.getAllocationEfficiency(), 1.0);
772}
773