// // Copyright © 2017 Arm Ltd. All rights reserved. // SPDX-License-Identifier: MIT // #include "CounterDirectory.hpp" #include "ProfilingUtils.hpp" #include #include #include namespace armnn { namespace profiling { const Category* CounterDirectory::RegisterCategory(const std::string& categoryName, const Optional& deviceUid, const Optional& counterSetUid) { // Check that the given category name is valid if (categoryName.empty() || !IsValidSwTraceString(categoryName)) { throw InvalidArgumentException("Trying to register a category with an invalid name"); } // Check that the given category is not already registered if (CheckIfCategoryIsRegistered(categoryName)) { throw InvalidArgumentException( boost::str(boost::format("Trying to register a category already registered (\"%1%\")") % categoryName)); } // Check that a device with the given (optional) UID is already registered uint16_t deviceUidValue = deviceUid.has_value() ? deviceUid.value() : 0; if (deviceUidValue > 0) { // Check that the (optional) device is already registered if (!CheckIfDeviceIsRegistered(deviceUidValue)) { throw InvalidArgumentException( boost::str(boost::format("Trying to connect a category (\"%1%\") to a device that is " "not registered (UID %2%)") % categoryName % deviceUidValue)); } } // Check that a counter set with the given (optional) UID is already registered uint16_t counterSetUidValue = counterSetUid.has_value() ? counterSetUid.value() : 0; if (counterSetUidValue > 0) { // Check that the (optional) counter set is already registered if (!CheckIfCounterSetIsRegistered(counterSetUidValue)) { throw InvalidArgumentException( boost::str(boost::format("Trying to connect a category (name: \"%1%\") to a counter set " "that is not registered (UID: %2%)") % categoryName % counterSetUidValue)); } } // Create the category CategoryPtr category = std::make_unique(categoryName, deviceUidValue, counterSetUidValue); BOOST_ASSERT(category); // Get the raw category pointer const Category* categoryPtr = category.get(); BOOST_ASSERT(categoryPtr); // Register the category m_Categories.insert(std::move(category)); return categoryPtr; } const Device* CounterDirectory::RegisterDevice(const std::string& deviceName, uint16_t cores, const Optional& parentCategoryName) { // Check that the given device name is valid if (deviceName.empty() || !IsValidSwTraceString(deviceName)) { throw InvalidArgumentException("Trying to register a device with an invalid name"); } // Check that a device with the given name is not already registered if (CheckIfDeviceIsRegistered(deviceName)) { throw InvalidArgumentException( boost::str(boost::format("Trying to register a device already registered (\"%1%\")") % deviceName)); } // Peek the next UID, do not get an actual valid UID just now as we don't want to waste a good UID in case // the registration fails. We'll get a proper one once we're sure that the device can be registered uint16_t deviceUidPeek = GetNextUid(true); // Check that a category with the given (optional) parent category name is already registered Category* parentCategoryPtr = nullptr; if (parentCategoryName.has_value()) { // Get the (optional) parent category name const std::string& parentCategoryNameValue = parentCategoryName.value(); if (parentCategoryNameValue.empty()) { throw InvalidArgumentException( boost::str(boost::format("Trying to connect a device (name: \"%1%\") to an invalid " "parent category (name: \"%2%\")") % deviceName % parentCategoryNameValue)); } // Check that the given parent category is already registered auto categoryIt = FindCategory(parentCategoryNameValue); if (categoryIt == m_Categories.end()) { throw InvalidArgumentException( boost::str(boost::format("Trying to connect a device (name: \"%1%\") to a parent category that " "is not registered (name: \"%2%\")") % deviceName % parentCategoryNameValue)); } // Get the parent category const CategoryPtr& parentCategory = *categoryIt; BOOST_ASSERT(parentCategory); // Check that the given parent category is not already connected to another device if (parentCategory->m_DeviceUid != 0 && parentCategory->m_DeviceUid != deviceUidPeek) { throw InvalidArgumentException( boost::str(boost::format("Trying to connect a device (UID: %1%) to a parent category that is " "already connected to a different device " "(category \"%2%\" connected to device %3%)") % deviceUidPeek % parentCategoryNameValue % parentCategory->m_DeviceUid)); } // The parent category can be associated to the device that is about to be registered. // Get the raw pointer to the parent category (to be used later when the device is actually been // registered, to make sure that the category is associated to an existing device) parentCategoryPtr = parentCategory.get(); } // Get the device UID uint16_t deviceUid = GetNextUid(); BOOST_ASSERT(deviceUid == deviceUidPeek); // Create the device DevicePtr device = std::make_unique(deviceUid, deviceName, cores); BOOST_ASSERT(device); // Get the raw device pointer const Device* devicePtr = device.get(); BOOST_ASSERT(devicePtr); // Register the device m_Devices.insert(std::make_pair(deviceUid, std::move(device))); // Connect the device to the parent category, if required if (parentCategoryPtr) { // Set the device UID in the parent category parentCategoryPtr->m_DeviceUid = deviceUid; } return devicePtr; } const CounterSet* CounterDirectory::RegisterCounterSet(const std::string& counterSetName, uint16_t count, const Optional& parentCategoryName) { // Check that the given counter set name is valid if (counterSetName.empty() || !IsValidSwTraceString(counterSetName)) { throw InvalidArgumentException("Trying to register a counter set with an invalid name"); } // Check that a counter set with the given name is not already registered if (CheckIfCounterSetIsRegistered(counterSetName)) { throw InvalidArgumentException( boost::str(boost::format("Trying to register a counter set already registered (\"%1%\")") % counterSetName)); } // Peek the next UID, do not get an actual valid UID just now as we don't want to waste a good UID in case // the registration fails. We'll get a proper one once we're sure that the counter set can be registered uint16_t counterSetUidPeek = GetNextUid(true); // Check that a category with the given (optional) parent category name is already registered Category* parentCategoryPtr = nullptr; if (parentCategoryName.has_value()) { // Get the (optional) parent category name const std::string& parentCategoryNameValue = parentCategoryName.value(); if (parentCategoryNameValue.empty()) { throw InvalidArgumentException( boost::str(boost::format("Trying to connect a counter set (UID: %1%) to an invalid " "parent category (name: \"%2%\")") % counterSetUidPeek % parentCategoryNameValue)); } // Check that the given parent category is already registered auto it = FindCategory(parentCategoryNameValue); if (it == m_Categories.end()) { throw InvalidArgumentException( boost::str(boost::format("Trying to connect a counter set (UID: %1%) to a parent category " "that is not registered (name: \"%2%\")") % counterSetUidPeek % parentCategoryNameValue)); } // Get the parent category const CategoryPtr& parentCategory = *it; BOOST_ASSERT(parentCategory); // Check that the given parent category is not already connected to another counter set if (parentCategory->m_CounterSetUid != 0 && parentCategory->m_CounterSetUid != counterSetUidPeek) { throw InvalidArgumentException( boost::str(boost::format("Trying to connect a counter set (UID: %1%) to a parent category " "that is already connected to a different counter set " "(category \"%2%\" connected to counter set %3%)") % counterSetUidPeek % parentCategoryNameValue % parentCategory->m_CounterSetUid)); } // The parent category can be associated to the counter set that is about to be registered. // Get the raw pointer to the parent category (to be used later when the counter set is actually been // registered, to make sure that the category is associated to an existing counter set) parentCategoryPtr = parentCategory.get(); } // Get the counter set UID uint16_t counterSetUid = GetNextUid(); BOOST_ASSERT(counterSetUid == counterSetUidPeek); // Create the counter set CounterSetPtr counterSet = std::make_unique(counterSetUid, counterSetName, count); BOOST_ASSERT(counterSet); // Get the raw counter set pointer const CounterSet* counterSetPtr = counterSet.get(); BOOST_ASSERT(counterSetPtr); // Register the counter set m_CounterSets.insert(std::make_pair(counterSetUid, std::move(counterSet))); // Connect the counter set to the parent category, if required if (parentCategoryPtr) { // Set the counter set UID in the parent category parentCategoryPtr->m_CounterSetUid = counterSetUid; } return counterSetPtr; } const Counter* CounterDirectory::RegisterCounter(const std::string& parentCategoryName, uint16_t counterClass, uint16_t interpolation, double multiplier, const std::string& name, const std::string& description, const Optional& units, const Optional& numberOfCores, const Optional& deviceUid, const Optional& counterSetUid) { // Check that the given parent category name is valid if (parentCategoryName.empty() || !IsValidSwTraceString(parentCategoryName)) { throw InvalidArgumentException("Trying to register a counter with an invalid parent category name"); } // Check that the given class is valid if (counterClass != 0 && counterClass != 1) { throw InvalidArgumentException("Trying to register a counter with an invalid class"); } // Check that the given interpolation is valid if (interpolation != 0 && interpolation != 1) { throw InvalidArgumentException("Trying to register a counter with an invalid interpolation"); } // Check that the given multiplier is valid if (multiplier == .0f) { throw InvalidArgumentException("Trying to register a counter with an invalid multiplier"); } // Check that the given name is valid if (name.empty() || !IsValidSwTraceString(name)) { throw InvalidArgumentException("Trying to register a counter with an invalid name"); } // Check that the given description is valid if (description.empty() || !IsValidSwTraceString(description)) { throw InvalidArgumentException("Trying to register a counter with an invalid description"); } // Check that the given units are valid if (units.has_value() && !IsValidSwTraceString(units.value())) { throw InvalidArgumentException("Trying to register a counter with a invalid units"); } // Check that the given parent category is registered auto categoryIt = FindCategory(parentCategoryName); if (categoryIt == m_Categories.end()) { throw InvalidArgumentException( boost::str(boost::format("Trying to connect a counter to a category " "that is not registered (name: \"%1%\")") % parentCategoryName)); } // Get the parent category const CategoryPtr& parentCategory = *categoryIt; BOOST_ASSERT(parentCategory); // Check that a counter with the given name is not already registered within the parent category const std::vector& parentCategoryCounters = parentCategory->m_Counters; for (uint16_t parentCategoryCounterUid : parentCategoryCounters) { const Counter* parentCategoryCounter = GetCounter(parentCategoryCounterUid); BOOST_ASSERT(parentCategoryCounter); if (parentCategoryCounter->m_Name == name) { throw InvalidArgumentException( boost::str(boost::format("Trying to register a counter to category \"%1%\" with a name that " "is already used within that category (name: \"%2%\")") % parentCategoryName % name)); } } // Check that a counter set with the given (optional) UID is already registered uint16_t counterSetUidValue = counterSetUid.has_value() ? counterSetUid.value() : 0; if (counterSetUidValue > 0) { // Check that the (optional) counter set is already registered if (!CheckIfCounterSetIsRegistered(counterSetUidValue)) { throw InvalidArgumentException( boost::str(boost::format("Trying to connect a counter to a counter set that is " "not registered (counter set UID: %1%)") % counterSetUidValue)); } } // Get the number of cores (this call may throw) uint16_t deviceUidValue = deviceUid.has_value() ? deviceUid.value() : 0; uint16_t deviceCores = GetNumberOfCores(numberOfCores, deviceUidValue, parentCategory); // Get the counter UIDs and calculate the max counter UID std::vector counterUids = GetNextCounterUids(deviceCores); BOOST_ASSERT(!counterUids.empty()); uint16_t maxCounterUid = deviceCores <= 1 ? counterUids.front() : counterUids.back(); // Get the counter units const std::string unitsValue = units.has_value() ? units.value() : ""; // Create the counter CounterPtr counter = std::make_shared(counterUids.front(), maxCounterUid, counterClass, interpolation, multiplier, name, description, unitsValue, deviceUidValue, counterSetUidValue); BOOST_ASSERT(counter); // Get the raw counter pointer const Counter* counterPtr = counter.get(); BOOST_ASSERT(counterPtr); // Process multiple counters if necessary for (uint16_t counterUid : counterUids) { // Connect the counter to the parent category parentCategory->m_Counters.push_back(counterUid); // Register the counter m_Counters.insert(std::make_pair(counterUid, counter)); } return counterPtr; } const Category* CounterDirectory::GetCategory(const std::string& categoryName) const { auto it = FindCategory(categoryName); if (it == m_Categories.end()) { return nullptr; } const Category* category = it->get(); BOOST_ASSERT(category); return category; } const Device* CounterDirectory::GetDevice(uint16_t deviceUid) const { auto it = FindDevice(deviceUid); if (it == m_Devices.end()) { return nullptr; } const Device* device = it->second.get(); BOOST_ASSERT(device); BOOST_ASSERT(device->m_Uid == deviceUid); return device; } const CounterSet* CounterDirectory::GetCounterSet(uint16_t counterSetUid) const { auto it = FindCounterSet(counterSetUid); if (it == m_CounterSets.end()) { return nullptr; } const CounterSet* counterSet = it->second.get(); BOOST_ASSERT(counterSet); BOOST_ASSERT(counterSet->m_Uid == counterSetUid); return counterSet; } const Counter* CounterDirectory::GetCounter(uint16_t counterUid) const { auto it = FindCounter(counterUid); if (it == m_Counters.end()) { return nullptr; } const Counter* counter = it->second.get(); BOOST_ASSERT(counter); BOOST_ASSERT(counter->m_Uid <= counterUid); BOOST_ASSERT(counter->m_Uid <= counter->m_MaxCounterUid); return counter; } CategoriesIt CounterDirectory::FindCategory(const std::string& categoryName) const { return std::find_if(m_Categories.begin(), m_Categories.end(), [&categoryName](const CategoryPtr& category) { BOOST_ASSERT(category); return category->m_Name == categoryName; }); } DevicesIt CounterDirectory::FindDevice(uint16_t deviceUid) const { return m_Devices.find(deviceUid); } DevicesIt CounterDirectory::FindDevice(const std::string& deviceName) const { return std::find_if(m_Devices.begin(), m_Devices.end(), [&deviceName](const auto& pair) { BOOST_ASSERT(pair.second); BOOST_ASSERT(pair.second->m_Uid == pair.first); return pair.second->m_Name == deviceName; }); } CounterSetsIt CounterDirectory::FindCounterSet(uint16_t counterSetUid) const { return m_CounterSets.find(counterSetUid); } CounterSetsIt CounterDirectory::FindCounterSet(const std::string& counterSetName) const { return std::find_if(m_CounterSets.begin(), m_CounterSets.end(), [&counterSetName](const auto& pair) { BOOST_ASSERT(pair.second); BOOST_ASSERT(pair.second->m_Uid == pair.first); return pair.second->m_Name == counterSetName; }); } CountersIt CounterDirectory::FindCounter(uint16_t counterUid) const { return m_Counters.find(counterUid); } bool CounterDirectory::CheckIfCategoryIsRegistered(const std::string& categoryName) const { auto it = FindCategory(categoryName); return it != m_Categories.end(); } bool CounterDirectory::CheckIfDeviceIsRegistered(uint16_t deviceUid) const { auto it = FindDevice(deviceUid); return it != m_Devices.end(); } bool CounterDirectory::CheckIfDeviceIsRegistered(const std::string& deviceName) const { auto it = FindDevice(deviceName); return it != m_Devices.end(); } bool CounterDirectory::CheckIfCounterSetIsRegistered(uint16_t counterSetUid) const { auto it = FindCounterSet(counterSetUid); return it != m_CounterSets.end(); } bool CounterDirectory::CheckIfCounterSetIsRegistered(const std::string& counterSetName) const { auto it = FindCounterSet(counterSetName); return it != m_CounterSets.end(); } uint16_t CounterDirectory::GetNumberOfCores(const Optional& numberOfCores, uint16_t deviceUid, const CategoryPtr& parentCategory) { BOOST_ASSERT(parentCategory); // To get the number of cores, apply the following rules: // // 1. If numberOfCores is set then take it as the deviceCores value // 2. If numberOfCores is not set then check to see if this counter is directly associated with a device, // if so then that devices number of cores is taken as the deviceCores value // 3. If neither of the above is set then look at the category to see if it has a device associated with it, // if it does then take that device's numberOfCores as the deviceCores value // 4. If none of the above holds then set deviceCores to zero // 1. If numberOfCores is set then take it as the deviceCores value if (numberOfCores.has_value()) { // Get the number of cores return numberOfCores.value(); } // 2. If numberOfCores is not set then check to see if this counter is directly associated with a device, // if so then that devices number of cores is taken as the deviceCores value if (deviceUid > 0) { // Check that the (optional) device is already registered auto deviceIt = FindDevice(deviceUid); if (deviceIt == m_Devices.end()) { throw InvalidArgumentException( boost::str(boost::format("Trying to connect a counter to a device that is " "not registered (device UID %1%)") % deviceUid)); } // Get the associated device const DevicePtr& device = deviceIt->second; BOOST_ASSERT(device); // Get the number of cores of the associated device return device->m_Cores; } // 3. If neither of the above is set then look at the category to see if it has a device associated with it, // if it does then take that device's numberOfCores as the deviceCores value uint16_t parentCategoryDeviceUid = parentCategory->m_DeviceUid; if (parentCategoryDeviceUid > 0) { // Check that the device associated to the parent category is already registered auto deviceIt = FindDevice(parentCategoryDeviceUid); if (deviceIt == m_Devices.end()) { throw InvalidArgumentException( boost::str(boost::format("Trying to get the number of cores from a device that is " "not registered (device UID %1%)") % parentCategoryDeviceUid)); } // Get the associated device const DevicePtr& device = deviceIt->second; BOOST_ASSERT(device); // Get the number of cores of the device associated to the parent category return device->m_Cores; } // 4. If none of the above holds then set deviceCores to zero return 0; } } // namespace profiling } // namespace armnn