blob: baa4035e47fbba30ec884c9a5bdc4ef774f98e67 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <gtest/gtest.h>
#include "../src/runtime/hexagon/hexagon_device_api.h"
using namespace tvm;
using namespace tvm::runtime;
using namespace tvm::runtime::hexagon;
using namespace tvm::ffi;
class HexagonVtcmPoolTest : public ::testing::Test {
void SetUp() override {
vtcm_pool = HexagonDeviceAPI::Global()->VtcmPool();
max_bytes = vtcm_pool->VtcmAllocatedBytes();
device_bytes = vtcm_pool->VtcmDeviceBytes();
}
void TearDown() override {}
public:
HexagonVtcmPool* vtcm_pool;
size_t max_bytes;
size_t device_bytes;
size_t four_k_block = 4096;
size_t two_k_block = 2048;
size_t one_k_block = 1024;
size_t min_bytes = 128;
};
TEST_F(HexagonVtcmPoolTest, basic) {
void* ptr;
void* ptr2;
CHECK(device_bytes >= max_bytes) << "VTCM device size " << device_bytes
<< " not greater than or equal to allocated size " << max_bytes;
ptr = vtcm_pool->Allocate(max_bytes);
CHECK((reinterpret_cast<uintptr_t>(ptr) & 0x7FF) == 0)
<< "Must be multiple of 2k " << ptr << " " << max_bytes;
vtcm_pool->Free(ptr, max_bytes);
ptr = vtcm_pool->Allocate(two_k_block);
CHECK((reinterpret_cast<uintptr_t>(ptr) & 0x7FF) == 0)
<< "Must be multiple of 2k " << ptr << " " << two_k_block;
vtcm_pool->Free(ptr, two_k_block);
ptr = vtcm_pool->Allocate(one_k_block);
CHECK((reinterpret_cast<uintptr_t>(ptr) & 0x7F) == 0)
<< "Must be multiple of 128 " << ptr << " " << one_k_block;
vtcm_pool->Free(ptr, one_k_block);
ptr = vtcm_pool->Allocate(min_bytes);
CHECK((reinterpret_cast<uintptr_t>(ptr) & 0x7F) == 0)
<< "Must be multiple of 128 " << ptr << " " << min_bytes;
ptr2 = vtcm_pool->Allocate(one_k_block);
CHECK((reinterpret_cast<uintptr_t>(ptr) & 0x7F) == 0)
<< "Must be multiple of 128 " << ptr2 << " " << one_k_block;
vtcm_pool->Free(ptr, min_bytes);
vtcm_pool->Free(ptr2, one_k_block);
EXPECT_THROW(ptr = vtcm_pool->Allocate(1), InternalError);
}
TEST_F(HexagonVtcmPoolTest, small_allocations) {
void* ptr1;
void* ptr2;
void* ptr3;
void* ptr4;
// Allocate small chunk from the back
ptr1 = vtcm_pool->Allocate(min_bytes);
// Allocate from the front
ptr2 = vtcm_pool->Allocate(two_k_block);
// Allocate the rest
ptr3 = vtcm_pool->Allocate(max_bytes - min_bytes - two_k_block);
// Should be no more memory left
EXPECT_THROW(ptr4 = vtcm_pool->Allocate(min_bytes), InternalError);
vtcm_pool->Free(ptr1, min_bytes);
vtcm_pool->Free(ptr2, two_k_block);
vtcm_pool->Free(ptr3, max_bytes - min_bytes - two_k_block);
// Make sure at the end we have the full amount available again
ptr4 = vtcm_pool->Allocate(max_bytes);
vtcm_pool->Free(ptr4, max_bytes);
}
TEST_F(HexagonVtcmPoolTest, no_free_vtcm) {
void* ptr = vtcm_pool->Allocate(max_bytes);
EXPECT_THROW(vtcm_pool->Allocate(min_bytes), InternalError);
vtcm_pool->Free(ptr, max_bytes);
}
TEST_F(HexagonVtcmPoolTest, not_enough_free_vtcm) {
void* ptr = vtcm_pool->Allocate(max_bytes - two_k_block);
EXPECT_THROW(vtcm_pool->Allocate(two_k_block * 2), InternalError);
vtcm_pool->Free(ptr, max_bytes - two_k_block);
}
TEST_F(HexagonVtcmPoolTest, free_with_wrong_size) {
void* ptr = vtcm_pool->Allocate(two_k_block * 2);
EXPECT_THROW(vtcm_pool->Free(ptr, two_k_block), InternalError);
vtcm_pool->Free(ptr, two_k_block * 2);
}
TEST_F(HexagonVtcmPoolTest, free_alloc_combinations) {
void* ptr1;
void* ptr2;
void* ptr3;
void* ptr4;
void* new_ptr;
size_t max_less_3_blocks = max_bytes - (3 * two_k_block);
ptr1 = vtcm_pool->Allocate(two_k_block);
ptr2 = vtcm_pool->Allocate(two_k_block);
ptr3 = vtcm_pool->Allocate(two_k_block);
ptr4 = vtcm_pool->Allocate(max_less_3_blocks);
// Make sure pointers are 2k apart from each other
CHECK(static_cast<char*>(ptr1) + two_k_block == static_cast<char*>(ptr2));
CHECK(static_cast<char*>(ptr2) + two_k_block == static_cast<char*>(ptr3));
CHECK(static_cast<char*>(ptr3) + two_k_block == static_cast<char*>(ptr4));
// Free 2, realloc it, make sure it is the same as before
vtcm_pool->Free(ptr2, two_k_block);
new_ptr = vtcm_pool->Allocate(two_k_block);
CHECK(new_ptr == ptr2);
// Free 1 and 2, re-alloc and make sure they are the same
vtcm_pool->Free(ptr1, two_k_block);
vtcm_pool->Free(ptr2, two_k_block);
new_ptr = vtcm_pool->Allocate(two_k_block);
CHECK(new_ptr == ptr1);
new_ptr = vtcm_pool->Allocate(two_k_block);
CHECK(new_ptr == ptr2);
// Exercise different deletion scenarios
vtcm_pool->Free(ptr2, two_k_block);
vtcm_pool->Free(ptr3, two_k_block);
vtcm_pool->Free(ptr4, max_less_3_blocks);
vtcm_pool->Free(ptr1, two_k_block);
ptr1 = vtcm_pool->Allocate(two_k_block);
ptr2 = vtcm_pool->Allocate(two_k_block);
ptr3 = vtcm_pool->Allocate(two_k_block);
vtcm_pool->Free(ptr1, two_k_block);
vtcm_pool->Free(ptr3, two_k_block);
vtcm_pool->Free(ptr2, two_k_block);
// Make sure at the end we have the full amount available again
ptr4 = vtcm_pool->Allocate(max_bytes);
vtcm_pool->Free(ptr4, max_bytes);
}
TEST_F(HexagonVtcmPoolTest, find_allocation) {
void* ptr1;
void* ptr2;
void* ptr3;
ptr1 = vtcm_pool->Allocate(two_k_block);
ptr2 = vtcm_pool->Allocate(two_k_block);
// Free the first allocation
vtcm_pool->Free(ptr1, two_k_block);
// Allocate a new larger block to initiate search and ensure
// it succeeds despite there not being a match in the first free block.
ptr3 = vtcm_pool->Allocate(four_k_block);
// Clean up the ptrs
vtcm_pool->Free(ptr2, two_k_block);
vtcm_pool->Free(ptr3, four_k_block);
// Make sure at the end we have the full amount available again
ptr1 = vtcm_pool->Allocate(max_bytes);
vtcm_pool->Free(ptr1, max_bytes);
}
TEST_F(HexagonVtcmPoolTest, find_smallest_allocation_combinations) {
void* ptr1;
void* ptr2;
void* ptr3;
void* ptr4;
void* new_ptr;
ptr1 = vtcm_pool->Allocate(two_k_block);
ptr2 = vtcm_pool->Allocate(two_k_block);
ptr3 = vtcm_pool->Allocate(four_k_block);
ptr4 = vtcm_pool->Allocate(four_k_block);
// Fragment memory allocations.
vtcm_pool->Free(ptr2, two_k_block);
vtcm_pool->Free(ptr3, four_k_block);
// Reallocate memory allocations and ensure that the smallest free allocations are used.
new_ptr = vtcm_pool->Allocate(two_k_block);
CHECK(new_ptr == ptr2);
new_ptr = vtcm_pool->Allocate(two_k_block);
CHECK(new_ptr == ptr3);
vtcm_pool->Free(ptr1, two_k_block);
vtcm_pool->Free(ptr2, two_k_block);
vtcm_pool->Free(ptr3, two_k_block);
vtcm_pool->Free(ptr4, four_k_block);
// Rerun the same test for non 2k aligned allocations.
ptr1 = vtcm_pool->Allocate(min_bytes);
ptr2 = vtcm_pool->Allocate(min_bytes);
ptr3 = vtcm_pool->Allocate(one_k_block);
ptr4 = vtcm_pool->Allocate(one_k_block);
// Fragment memory allocations.
vtcm_pool->Free(ptr2, min_bytes);
vtcm_pool->Free(ptr3, one_k_block);
// Reallocate memory allocations and ensure that the smallest free allocations are used.
new_ptr = vtcm_pool->Allocate(min_bytes);
CHECK(new_ptr == ptr2);
new_ptr = vtcm_pool->Allocate(one_k_block);
CHECK(new_ptr == ptr3);
vtcm_pool->Free(ptr1, min_bytes);
vtcm_pool->Free(ptr2, min_bytes);
vtcm_pool->Free(ptr3, one_k_block);
vtcm_pool->Free(ptr4, one_k_block);
// Make sure at the end we have the full amount available again
ptr4 = vtcm_pool->Allocate(max_bytes);
vtcm_pool->Free(ptr4, max_bytes);
}
// Test alignment edge cases allocating through HexagonBuffer
TEST_F(HexagonVtcmPoolTest, vtcm_alignment) {
std::unique_ptr<HexagonBufferManager> test_hexbuffs = std::make_unique<HexagonBufferManager>();
void* ptr;
// Invalid alignments
EXPECT_THROW(test_hexbuffs->AllocateHexagonBuffer(min_bytes, 128 + 1, ffi::String("global")),
InternalError);
EXPECT_THROW(test_hexbuffs->AllocateHexagonBuffer(min_bytes, 2048 + 1, ffi::String("global")),
InternalError);
// Valid alignments, sizes need to be adjusted
ptr = test_hexbuffs->AllocateHexagonBuffer(1, 128, ffi::String("global"));
CHECK((reinterpret_cast<uintptr_t>(ptr) & 0x7F) == 0) << "Must be multiple of 128 " << ptr;
ptr = test_hexbuffs->AllocateHexagonBuffer(127, 128, ffi::String("global"));
CHECK((reinterpret_cast<uintptr_t>(ptr) & 0x7F) == 0) << "Must be multiple of 128 " << ptr;
ptr = test_hexbuffs->AllocateHexagonBuffer(129, 128, ffi::String("global"));
CHECK((reinterpret_cast<uintptr_t>(ptr) & 0x7F) == 0) << "Must be multiple of 128 " << ptr;
ptr = test_hexbuffs->AllocateHexagonBuffer(1, 2048, ffi::String("global"));
CHECK((reinterpret_cast<uintptr_t>(ptr) & 0x7FF) == 0) << "Must be multiple of 2k " << ptr;
ptr = test_hexbuffs->AllocateHexagonBuffer(2047, 2048, ffi::String("global"));
CHECK((reinterpret_cast<uintptr_t>(ptr) & 0x7FF) == 0) << "Must be multiple of 2k " << ptr;
ptr = test_hexbuffs->AllocateHexagonBuffer(2049, 2048, ffi::String("global"));
CHECK((reinterpret_cast<uintptr_t>(ptr) & 0x7FF) == 0) << "Must be multiple of 2k " << ptr;
test_hexbuffs.reset();
// Make sure at the end we have the full amount available again
ptr = vtcm_pool->Allocate(max_bytes);
vtcm_pool->Free(ptr, max_bytes);
}