From 7be47efac07b6276f02a17cb486f9061a426a837 Mon Sep 17 00:00:00 2001 From: Narumol Prangnawarat Date: Fri, 27 Sep 2019 18:00:11 +0100 Subject: IVGCVSW-3903 Create Counter Stream Buffer * Add implementation of PacketBuffer * Add implementation of BufferManager * Unit tests Signed-off-by: Narumol Prangnawarat Change-Id: Icca3807149ff5a8ed31cc87de73c50b95bca39d4 --- CMakeLists.txt | 5 + src/profiling/BufferManager.cpp | 93 +++++++++ src/profiling/BufferManager.hpp | 59 ++++++ src/profiling/PacketBuffer.cpp | 60 ++++++ src/profiling/PacketBuffer.hpp | 45 +++++ src/profiling/test/BufferTests.cpp | 279 ++++++++++++++++++++++++++ src/profiling/test/SendCounterPacketTests.hpp | 4 +- 7 files changed, 543 insertions(+), 2 deletions(-) create mode 100644 src/profiling/BufferManager.cpp create mode 100644 src/profiling/BufferManager.hpp create mode 100644 src/profiling/PacketBuffer.cpp create mode 100644 src/profiling/PacketBuffer.hpp create mode 100644 src/profiling/test/BufferTests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 11afe7ad17..6ae352d94c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -424,6 +424,8 @@ list(APPEND armnn_sources src/armnn/optimizations/PermuteAndBatchToSpaceAsDepthToSpace.cpp src/armnn/optimizations/PermuteAsReshape.hpp src/armnn/optimizations/SquashEqualSiblings.hpp + src/profiling/BufferManager.cpp + src/profiling/BufferManager.hpp src/profiling/CommandHandlerFunctor.cpp src/profiling/CommandHandlerFunctor.hpp src/profiling/CommandHandlerKey.cpp @@ -449,6 +451,8 @@ list(APPEND armnn_sources src/profiling/IReadCounterValue.hpp src/profiling/Packet.cpp src/profiling/Packet.hpp + src/profiling/PacketBuffer.cpp + src/profiling/PacketBuffer.hpp src/profiling/PacketVersionResolver.cpp src/profiling/PacketVersionResolver.hpp src/profiling/PeriodicCounterCapture.hpp @@ -590,6 +594,7 @@ if(BUILD_UNIT_TESTS) src/armnnUtils/test/PrototxtConversionsTest.cpp src/armnnUtils/test/ParserHelperTest.cpp src/armnnUtils/test/TensorUtilsTest.cpp + src/profiling/test/BufferTests.cpp src/profiling/test/ProfilingConnectionDumpToFileDecoratorTests.cpp src/profiling/test/ProfilingTests.cpp src/profiling/test/SendCounterPacketTests.cpp diff --git a/src/profiling/BufferManager.cpp b/src/profiling/BufferManager.cpp new file mode 100644 index 0000000000..91ad1c5f3a --- /dev/null +++ b/src/profiling/BufferManager.cpp @@ -0,0 +1,93 @@ +// +// Copyright © 2019 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "BufferManager.hpp" +#include "PacketBuffer.hpp" +#include "ProfilingUtils.hpp" + +#include + +namespace armnn +{ + +namespace profiling +{ + +BufferManager::BufferManager(unsigned int numberOfBuffers, unsigned int maxPacketSize) + : m_MaxBufferSize(maxPacketSize) +{ + m_AvailableList.reserve(numberOfBuffers); + for (unsigned int i = 0; i < numberOfBuffers; ++i) + { + std::unique_ptr buffer = std::make_unique(maxPacketSize); + m_AvailableList.emplace_back(std::move(buffer)); + } + m_ReadableList.reserve(numberOfBuffers); +} + +std::unique_ptr BufferManager::Reserve(unsigned int requestedSize, unsigned int& reservedSize) +{ + std::unique_lock availableListLock(m_AvailableMutex, std::defer_lock); + if (requestedSize > m_MaxBufferSize) + { + throw armnn::RuntimeException("Maximum buffer size that can be requested is [" + + std::to_string(m_MaxBufferSize) + "] bytes"); + } + availableListLock.lock(); + if (m_AvailableList.empty()) + { + throw armnn::profiling::BufferExhaustion("Buffer not available"); + } + std::unique_ptr buffer = std::move(m_AvailableList.back()); + m_AvailableList.pop_back(); + availableListLock.unlock(); + reservedSize = requestedSize; + return buffer; +} + +void BufferManager::Commit(std::unique_ptr& packetBuffer, unsigned int size) +{ + std::unique_lock readableListLock(m_ReadableMutex, std::defer_lock); + packetBuffer->Commit(size); + readableListLock.lock(); + m_ReadableList.push_back(std::move(packetBuffer)); + readableListLock.unlock(); + m_ReadDataAvailable.notify_one(); +} + +void BufferManager::Release(std::unique_ptr& packetBuffer) +{ + std::unique_lock availableListLock(m_AvailableMutex, std::defer_lock); + packetBuffer->Release(); + availableListLock.lock(); + m_AvailableList.push_back(std::move(packetBuffer)); + availableListLock.unlock(); +} + +std::unique_ptr BufferManager::GetReadableBuffer() +{ + std::unique_lock readableListLock(m_ReadableMutex); + if (!m_ReadableList.empty()) + { + std::unique_ptr buffer = std::move(m_ReadableList.back()); + m_ReadableList.pop_back(); + readableListLock.unlock(); + return buffer; + } + return nullptr; +} + +void BufferManager::MarkRead(std::unique_ptr& packetBuffer) +{ + std::unique_lock availableListLock(m_AvailableMutex, std::defer_lock); + packetBuffer->MarkRead(); + availableListLock.lock(); + m_AvailableList.push_back(std::move(packetBuffer)); + availableListLock.unlock(); +} + +} // namespace profiling + +} // namespace armnn diff --git a/src/profiling/BufferManager.hpp b/src/profiling/BufferManager.hpp new file mode 100644 index 0000000000..04a1507512 --- /dev/null +++ b/src/profiling/BufferManager.hpp @@ -0,0 +1,59 @@ +// +// Copyright © 2019 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include "IBufferManager.hpp" + +#include +#include +#include + +namespace armnn +{ + +namespace profiling +{ + +class BufferManager : public IBufferManager +{ +public: + BufferManager(unsigned int numberOfBuffers = 5, unsigned int maxPacketSize = 4096); + + ~BufferManager() {} + + std::unique_ptr Reserve(unsigned int requestedSize, unsigned int& reservedSize) override; + + void Commit(std::unique_ptr& packetBuffer, unsigned int size) override; + + void Release(std::unique_ptr& packetBuffer) override; + + std::unique_ptr GetReadableBuffer() override; + + void MarkRead(std::unique_ptr& packetBuffer) override; + +private: + // Maximum buffer size + unsigned int m_MaxBufferSize; + + // List of available packet buffers + std::vector> m_AvailableList; + + // List of readable packet buffers + std::vector> m_ReadableList; + + // Mutex for available packet buffer list + std::mutex m_AvailableMutex; + + // Mutex for readable packet buffer list + std::mutex m_ReadableMutex; + + // Condition to notify when data is availabe to be read + std::condition_variable m_ReadDataAvailable; +}; + +} // namespace profiling + +} // namespace armnn diff --git a/src/profiling/PacketBuffer.cpp b/src/profiling/PacketBuffer.cpp new file mode 100644 index 0000000000..88133d7674 --- /dev/null +++ b/src/profiling/PacketBuffer.cpp @@ -0,0 +1,60 @@ +// +// Copyright © 2019 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "PacketBuffer.hpp" + +#include + +namespace armnn +{ + +namespace profiling +{ + +PacketBuffer::PacketBuffer(unsigned int maxSize) + : m_MaxSize(maxSize) + , m_Size(0) +{ + m_Data = std::make_unique(m_MaxSize); +} + +const unsigned char* const PacketBuffer::GetReadableData() const +{ + return m_Data.get(); +} + +unsigned int PacketBuffer::GetSize() const +{ + return m_Size; +} + +void PacketBuffer::MarkRead() +{ + m_Size = 0; +} + +void PacketBuffer::Commit(unsigned int size) +{ + if (size > m_MaxSize) + { + throw armnn::RuntimeException("Cannot commit [" + std::to_string(size) + + "] bytes which is more than the maximum size of the buffer [" + std::to_string(m_MaxSize) + "]"); + } + m_Size = size; +} + +void PacketBuffer::Release() +{ + m_Size = 0; +} + +unsigned char* PacketBuffer::GetWritableData() +{ + return m_Data.get(); +} + +} // namespace profiling + +} // namespace armnn diff --git a/src/profiling/PacketBuffer.hpp b/src/profiling/PacketBuffer.hpp new file mode 100644 index 0000000000..a3d95d4108 --- /dev/null +++ b/src/profiling/PacketBuffer.hpp @@ -0,0 +1,45 @@ +// +// Copyright © 2019 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include "IPacketBuffer.hpp" + +#include + +namespace armnn +{ + +namespace profiling +{ + +class PacketBuffer : public IPacketBuffer +{ +public: + PacketBuffer(unsigned int maxSize); + + ~PacketBuffer() {} + + const unsigned char* const GetReadableData() const override; + + unsigned int GetSize() const override; + + void MarkRead() override; + + void Commit(unsigned int size) override; + + void Release() override; + + unsigned char* GetWritableData() override; + +private: + unsigned int m_MaxSize; + unsigned int m_Size; + std::unique_ptr m_Data; +}; + +} // namespace profiling + +} // namespace armnn \ No newline at end of file diff --git a/src/profiling/test/BufferTests.cpp b/src/profiling/test/BufferTests.cpp new file mode 100644 index 0000000000..b678350bb3 --- /dev/null +++ b/src/profiling/test/BufferTests.cpp @@ -0,0 +1,279 @@ +// +// Copyright © 2019 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "BufferManager.hpp" +#include "PacketBuffer.hpp" +#include "ProfilingUtils.hpp" + +#include + +#include + +using namespace armnn::profiling; + +BOOST_AUTO_TEST_SUITE(BufferTests) + +BOOST_AUTO_TEST_CASE(PacketBufferTest0) +{ + std::unique_ptr packetBuffer = std::make_unique(512); + + BOOST_TEST(packetBuffer->GetSize() == 0); + + // Write data to the buffer + WriteUint32(packetBuffer, 0, 10); + WriteUint32(packetBuffer, 4, 20); + WriteUint32(packetBuffer, 8, 30); + WriteUint32(packetBuffer, 12, 40); + + // Commit + packetBuffer->Commit(16); + + // Size of buffer is equal to committed data + BOOST_TEST(packetBuffer->GetSize() == 16); + + // Read data from the buffer + auto readBuffer = packetBuffer->GetReadableData(); + uint32_t readData0 = ReadUint32(readBuffer, 0); + uint32_t readData1 = ReadUint32(readBuffer, 4); + uint32_t readData2 = ReadUint32(readBuffer, 8); + uint32_t readData3 = ReadUint32(readBuffer, 12); + + // Check that data is correct + BOOST_TEST(readData0 == 10); + BOOST_TEST(readData1 == 20); + BOOST_TEST(readData2 == 30); + BOOST_TEST(readData3 == 40); + + // Mark read + packetBuffer->MarkRead(); + + // Size of buffer become 0 after marked read + BOOST_TEST(packetBuffer->GetSize() == 0); +} + +BOOST_AUTO_TEST_CASE(PacketBufferTest1) +{ + std::unique_ptr packetBuffer = std::make_unique(512); + + BOOST_TEST(packetBuffer->GetSize() == 0); + + // Write data to the buffer using GetWritableData + auto writeBuffer = packetBuffer->GetWritableData(); + WriteUint32(writeBuffer, 0, 10); + WriteUint32(writeBuffer, 4, 20); + WriteUint32(writeBuffer, 8, 30); + WriteUint32(writeBuffer, 12, 40); + + packetBuffer->Commit(16); + + BOOST_TEST(packetBuffer->GetSize() == 16); + + // Read data from the buffer + auto readBuffer = packetBuffer->GetReadableData(); + uint32_t readData0 = ReadUint32(readBuffer, 0); + uint32_t readData1 = ReadUint32(readBuffer, 4); + uint32_t readData2 = ReadUint32(readBuffer, 8); + uint32_t readData3 = ReadUint32(readBuffer, 12); + + BOOST_TEST(readData0 == 10); + BOOST_TEST(readData1 == 20); + BOOST_TEST(readData2 == 30); + BOOST_TEST(readData3 == 40); + + packetBuffer->MarkRead(); + + BOOST_TEST(packetBuffer->GetSize() == 0); +} + +BOOST_AUTO_TEST_CASE(PacketBufferReleaseTest) { + std::unique_ptr packetBuffer = std::make_unique(512); + + BOOST_TEST(packetBuffer->GetSize() == 0); + + auto writeBuffer = packetBuffer->GetWritableData(); + + WriteUint32(writeBuffer, 0, 10); + WriteUint32(writeBuffer, 4, 20); + WriteUint32(writeBuffer, 8, 30); + WriteUint32(writeBuffer, 12, 40); + + packetBuffer->Release(); + + // Size of buffer become 0 after release + BOOST_TEST(packetBuffer->GetSize() == 0); +} + +BOOST_AUTO_TEST_CASE(PacketBufferCommitErrorTest) +{ + std::unique_ptr packetBuffer = std::make_unique(8); + + // Cannot commit data bigger than the max size of the buffer + BOOST_CHECK_THROW(packetBuffer->Commit(16);, armnn::RuntimeException); +} + +BOOST_AUTO_TEST_CASE(BufferReserveTest) +{ + BufferManager bufferManager(1, 512); + unsigned int reservedSize = 0; + auto packetBuffer = bufferManager.Reserve(512, reservedSize); + + // Successfully reserved the buffer with requested size + BOOST_TEST(reservedSize == 512); + BOOST_TEST(packetBuffer.get()); +} + +BOOST_AUTO_TEST_CASE(BufferReserveExceedingSpaceTest) +{ + BufferManager bufferManager(1, 512); + unsigned int reservedSize = 0; + + // Cannot reserve buffer bigger than maximum buffer size + BOOST_CHECK_THROW(bufferManager.Reserve(1024, reservedSize), armnn::RuntimeException); +} + +BOOST_AUTO_TEST_CASE(BufferExhaustionTest) +{ + BufferManager bufferManager(1, 512); + unsigned int reservedSize = 0; + auto packetBuffer = bufferManager.Reserve(512, reservedSize); + + // Successfully reserved the buffer with requested size + BOOST_TEST(reservedSize == 512); + BOOST_TEST(packetBuffer.get()); + + // Cannot reserve buffer when buffer is not available + BOOST_CHECK_THROW(bufferManager.Reserve(512, reservedSize), BufferExhaustion); +} + +BOOST_AUTO_TEST_CASE(BufferReserveMultipleTest) +{ + BufferManager bufferManager(3, 512); + unsigned int reservedSize0 = 0; + auto packetBuffer0 = bufferManager.Reserve(512, reservedSize0); + + // Successfully reserved the buffer with requested size + BOOST_TEST(reservedSize0 == 512); + BOOST_TEST(packetBuffer0.get()); + + unsigned int reservedSize1 = 0; + auto packetBuffer1 = bufferManager.Reserve(128, reservedSize1); + + // Successfully reserved the buffer with requested size + BOOST_TEST(reservedSize1 == 128); + BOOST_TEST(packetBuffer1.get()); + + unsigned int reservedSize2 = 0; + auto packetBuffer2 = bufferManager.Reserve(512, reservedSize2); + + // Successfully reserved the buffer with requested size + BOOST_TEST(reservedSize2 == 512); + BOOST_TEST(packetBuffer2.get()); + + // Cannot reserve when buffer is not available + unsigned int reservedSize3 = 0; + BOOST_CHECK_THROW(bufferManager.Reserve(512, reservedSize3), BufferExhaustion); +} + +BOOST_AUTO_TEST_CASE(BufferReleaseTest) +{ + BufferManager bufferManager(2, 512); + unsigned int reservedSize0 = 0; + auto packetBuffer0 = bufferManager.Reserve(512, reservedSize0); + + // Successfully reserved the buffer with requested size + BOOST_TEST(reservedSize0 == 512); + BOOST_TEST(packetBuffer0.get()); + + unsigned int reservedSize1 = 0; + auto packetBuffer1 = bufferManager.Reserve(128, reservedSize1); + + // Successfully reserved the buffer with requested size + BOOST_TEST(reservedSize1 == 128); + BOOST_TEST(packetBuffer1.get()); + + // Cannot reserve when buffer is not available + unsigned int reservedSize2 = 0; + BOOST_CHECK_THROW(bufferManager.Reserve(512, reservedSize2), BufferExhaustion); + + bufferManager.Release(packetBuffer0); + + // Buffer should become available after release + auto packetBuffer2 = bufferManager.Reserve(128, reservedSize2); + + BOOST_TEST(reservedSize2 == 128); + BOOST_TEST(packetBuffer2.get()); +} + +BOOST_AUTO_TEST_CASE(BufferCommitTest) +{ + BufferManager bufferManager(2, 512); + unsigned int reservedSize0 = 0; + auto packetBuffer0 = bufferManager.Reserve(512, reservedSize0); + + BOOST_TEST(reservedSize0 == 512); + BOOST_TEST(packetBuffer0.get()); + + unsigned int reservedSize1 = 0; + auto packetBuffer1 = bufferManager.Reserve(128, reservedSize1); + + BOOST_TEST(reservedSize1 == 128); + BOOST_TEST(packetBuffer1.get()); + + unsigned int reservedSize2 = 0; + BOOST_CHECK_THROW(bufferManager.Reserve(512, reservedSize2), BufferExhaustion); + + bufferManager.Commit(packetBuffer0, 256); + + // Buffer should become readable after commit + auto packetBuffer2 = bufferManager.GetReadableBuffer(); + BOOST_TEST(packetBuffer2.get()); + BOOST_TEST(packetBuffer2->GetSize() == 256); + + // Buffer not set back to available list after commit + BOOST_CHECK_THROW(bufferManager.Reserve(512, reservedSize2), BufferExhaustion); +} + +BOOST_AUTO_TEST_CASE(BufferMarkReadTest) +{ + BufferManager bufferManager(2, 512); + unsigned int reservedSize0 = 0; + auto packetBuffer0 = bufferManager.Reserve(512, reservedSize0); + + BOOST_TEST(reservedSize0 == 512); + BOOST_TEST(packetBuffer0.get()); + + unsigned int reservedSize1 = 0; + auto packetBuffer1 = bufferManager.Reserve(128, reservedSize1); + + BOOST_TEST(reservedSize1 == 128); + BOOST_TEST(packetBuffer1.get()); + + // Cannot reserve when buffer is not available + unsigned int reservedSize2 = 0; + BOOST_CHECK_THROW(bufferManager.Reserve(512, reservedSize2), BufferExhaustion); + + bufferManager.Commit(packetBuffer0, 256); + + // Buffer should become readable after commit + auto packetBuffer2 = bufferManager.GetReadableBuffer(); + BOOST_TEST(packetBuffer2.get()); + BOOST_TEST(packetBuffer2->GetSize() == 256); + + // Buffer not set back to available list after commit + BOOST_CHECK_THROW(bufferManager.Reserve(512, reservedSize2), BufferExhaustion); + + bufferManager.MarkRead(packetBuffer2); + + //Buffer should set back to available list after marked read and can be reserved + auto readBuffer = bufferManager.GetReadableBuffer(); + BOOST_TEST(!readBuffer); + unsigned int reservedSize3 = 0; + auto packetBuffer3 = bufferManager.Reserve(56, reservedSize3); + + BOOST_TEST(reservedSize3 == 56); + BOOST_TEST(packetBuffer3.get()); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/profiling/test/SendCounterPacketTests.hpp b/src/profiling/test/SendCounterPacketTests.hpp index c3d47157d0..243731cb2c 100644 --- a/src/profiling/test/SendCounterPacketTests.hpp +++ b/src/profiling/test/SendCounterPacketTests.hpp @@ -147,10 +147,10 @@ public: { std::unique_lock availableListLock(m_AvailableMutex, std::defer_lock); if (requestedSize > m_MaxBufferSize) - { + { throw armnn::Exception("Maximum buffer size that can be requested is [" + std::to_string(m_MaxBufferSize) + "] bytes"); - } + } availableListLock.lock(); if (m_AvailableList.empty()) { -- cgit v1.2.1