Substitute pow2bin allocator with a dlmalloc based allocator (#71)

* Add dlmalloc_strat allocator strategy
 - Use mspace variant to ease encapsulation
 - Make pow2bins and dlmalloc cmake selectable
* Add unit tester for dlmalloc, rework single_heap, pow2bins unit testers
accordingly
 - add dlmalloc get_used/get_avail, and have all strats allocators also have a get_used
 - Rework memallocator unit tests: bin size is per strat, alignment is verified in singleheap
* bugfix: dlmalloc exposed that the pingpong test would write past end of
allocation with -w 32
* iostream leakage/mixed usage of cerr and fprintf(stderr

---------

Signed-off-by: Aurelien Bouteiller <aurelien.bouteiller@amd.com>

[ROCm/rocshmem commit: b835de6cd5]
Dieser Commit ist enthalten in:
Aurelien Bouteiller
2025-05-01 11:55:23 -04:00
committet von GitHub
Ursprung 835de6be0e
Commit 27d1189ff3
20 geänderte Dateien mit 6956 neuen und 70 gelöschten Zeilen
@@ -47,8 +47,8 @@ __global__ void PingAllTest(int loop, int skip, long long int *start_time,
status[j] = 0;
}
if (hipThreadIdx_x == 0) {
auto blk_pe_off {hipBlockIdx_x * num_pe};
if (is_thread_zero_in_block()) {
auto blk_pe_off {wg_id * num_pe};
for (int i = 0; i < loop + skip; i++) {
if (i == skip) {
@@ -61,6 +61,7 @@ __global__ void PingAllTest(int loop, int skip, long long int *start_time,
rocshmem_int_wait_until_all(&r_buf[blk_pe_off], num_pe, status, ROCSHMEM_CMP_EQ, 1);
}
end_time[wg_id] = wall_clock64();
rocshmem_ctx_quiet(ctx);
}
rocshmem_wg_ctx_destroy(&ctx);
rocshmem_wg_finalize();
@@ -71,14 +72,14 @@ __global__ void PingAllTest(int loop, int skip, long long int *start_time,
*****************************************************************************/
PingAllTester::PingAllTester(TesterArguments args) : Tester(args) {
int num_pes {rocshmem_n_pes()};
r_buf = (int *)rocshmem_malloc(sizeof(int) * args.wg_size * num_pes);
r_buf = (int *)rocshmem_malloc(sizeof(int) * args.num_wgs * num_pes);
}
PingAllTester::~PingAllTester() { rocshmem_free(r_buf); }
void PingAllTester::resetBuffers(uint64_t size) {
int num_pes {rocshmem_n_pes()};
memset(r_buf, 0, sizeof(int) * args.wg_size * num_pes);
memset(r_buf, 0, sizeof(int) * args.num_wgs * num_pes);
}
void PingAllTester::launchKernel(dim3 gridSize, dim3 blockSize, int loop,
@@ -42,7 +42,7 @@ __global__ void PingPongTest(int loop, int skip, long long int *start_time,
int pe = rocshmem_ctx_my_pe(ctx);
if (hipThreadIdx_x == 0) {
if (is_thread_zero_in_block()) {
for (int i = 0; i < loop + skip; i++) {
if (i == skip) {
@@ -60,7 +60,10 @@ __global__ void PingPongTest(int loop, int skip, long long int *start_time,
}
}
end_time[wg_id] = wall_clock64();
rocshmem_ctx_quiet(ctx);
}
rocshmem_wg_ctx_destroy(&ctx);
rocshmem_wg_finalize();
}
@@ -69,13 +72,13 @@ __global__ void PingPongTest(int loop, int skip, long long int *start_time,
* HOST TESTER CLASS METHODS
*****************************************************************************/
PingPongTester::PingPongTester(TesterArguments args) : Tester(args) {
r_buf = (int *)rocshmem_malloc(sizeof(int) * args.wg_size);
r_buf = (int *)rocshmem_malloc(sizeof(int) * args.num_wgs);
}
PingPongTester::~PingPongTester() { rocshmem_free(r_buf); }
void PingPongTester::resetBuffers(uint64_t size) {
memset(r_buf, 0, sizeof(int) * args.wg_size);
memset(r_buf, 0, sizeof(int) * args.num_wgs);
}
void PingPongTester::launchKernel(dim3 gridSize, dim3 blockSize, int loop,
@@ -84,6 +84,7 @@ target_sources(
single_heap_gtest.cpp
symmetric_heap_gtest.cpp
pow2_bins_gtest.cpp
dlmalloc_gtest.cpp
remote_heap_info_gtest.cpp
mpi_init_singleton_gtest.cpp
abql_block_mutex_gtest.cpp
@@ -0,0 +1,130 @@
/******************************************************************************
* Copyright (c) Advanced Micro Devices, Inc. All rights reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*****************************************************************************/
#include "dlmalloc_gtest.hpp"
#include <cstdint>
using namespace rocshmem;
// a small portion of the heap is not available due to cost of dlmalloc bookkeeping
#define DLMALLOC_BOOKKEEPING static_cast<size_t>(128 * ALIGNMENT)
TEST_F(DLMallocTestFixture, used_0_bytes) {
size_t heap_size{1 << 30};
ASSERT_LE(strat_.get_used(), DLMALLOC_BOOKKEEPING);
ASSERT_EQ(strat_.get_used() + strat_.get_avail(), heap_size);
}
TEST_F(DLMallocTestFixture, alloc_0_bytes) {
size_t initial_used{strat_.get_used()};
char* c_ptr{nullptr};
size_t size{0};
strat_.alloc(&c_ptr, size);
ASSERT_EQ(c_ptr, nullptr);
ASSERT_EQ(strat_.get_used(), initial_used);
}
TEST_F(DLMallocTestFixture, alloc_1_byte) {
char* c_ptr{nullptr};
size_t size{1};
strat_.alloc(&c_ptr, size);
ASSERT_NE(c_ptr, nullptr);
}
TEST_F(DLMallocTestFixture, alloc_128_bytes) {
char* c_ptr{nullptr};
size_t size{128};
strat_.alloc(&c_ptr, size);
ASSERT_NE(c_ptr, nullptr);
}
TEST_F(DLMallocTestFixture, alloc_256_bytes) {
char* c_ptr{nullptr};
size_t size{256};
strat_.alloc(&c_ptr, size);
ASSERT_NE(c_ptr, nullptr);
}
TEST_F(DLMallocTestFixture, alloc_512_bytes) {
char* c_ptr{nullptr};
size_t size{512};
strat_.alloc(&c_ptr, size);
ASSERT_NE(c_ptr, nullptr);
}
TEST_F(DLMallocTestFixture, alloc_513_bytes) {
char* c_ptr{nullptr};
size_t size{513};
strat_.alloc(&c_ptr, size);
ASSERT_NE(c_ptr, nullptr);
}
TEST_F(DLMallocTestFixture, alloc_4KB) {
char* c_ptr{nullptr};
size_t size{4096};
strat_.alloc(&c_ptr, size);
ASSERT_NE(c_ptr, nullptr);
}
TEST_F(DLMallocTestFixture, alloc_128KB) {
char* c_ptr{nullptr};
size_t size{131072};
strat_.alloc(&c_ptr, size);
ASSERT_NE(c_ptr, nullptr);
}
TEST_F(DLMallocTestFixture, alloc_1GB) {
char* c_ptr{nullptr};
size_t heap_size{1 << 30};
size_t size{heap_size - DLMALLOC_BOOKKEEPING};
strat_.alloc(&c_ptr, size);
ASSERT_NE(c_ptr, nullptr);
ASSERT_EQ(strat_.get_used() + strat_.get_avail(), heap_size);
}
TEST_F(DLMallocTestFixture, alloc_256_bytes_X2_free_256_bytes_X2) {
char* c_ptr_1{nullptr};
char* c_ptr_2{nullptr};
size_t size{256};
strat_.alloc(&c_ptr_1, size);
ASSERT_NE(c_ptr_1, nullptr);
strat_.alloc(&c_ptr_2, size);
ASSERT_NE(c_ptr_2, nullptr);
strat_.free(c_ptr_1);
strat_.free(c_ptr_2);
}
TEST_F(DLMallocTestFixture, alloc_1GB_free_1GB) {
char* c_ptr{nullptr};
size_t heap_size{1 << 30};
size_t size{heap_size - DLMALLOC_BOOKKEEPING};
strat_.alloc(&c_ptr, size);
ASSERT_NE(c_ptr, nullptr);
ASSERT_EQ(strat_.get_used() + strat_.get_avail(), heap_size);
strat_.free(c_ptr);
}
@@ -0,0 +1,62 @@
/******************************************************************************
* Copyright (c) Advanced Micro Devices, Inc. All rights reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*****************************************************************************/
#ifndef ROCSHMEM_DLMALLOC_GTEST_HPP
#define ROCSHMEM_DLMALLOC_GTEST_HPP
#include "gtest/gtest.h"
#include "../src/memory/heap_memory.hpp"
#include "../src/memory/hip_allocator.hpp"
#include "../src/memory/dlmalloc.hpp"
namespace rocshmem {
class DLMallocTestFixture : public ::testing::Test
{
/**
* @brief Helper type for heap memory
*/
using HEAP_T = HeapMemory<HIPAllocator>;
/**
* @brief Helper type for allocation strategy
*/
using STRAT_T = DLAllocatorStrategy<HEAP_T>;
protected:
/**
* @brief Heap memory object
*/
HEAP_T heap_mem_ {};
/**
* @brief Allocation strategy object
*/
STRAT_T strat_ {&heap_mem_};
};
} // namespace rocshmem
#endif // ROCSHMEM_DLMALLOC_GTEST_HPP
@@ -26,35 +26,47 @@
using namespace rocshmem;
TEST_F(Pow2BinsTestFixture, used_0_bytes) {
ASSERT_EQ(strat_.get_used(), 0);
}
TEST_F(Pow2BinsTestFixture, alloc_0_bytes) {
char* c_ptr{nullptr};
size_t size{0};
size_t expected_used{0};
strat_.alloc(&c_ptr, size);
ASSERT_EQ(c_ptr, nullptr);
ASSERT_EQ(strat_.get_used(), expected_used);
}
TEST_F(Pow2BinsTestFixture, alloc_1_byte) {
char* c_ptr{nullptr};
size_t size{1};
size_t align_size{ALIGNMENT * (1 + (size - 1) / ALIGNMENT)};
strat_.alloc(&c_ptr, size);
ASSERT_NE(c_ptr, nullptr);
size_t min_size{256};
ASSERT_LE(align_size, 256); // test fixture won't work for larger values
auto bins{strat_.get_bins()};
auto bin{(*bins)[min_size]};
ASSERT_EQ(bin.size(), 1);
ASSERT_EQ(strat_.get_used(), align_size);
}
TEST_F(Pow2BinsTestFixture, alloc_128_bytes) {
char* c_ptr{nullptr};
size_t size{128};
size_t align_size{ALIGNMENT * (1 + (size - 1) / ALIGNMENT)};
strat_.alloc(&c_ptr, size);
ASSERT_NE(c_ptr, nullptr);
size_t min_size{256};
ASSERT_LE(align_size, 256);
auto bins{strat_.get_bins()};
auto bin{(*bins)[min_size]};
ASSERT_EQ(bin.size(), 1);
ASSERT_EQ(strat_.get_used(), align_size);
}
TEST_F(Pow2BinsTestFixture, alloc_256_bytes) {
@@ -66,6 +78,7 @@ TEST_F(Pow2BinsTestFixture, alloc_256_bytes) {
auto bins{strat_.get_bins()};
auto bin{(*bins)[size]};
ASSERT_EQ(bin.size(), 1);
ASSERT_EQ(strat_.get_used(), size);
}
TEST_F(Pow2BinsTestFixture, alloc_512_bytes) {
@@ -77,6 +90,7 @@ TEST_F(Pow2BinsTestFixture, alloc_512_bytes) {
auto bins{strat_.get_bins()};
auto bin{(*bins)[size]};
ASSERT_EQ(bin.size(), 1);
ASSERT_EQ(strat_.get_used(), size);
}
TEST_F(Pow2BinsTestFixture, alloc_513_bytes) {
@@ -89,6 +103,20 @@ TEST_F(Pow2BinsTestFixture, alloc_513_bytes) {
auto bins{strat_.get_bins()};
auto bin{(*bins)[min_size]};
ASSERT_EQ(bin.size(), 1);
ASSERT_EQ(strat_.get_used(), min_size);
}
TEST_F(Pow2BinsTestFixture, alloc_4095_bytes) {
char* c_ptr{nullptr};
size_t size{4095};
strat_.alloc(&c_ptr, size);
ASSERT_NE(c_ptr, nullptr);
size_t min_size{4096};
auto bins{strat_.get_bins()};
auto bin{(*bins)[min_size]};
ASSERT_EQ(bin.size(), 1);
ASSERT_EQ(strat_.get_used(), min_size);
}
TEST_F(Pow2BinsTestFixture, alloc_4KB) {
@@ -100,6 +128,20 @@ TEST_F(Pow2BinsTestFixture, alloc_4KB) {
auto bins{strat_.get_bins()};
auto bin{(*bins)[size]};
ASSERT_EQ(bin.size(), 1);
ASSERT_EQ(strat_.get_used(), size);
}
TEST_F(Pow2BinsTestFixture, alloc_4097_bytes) {
char* c_ptr{nullptr};
size_t size{4097};
strat_.alloc(&c_ptr, size);
ASSERT_NE(c_ptr, nullptr);
size_t min_size{8192};
auto bins{strat_.get_bins()};
auto bin{(*bins)[min_size]};
ASSERT_EQ(bin.size(), 1);
ASSERT_EQ(strat_.get_used(), min_size);
}
TEST_F(Pow2BinsTestFixture, alloc_128KB) {
@@ -111,6 +153,7 @@ TEST_F(Pow2BinsTestFixture, alloc_128KB) {
auto bins{strat_.get_bins()};
auto bin{(*bins)[size]};
ASSERT_EQ(bin.size(), 1);
ASSERT_EQ(strat_.get_used(), size);
}
TEST_F(Pow2BinsTestFixture, alloc_1GB) {
@@ -122,6 +165,7 @@ TEST_F(Pow2BinsTestFixture, alloc_1GB) {
auto bins{strat_.get_bins()};
auto bin{(*bins)[size]};
ASSERT_EQ(bin.size(), 0);
ASSERT_EQ(strat_.get_used(), size);
}
TEST_F(Pow2BinsTestFixture, alloc_256_bytes_X2_free_256_bytes_X2) {
@@ -136,11 +180,13 @@ TEST_F(Pow2BinsTestFixture, alloc_256_bytes_X2_free_256_bytes_X2) {
auto bins{strat_.get_bins()};
auto& bin{(*bins)[size]};
ASSERT_EQ(bin.size(), 0);
ASSERT_EQ(strat_.get_used(), 2 * size);
strat_.free(c_ptr_1);
strat_.free(c_ptr_2);
ASSERT_EQ(bin.size(), 2);
ASSERT_EQ(strat_.get_used(), 0);
}
TEST_F(Pow2BinsTestFixture, alloc_1GB_free_1GB) {
@@ -152,8 +198,10 @@ TEST_F(Pow2BinsTestFixture, alloc_1GB_free_1GB) {
auto bins{strat_.get_bins()};
auto& bin{(*bins)[size]};
ASSERT_EQ(bin.size(), 0);
ASSERT_EQ(strat_.get_used(), size);
strat_.free(c_ptr);
ASSERT_EQ(bin.size(), 1);
ASSERT_EQ(strat_.get_used(), 0);
}
@@ -30,115 +30,157 @@ TEST_F(SingleHeapTestFixture, unallocated_size_check) {
ASSERT_EQ(single_heap_.get_size(), 1 << 30);
}
TEST_F(SingleHeapTestFixture, unallocated_avail_check) {
ASSERT_EQ(single_heap_.get_avail(), 1 << 30);
}
TEST_F(SingleHeapTestFixture, unallocated_used_check) {
ASSERT_EQ(single_heap_.get_used(), 0);
}
TEST_F(SingleHeapTestFixture, free_null) {
void* ptr{nullptr};
single_heap_.free(ptr);
}
TEST_F(SingleHeapTestFixture, alloc_0) {
// some allocators (e.g. dlmalloc) use memory for internal bookkeeping
size_t initial_used{single_heap_.get_used()};
size_t request_size{0};
void* ptr{nullptr};
single_heap_.malloc(&ptr, request_size);
ASSERT_EQ(ptr, nullptr);
size_t expected_used{request_size};
ASSERT_EQ(single_heap_.get_used(), expected_used);
size_t expected_avail{single_heap_.get_size() - expected_used};
ASSERT_EQ(single_heap_.get_avail(), expected_avail);
ASSERT_EQ(single_heap_.get_used(), initial_used);
single_heap_.free(ptr);
expected_used = 0;
ASSERT_EQ(single_heap_.get_used(), expected_used);
expected_avail = single_heap_.get_size();
ASSERT_EQ(single_heap_.get_avail(), expected_avail);
ASSERT_EQ(single_heap_.get_used(), initial_used);
}
TEST_F(SingleHeapTestFixture, alloc_1) {
size_t initial_used{single_heap_.get_used()};
size_t request_size{1};
void* ptr{nullptr};
single_heap_.malloc(&ptr, request_size);
ASSERT_NE(ptr, nullptr);
size_t expected_used{128};
ASSERT_EQ(single_heap_.get_used(), expected_used);
size_t expected_avail{single_heap_.get_size() - expected_used};
ASSERT_EQ(single_heap_.get_avail(), expected_avail);
ASSERT_EQ(reinterpret_cast<uintptr_t>(ptr) & (ALIGNMENT-1), 0);
single_heap_.free(ptr);
expected_used = 0;
ASSERT_EQ(single_heap_.get_used(), expected_used);
expected_avail = single_heap_.get_size();
ASSERT_EQ(single_heap_.get_avail(), expected_avail);
ASSERT_EQ(single_heap_.get_used(), initial_used);
}
TEST_F(SingleHeapTestFixture, alloc_256) {
size_t initial_used{single_heap_.get_used()};
size_t request_size{256};
void* ptr{nullptr};
single_heap_.malloc(&ptr, request_size);
ASSERT_NE(ptr, nullptr);
size_t expected_used{request_size};
ASSERT_EQ(single_heap_.get_used(), expected_used);
size_t expected_avail{single_heap_.get_size() - expected_used};
ASSERT_EQ(single_heap_.get_avail(), expected_avail);
ASSERT_EQ(reinterpret_cast<uintptr_t>(ptr) & (ALIGNMENT-1), 0);
single_heap_.free(ptr);
expected_used = 0;
ASSERT_EQ(single_heap_.get_used(), expected_used);
expected_avail = single_heap_.get_size();
ASSERT_EQ(single_heap_.get_avail(), expected_avail);
ASSERT_EQ(single_heap_.get_used(), initial_used);
}
TEST_F(SingleHeapTestFixture, alloc_1024) {
size_t initial_used{single_heap_.get_used()};
size_t request_size{1024};
void* ptr{nullptr};
single_heap_.malloc(&ptr, request_size);
ASSERT_NE(ptr, nullptr);
size_t expected_used{request_size};
ASSERT_EQ(single_heap_.get_used(), expected_used);
size_t expected_avail{single_heap_.get_size() - expected_used};
ASSERT_EQ(single_heap_.get_avail(), expected_avail);
ASSERT_EQ(reinterpret_cast<uintptr_t>(ptr) & (ALIGNMENT-1), 0);
single_heap_.free(ptr);
ASSERT_EQ(single_heap_.get_used(), initial_used);
}
expected_used = 0;
ASSERT_EQ(single_heap_.get_used(), expected_used);
expected_avail = single_heap_.get_size();
ASSERT_EQ(single_heap_.get_avail(), expected_avail);
TEST_F(SingleHeapTestFixture, alloc_1MB) {
size_t initial_used{single_heap_.get_used()};
size_t request_size{1 << 20};
void* ptr{nullptr};
single_heap_.malloc(&ptr, request_size);
ASSERT_NE(ptr, nullptr);
ASSERT_EQ(reinterpret_cast<uintptr_t>(ptr) & (ALIGNMENT-1), 0);
single_heap_.free(ptr);
ASSERT_EQ(single_heap_.get_used(), initial_used);
}
TEST_F(SingleHeapTestFixture, alloc_4097) {
size_t initial_used{single_heap_.get_used()};
size_t request_size{4097};
void* ptr{nullptr};
single_heap_.malloc(&ptr, request_size);
ASSERT_NE(ptr, nullptr);
size_t expected_used{8192};
ASSERT_EQ(single_heap_.get_used(), expected_used);
size_t expected_avail{single_heap_.get_size() - expected_used};
ASSERT_EQ(single_heap_.get_avail(), expected_avail);
ASSERT_EQ(reinterpret_cast<uintptr_t>(ptr) & (ALIGNMENT-1), 0);
single_heap_.free(ptr);
expected_used = 0;
ASSERT_EQ(single_heap_.get_used(), expected_used);
expected_avail = single_heap_.get_size();
ASSERT_EQ(single_heap_.get_avail(), expected_avail);
ASSERT_EQ(single_heap_.get_used(), initial_used);
}
TEST_F(SingleHeapTestFixture, alloc_X2_8191) {
size_t initial_used{single_heap_.get_used()};
size_t request_size{8191};
void* ptr_1{nullptr};
void* ptr_2{nullptr};
single_heap_.malloc(&ptr_1, request_size);
ASSERT_NE(ptr_1, nullptr);
ASSERT_EQ(reinterpret_cast<uintptr_t>(ptr_1) & (ALIGNMENT-1), 0);
single_heap_.malloc(&ptr_2, request_size);
ASSERT_NE(ptr_2, nullptr);
ASSERT_EQ(reinterpret_cast<uintptr_t>(ptr_2) & (ALIGNMENT-1), 0);
single_heap_.free(ptr_1);
single_heap_.free(ptr_2);
ASSERT_EQ(single_heap_.get_used(), initial_used);
}
TEST_F(SingleHeapTestFixture, alloc_X2_free_alloc_free_X2_1MB) {
size_t initial_used{single_heap_.get_used()};
void* ptr_1{nullptr};
void* ptr_2{nullptr};
void* ptr_3{nullptr};
size_t request_size{1 << 20};
single_heap_.malloc(&ptr_1, request_size);
ASSERT_NE(ptr_1, nullptr);
ASSERT_EQ(reinterpret_cast<uintptr_t>(ptr_1) & (ALIGNMENT-1), 0);
single_heap_.malloc(&ptr_2, request_size);
ASSERT_NE(ptr_1, nullptr);
ASSERT_EQ(reinterpret_cast<uintptr_t>(ptr_2) & (ALIGNMENT-1), 0);
single_heap_.free(ptr_1);
single_heap_.malloc(&ptr_3, request_size);
ASSERT_NE(ptr_3, nullptr);
ASSERT_EQ(reinterpret_cast<uintptr_t>(ptr_3) & (ALIGNMENT-1), 0);
single_heap_.free(ptr_3);
single_heap_.free(ptr_2);
ASSERT_EQ(single_heap_.get_used(), initial_used);
}
TEST_F(SingleHeapTestFixture, alloc_X2_free_alloc_free_X2_63) {
size_t initial_used{single_heap_.get_used()};
void* ptr_1{nullptr};
void* ptr_2{nullptr};
void* ptr_3{nullptr};
size_t request_size{63};
single_heap_.malloc(&ptr_1, request_size);
ASSERT_NE(ptr_1, nullptr);
ASSERT_EQ(reinterpret_cast<uintptr_t>(ptr_1) & (ALIGNMENT-1), 0);
single_heap_.malloc(&ptr_2, request_size);
ASSERT_NE(ptr_1, nullptr);
ASSERT_EQ(reinterpret_cast<uintptr_t>(ptr_2) & (ALIGNMENT-1), 0);
single_heap_.free(ptr_1);
single_heap_.malloc(&ptr_3, request_size);
ASSERT_NE(ptr_3, nullptr);
ASSERT_EQ(reinterpret_cast<uintptr_t>(ptr_3) & (ALIGNMENT-1), 0);
single_heap_.free(ptr_3);
single_heap_.free(ptr_2);
ASSERT_EQ(single_heap_.get_used(), initial_used);
}