diff --git a/runtime/memory_manager/CMakeLists.txt b/runtime/memory_manager/CMakeLists.txt index 879f6dd639..f1be7eb3d5 100644 --- a/runtime/memory_manager/CMakeLists.txt +++ b/runtime/memory_manager/CMakeLists.txt @@ -9,6 +9,8 @@ set(RUNTIME_SRCS_MEMORY_MANAGER ${CMAKE_CURRENT_SOURCE_DIR}/address_mapper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/address_mapper.h ${CMAKE_CURRENT_SOURCE_DIR}/allocations_list.h + ${CMAKE_CURRENT_SOURCE_DIR}/deferrable_allocation_deletion.h + ${CMAKE_CURRENT_SOURCE_DIR}/deferrable_allocation_deletion.cpp ${CMAKE_CURRENT_SOURCE_DIR}/deferrable_deletion.h ${CMAKE_CURRENT_SOURCE_DIR}/deferred_deleter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/deferred_deleter.h diff --git a/runtime/memory_manager/deferrable_allocation_deletion.cpp b/runtime/memory_manager/deferrable_allocation_deletion.cpp new file mode 100644 index 0000000000..8be13d023f --- /dev/null +++ b/runtime/memory_manager/deferrable_allocation_deletion.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2018 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#include "runtime/command_stream/command_stream_receiver.h" +#include "runtime/memory_manager/deferrable_allocation_deletion.h" +#include "runtime/memory_manager/memory_manager.h" + +namespace OCLRT { + +DeferrableAllocationDeletion::DeferrableAllocationDeletion(MemoryManager &memoryManager, GraphicsAllocation &graphicsAllocation) : memoryManager(memoryManager), + graphicsAllocation(graphicsAllocation) {} +void DeferrableAllocationDeletion::apply() { + while (graphicsAllocation.isUsed()) { + + for (auto contextId = 0u; contextId < memoryManager.getOsContextCount(); contextId++) { + if (graphicsAllocation.isUsedByContext(contextId)) { + auto currentContextTaskCount = *memoryManager.getCommandStreamReceiver(contextId)->getTagAddress(); + if (graphicsAllocation.getTaskCount(contextId) <= currentContextTaskCount) { + graphicsAllocation.resetTaskCount(contextId); + } + } + } + } + memoryManager.freeGraphicsMemory(&graphicsAllocation); +} +} // namespace OCLRT diff --git a/runtime/memory_manager/deferrable_allocation_deletion.h b/runtime/memory_manager/deferrable_allocation_deletion.h new file mode 100644 index 0000000000..0ffb82f19e --- /dev/null +++ b/runtime/memory_manager/deferrable_allocation_deletion.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2018 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#pragma once +#include "runtime/memory_manager/deferrable_deletion.h" + +namespace OCLRT { + +class GraphicsAllocation; +class MemoryManager; + +class DeferrableAllocationDeletion : public DeferrableDeletion { + public: + DeferrableAllocationDeletion(MemoryManager &memoryManager, GraphicsAllocation &graphicsAllocation); + void apply() override; + + protected: + MemoryManager &memoryManager; + GraphicsAllocation &graphicsAllocation; +}; +} // namespace OCLRT diff --git a/runtime/memory_manager/graphics_allocation.h b/runtime/memory_manager/graphics_allocation.h index 0c86b68f5a..720b270f4f 100644 --- a/runtime/memory_manager/graphics_allocation.h +++ b/runtime/memory_manager/graphics_allocation.h @@ -120,8 +120,10 @@ class GraphicsAllocation : public IDNode { return memoryPool; } bool isUsed() const { return registeredContextsNum > 0; } + bool isUsedByContext(uint32_t contextId) const { return objectNotUsed != getTaskCount(contextId); } void updateTaskCount(uint32_t newTaskCount, uint32_t contextId); uint32_t getTaskCount(uint32_t contextId) const { return usageInfos[contextId].taskCount; } + void resetTaskCount(uint32_t contextId) { updateTaskCount(objectNotUsed, contextId); } void updateResidencyTaskCount(uint32_t newTaskCount, uint32_t contextId) { usageInfos[contextId].residencyTaskCount = newTaskCount; } uint32_t getResidencyTaskCount(uint32_t contextId) const { return usageInfos[contextId].residencyTaskCount; } diff --git a/unit_tests/memory_manager/CMakeLists.txt b/unit_tests/memory_manager/CMakeLists.txt index 73b2ce825b..4a51731b7f 100644 --- a/unit_tests/memory_manager/CMakeLists.txt +++ b/unit_tests/memory_manager/CMakeLists.txt @@ -7,6 +7,7 @@ set(IGDRCL_SRCS_tests_memory_manager ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt ${CMAKE_CURRENT_SOURCE_DIR}/address_mapper_tests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/deferrable_allocation_deletion_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/deferred_deleter_mt_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/graphics_allocation_tests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/host_ptr_manager_tests.cpp diff --git a/unit_tests/memory_manager/deferrable_allocation_deletion_tests.cpp b/unit_tests/memory_manager/deferrable_allocation_deletion_tests.cpp new file mode 100644 index 0000000000..cd121b1c16 --- /dev/null +++ b/unit_tests/memory_manager/deferrable_allocation_deletion_tests.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2017-2018 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#include "runtime/command_stream/command_stream_receiver.h" +#include "runtime/device/device.h" +#include "runtime/memory_manager/deferred_deleter.h" +#include "runtime/memory_manager/deferrable_allocation_deletion.h" + +#include "unit_tests/mocks/mock_memory_manager.h" +#include "gtest/gtest.h" + +using namespace OCLRT; + +struct DeferredDeleterPublic : DeferredDeleter { + public: + using DeferredDeleter::doWorkInBackground; + using DeferredDeleter::queue; + using DeferredDeleter::queueMutex; + bool shouldStopReached = false; + bool allowExit = false; + bool shouldStop() override { + shouldStopReached = allowExit; + return allowExit; + } +}; + +struct DeferrableAllocationDeletionTest : ::testing::Test { + void SetUp() override { + auto executionEnvironment = std::make_unique(); + memoryManager = new MockMemoryManager(*executionEnvironment); + executionEnvironment->memoryManager.reset(memoryManager); + device.reset(Device::create(nullptr, executionEnvironment.release(), 0u)); + hwTag = device->getTagAddress(); + asyncDeleter = std::make_unique(); + asyncDeleter->addClient(); + } + void TearDown() override { + asyncDeleter->removeClient(); + } + std::unique_ptr asyncDeleter; + MockMemoryManager *memoryManager = nullptr; + std::unique_ptr device; + volatile uint32_t *hwTag = nullptr; +}; + +TEST_F(DeferrableAllocationDeletionTest, givenDeferrableAllocationWhenApplyThenWaitForEachTaskCount) { + EXPECT_EQ(1u, memoryManager->getOsContextCount()); + auto allocation = memoryManager->allocateGraphicsMemory(MemoryConstants::pageSize); + allocation->updateTaskCount(1u, 0u); + *hwTag = 0u; + asyncDeleter->deferDeletion(new DeferrableAllocationDeletion(*memoryManager, *allocation)); + while (!asyncDeleter->queue.peekIsEmpty()) // wait for async thread to get allocation from queue + ; + + EXPECT_EQ(0u, memoryManager->freeGraphicsMemoryCalled); + EXPECT_TRUE(allocation->isUsedByContext(0u)); + + // let async thread exit + asyncDeleter->allowExit = true; + + *hwTag = 1u; // allow to destroy allocation + while (!asyncDeleter->shouldStopReached) + ; + EXPECT_EQ(1u, memoryManager->freeGraphicsMemoryCalled); +} + +TEST_F(DeferrableAllocationDeletionTest, givenAllocationUsedByTwoOsContextsWhenApplyDeletionThenWaitForBothContexts) { + std::unique_ptr secondDevice(Device::create(nullptr, device->getExecutionEnvironment(), 1u)); + EXPECT_EQ(2u, memoryManager->getOsContextCount()); + auto allocation = memoryManager->allocateGraphicsMemory(MemoryConstants::pageSize); + *hwTag = 0u; + *secondDevice->getTagAddress() = 1u; + allocation->updateTaskCount(1u, 0u); + allocation->updateTaskCount(1u, 1u); + EXPECT_TRUE(allocation->isUsedByContext(0u)); + EXPECT_TRUE(allocation->isUsedByContext(1u)); + EXPECT_EQ(0u, memoryManager->freeGraphicsMemoryCalled); + asyncDeleter->deferDeletion(new DeferrableAllocationDeletion(*memoryManager, *allocation)); + while (allocation->isUsedByContext(1u)) // wait for second context completion signal + ; + EXPECT_EQ(0u, memoryManager->freeGraphicsMemoryCalled); + asyncDeleter->allowExit = true; + *hwTag = 1u; +} +TEST_F(DeferrableAllocationDeletionTest, givenNotUsedAllocationWhenApplyDeletionThenDontWait) { + EXPECT_EQ(1u, memoryManager->getOsContextCount()); + auto allocation = memoryManager->allocateGraphicsMemory(MemoryConstants::pageSize); + EXPECT_FALSE(allocation->isUsed()); + EXPECT_EQ(0u, memoryManager->freeGraphicsMemoryCalled); + while (!asyncDeleter->doWorkInBackground) + ; //wait for start async thread work + std::unique_lock lock(asyncDeleter->queueMutex); + asyncDeleter->allowExit = true; + lock.unlock(); + asyncDeleter->deferDeletion(new DeferrableAllocationDeletion(*memoryManager, *allocation)); + while (!asyncDeleter->shouldStopReached) // wait async thread job end + ; + EXPECT_EQ(1u, memoryManager->freeGraphicsMemoryCalled); +} \ No newline at end of file diff --git a/unit_tests/mocks/mock_memory_manager.h b/unit_tests/mocks/mock_memory_manager.h index 10c7d734cd..38938e656d 100644 --- a/unit_tests/mocks/mock_memory_manager.h +++ b/unit_tests/mocks/mock_memory_manager.h @@ -41,6 +41,12 @@ class MockMemoryManager : public OsAgnosticMemoryManager { GraphicsAllocation *allocateGraphicsMemoryInDevicePool(const AllocationData &allocationData, AllocationStatus &status) override; GraphicsAllocation *allocateGraphicsMemory(size_t size, size_t alignment, bool forcePin, bool uncacheable) override; + void freeGraphicsMemoryImpl(GraphicsAllocation *gfxAllocation) override { + freeGraphicsMemoryCalled++; + OsAgnosticMemoryManager::freeGraphicsMemoryImpl(gfxAllocation); + }; + + uint32_t freeGraphicsMemoryCalled = 0u; bool allocationCreated = false; bool allocation64kbPageCreated = false; bool allocationInDevicePoolCreated = false;