impl/lola: Added shm-size calculation by analysis for DATA/CONTROL - #760
impl/lola: Added shm-size calculation by analysis for DATA/CONTROL#760crimson11 wants to merge 6 commits into
Conversation
c72a678 to
a090932
Compare
a090932 to
59cd98d
Compare
| enum class ShmSizeCalculationMode : std::uint8_t | ||
| { | ||
| kSimulation, | ||
| kEstimation, |
There was a problem hiding this comment.
This is no longer an estimation - this is now an calculation! We should be clear in the wording.
59cd98d to
e088dba
Compare
| return key_equal_; | ||
| } | ||
|
|
||
| mapped_type& at(const Key& key) |
There was a problem hiding this comment.
| mapped_type& at(const Key& key) | |
| mapped_type& at(const Key& key)& |
I think this should have the trailing & so that it can't be called on a temporary(would lead to a dangling reference).
There was a problem hiding this comment.
Hm. I'm not convinced ;) Why?
- we resemble a std::map interface here! map doesn't lvalue qualify at() neither. This confuses imho.
- then I would need to be consistent! Also would have to add it o plenty of other methods! Like begin()/end() etc. they are all returning iterators/pointers which may be dangling in case of a temporary ...
There was a problem hiding this comment.
There was some misra rule I believe about this. But I'm fine if you follow the std::map interface and we can revisit this if the warning pops up
| { | ||
| const auto field_it = lola_service_instance_deployment_.fields_.find(name); | ||
| SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(field_it != lola_service_instance_deployment_.fields_.cend(), | ||
| "Could not find field in deployment configuration."); |
There was a problem hiding this comment.
The message should not just be repeating what the check is in words. It should either be explaining why the assertion is checked / expected or just omit it. It's not adding any new information here. Same for the other assertions
| return total_size; | ||
| } | ||
|
|
||
| std::size_t SkeletonMemoryManager::GetNumberOfSampleSlotsFromConfig(const std::string_view service_element_name, |
There was a problem hiding this comment.
What about something like this to avoid code duplication?
template <ServiceElementType ServiceElementType>
std::size_t SkeletonMemoryManager::GetNumberOfSampleSlotsFromConfig(const std::string_view service_element_name) const
{
SCORE_LANGUAGE_FUTURECPP_PRECONDITION(ServiceElementType == ServiceElementType::EVENT ||
ServiceElementType == ServiceElementType::FIELD);
const auto& lola_service_element_deployment = [this, service_element_name]() {
if constexpr (ServiceElementType == ServiceElementType::EVENT)
{
return lola_service_instance_deployment_.events_.at(std::string{service_element_name});
}
else
{
return lola_service_instance_deployment_.fields_.at(std::string{service_element_name})
.lola_event_instance_deployment_;
}
}();
const std::string name{service_element_name};
const auto field_it = lola_service_element_deployment.fields_.find(name);
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(field_it != lola_service_instance_deployment_.fields_.cend(),
"Could not find field in deployment configuration.");
const auto number_of_slots = field_it->second.lola_event_instance_deployment_.GetNumberOfSampleSlots();
SCORE_LANGUAGE_FUTURECPP_ASSERT_PRD_MESSAGE(number_of_slots.has_value(),
"Number of sample slots not specified for field.");
return number_of_slots.value();
}
There was a problem hiding this comment.
Same for GetMaxSubscribersFromConfig
There was a problem hiding this comment.
Hm. I'm not convinced. Just compare the src-code sizes. It is not really much less source-code and the original was also "easy" to grasp imho. Now you are templating it and in reality we will have larger code-size, because we have now the impl. instantiated twice ... and as I said - the new impl. still has a cosnstexpr if-else, which makes the code not really simpler?
If you still insist -> I can change ;)
There was a problem hiding this comment.
My point wasn't really about number of lines, it was more about avoiding code duplication. We're currently duplicating the "algorithm" to extract number of slots from a deployment. So if we for example changed the structure of the deployments, we'd have to change it (correctly) in 2 places instead of 1. But it's such a small function, I'm fine if you leave it as is.
f8aab17 to
d04e2bf
Compare
Currently we determine shm-sizes of the CTRL and DATA shm-objects by a simulation run. I.e. we initialize the content of both section 1st within a heap-allocated resource. At the end we use the sizes to correctly size the shm-objects. This "SIMULATION" method is exact but has high runtime costs and might consume lots of memory during startup. This change now redesigns the containers/dynamic data types being used within the DATA section, to use only classes, which we are in control of and where we exactly know based on our configuration, who much size they will need. Thus the whole simulation canbe skipped and we calculate the size correctly from the configuration settins for the service instance. We re-introduce therefore the ESTIMATION mode in parallel to the SIMULATION mode.
Added analytical estimate/ANALYSIS mode for the shm-size calculation for the CONTROL section.
Adapted signature of LinearSearchMap to support custom KeqEqual just like std::unordered_map.
In the skeleton component tests we were testing the lola::Skeleton with a event and field service element. But we only provided the config/ deployment info for these elements, but did never register them at their parent Skeleton. Thus, essential tests checking the shm-size calculation were off! The tests now correctly register the elements.
Fixed review comments for ne ANALYSIS based size calculations. Added unit test for parsing of new ANALYSIS mode.
d04e2bf to
93af38c
Compare
| } // namespace | ||
|
|
||
| std::size_t CalculateServiceDataControlShmSize( | ||
| const score::cpp::span<const ServiceElementControlSizeInfo> service_elements_size_info) |
| } // namespace | ||
|
|
||
| std::size_t CalculateServiceDataStorageShmSize( | ||
| const score::cpp::span<const ServiceElementDataStorageSizeInfo> service_elements_size_info) |
Moved the shm-size calc for ServiceDataStorage and ServiceDataControl out of SkeletonMemoryManager to the correspoinding data structures itself.
93af38c to
798fdc0
Compare
|
|
||
| TEST_F(LinearSearchMapFixture, IsEmptyAfterConstruction) | ||
| { | ||
| // Given a freshly constructed LinearSearchMap with a capacity of 4 |
There was a problem hiding this comment.
| // Given a freshly constructed LinearSearchMap with a capacity of 4 |
| { | ||
| // Given a freshly constructed LinearSearchMap with a capacity of 4 | ||
|
|
||
| // When constructing the map |
There was a problem hiding this comment.
When constructing a LinearSearchMap with a capacity of 4
| TestMap unit{4U, memory_}; | ||
|
|
||
| // When emplacing a new key/value pair | ||
| const auto result = unit.emplace(1, 100); |
There was a problem hiding this comment.
We should minimise the use of magic numbers which are reused. The key and value should be variables which are reused in the Then section.
| // When searching via the const overload of find | ||
| const auto it = const_unit.find(5); | ||
|
|
||
| // Then an existing key is found and a missing key yields the const end iterator |
There was a problem hiding this comment.
| // Then an existing key is found and a missing key yields the const end iterator | |
| // Then an existing key is found |
| // Then the returned predicate exhibits the custom equality semantics | ||
| EXPECT_TRUE(predicate(-7, 7)); | ||
| EXPECT_FALSE(predicate(-7, 8)); | ||
| } |
There was a problem hiding this comment.
What do we want to happen when we try to emplace more elements than we reserved capacity for? I guess that it would have to terminate because otherwise emplace would return false but what iterator to return? I think this should be specified in the header docs and also probably should have a test here. I know the behaviour is implemented in NonRelocatableVector, but IMO, this is an important variant of this class which should be documented and tested.
There was a problem hiding this comment.
Added test and doc.
| return total_size; | ||
| } | ||
|
|
||
| std::size_t SkeletonMemoryManager::GetNumberOfSampleSlotsFromConfig(const std::string_view service_element_name, |
There was a problem hiding this comment.
My point wasn't really about number of lines, it was more about avoiding code duplication. We're currently duplicating the "algorithm" to extract number of slots from a deployment. So if we for example changed the structure of the deployments, we'd have to change it (correctly) in 2 places instead of 1. But it's such a small function, I'm fine if you leave it as is.
| return key_equal_; | ||
| } | ||
|
|
||
| mapped_type& at(const Key& key) |
There was a problem hiding this comment.
There was some misra rule I believe about this. But I'm fine if you follow the std::map interface and we can revisit this if the warning pops up
| number_of_service_elements * sizeof(ServiceDataStorage::EventMetaInfoMap::value_type), kMaxAlign); | ||
|
|
||
| // The size of the EventDataStorage control structure (a DynamicArray) is independent of the concrete sample-type | ||
| // (it only holds an offset-pointer, an allocator and two size_t members). |
There was a problem hiding this comment.
We're making a lot of assumptions here about the internals of a DynamicArray. Would it make sense that this is added to the DynamicArray itself?
There was a problem hiding this comment.
Is this our discussion regarding splitting up/making a recursive approach for CalculateServiceDataStorageShmSize ? I.e. also implement a corresponding calculate-method in DynamicArray ?
This we could do, but I would like to postpone it after we have switched to type-erased storage for the binding ... as it will change a lot again.
| const std::string name{service_element_name}; | ||
| if (is_field) | ||
| { | ||
| const auto field_it = lola_service_instance_deployment_.fields_.find(name); |
There was a problem hiding this comment.
You can actually use GetServiceElementInstanceDeployment from score/mw/com/impl/configuration/lola_service_instance_deployment.h here.
| "Number of sample slots not specified for field."); | ||
| return number_of_slots.value(); | ||
| } | ||
| const auto event_it = lola_service_instance_deployment_.events_.find(name); |
There was a problem hiding this comment.
You can actually use GetServiceElementInstanceDeployment from score/mw/com/impl/configuration/lola_service_instance_deployment.h here.
| /// service-elements (events + fields), which is the fixed capacity the event_controls_ container is constructed | ||
| /// with. | ||
| /// \return needed size (in bytes) for a single control shm-object. | ||
| std::size_t CalculateServiceDataControlShmSize( |
There was a problem hiding this comment.
We're missing tests for this?
| /// service-elements (events + fields), which is the fixed capacity the ServiceDataStorage containers are | ||
| /// constructed with. | ||
| /// \return needed size (in bytes) for the data shm-object. | ||
| std::size_t CalculateServiceDataStorageShmSize( |
There was a problem hiding this comment.
We're missing tests for this?
Currently we determine shm-sizes of the
CTRL and DATA shm-objects by a simulation run.
I.e. we initialize the content of both section
1st within a heap-allocated resource. At the end
we use the sizes to correctly size the shm-objects. This "SIMULATION" method is exact but has high runtime costs and might consume lots of memory during startup.
This change now redesigns the containers/dynamic data types being used within the DATA section, to use only classes, which we are in control of and where we exactly know based on our configuration, who much size they will need. Thus the whole simulation canbe skipped and we calculate the size correctly from the configuration settins for the service instance.
We re-introduce therefore the ESTIMATION mode in parallel to the SIMULATION mode.
Tackles issue #761