Skip to content

impl/lola: Added shm-size calculation by analysis for DATA/CONTROL - #760

Open
crimson11 wants to merge 9 commits into
mainfrom
mf_new_shm_size_calculation
Open

impl/lola: Added shm-size calculation by analysis for DATA/CONTROL#760
crimson11 wants to merge 9 commits into
mainfrom
mf_new_shm_size_calculation

Conversation

@crimson11

@crimson11 crimson11 commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

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

@crimson11
crimson11 force-pushed the mf_new_shm_size_calculation branch from c72a678 to a090932 Compare July 23, 2026 21:01
@crimson11 crimson11 changed the title impl/lola: Added shm-size calc for CTRL impl/lola: Added shm-size calc by estimation for DATA/CONTROL Jul 23, 2026
@crimson11
crimson11 force-pushed the mf_new_shm_size_calculation branch from a090932 to 59cd98d Compare July 23, 2026 21:24
enum class ShmSizeCalculationMode : std::uint8_t
{
kSimulation,
kEstimation,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is no longer an estimation - this is now an calculation! We should be clear in the wording.

@crimson11
crimson11 force-pushed the mf_new_shm_size_calculation branch from 59cd98d to e088dba Compare July 24, 2026 09:46
@crimson11
crimson11 requested a review from castler July 24, 2026 09:57
@castler castler changed the title impl/lola: Added shm-size calc by estimation for DATA/CONTROL impl/lola: Added shm-size calculation for DATA/CONTROL Jul 27, 2026
@castler castler changed the title impl/lola: Added shm-size calculation for DATA/CONTROL impl/lola: Added shm-size calculation by analysis for DATA/CONTROL Jul 27, 2026
Comment thread score/mw/com/impl/bindings/lola/linear_search_map.h Outdated
Comment thread score/mw/com/impl/bindings/lola/linear_search_map.h Outdated
return key_equal_;
}

mapped_type& at(const Key& key)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 ...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread score/mw/com/impl/bindings/lola/linear_search_map.h
Comment thread score/mw/com/impl/bindings/lola/linear_search_map.h Outdated
Comment thread score/mw/com/impl/bindings/lola/skeleton_memory_manager.cpp Outdated
Comment thread score/mw/com/impl/bindings/lola/skeleton_memory_manager.cpp Outdated
Comment thread score/mw/com/impl/bindings/lola/skeleton_memory_manager.cpp Outdated
{
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.");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for GetMaxSubscribersFromConfig

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 ;)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will leave it.

@bemerybmw bemerybmw self-assigned this Jul 29, 2026
@crimson11
crimson11 force-pushed the mf_new_shm_size_calculation branch 5 times, most recently from f8aab17 to d04e2bf Compare August 1, 2026 16:18
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.
@crimson11
crimson11 force-pushed the mf_new_shm_size_calculation branch from d04e2bf to 93af38c Compare August 4, 2026 11:35
Comment thread score/mw/com/impl/bindings/lola/service_data_control.cpp Fixed
Comment thread score/mw/com/impl/bindings/lola/service_data_control.cpp Fixed
Comment thread score/mw/com/impl/bindings/lola/service_data_control.cpp Fixed
Comment thread score/mw/com/impl/bindings/lola/service_data_storage.cpp Fixed
Comment thread score/mw/com/impl/bindings/lola/service_data_storage.cpp Fixed
Comment thread score/mw/com/impl/bindings/lola/service_data_storage.cpp Fixed
Moved the shm-size calc for ServiceDataStorage and
ServiceDataControl out of SkeletonMemoryManager
to the correspoinding data structures itself.
@crimson11
crimson11 force-pushed the mf_new_shm_size_calculation branch from 93af38c to 798fdc0 Compare August 4, 2026 12:50

TEST_F(LinearSearchMapFixture, IsEmptyAfterConstruction)
{
// Given a freshly constructed LinearSearchMap with a capacity of 4

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Given a freshly constructed LinearSearchMap with a capacity of 4

{
// Given a freshly constructed LinearSearchMap with a capacity of 4

// When constructing the map

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When constructing a LinearSearchMap with a capacity of 4

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for the test below

TestMap unit{4U, memory_};

// When emplacing a new key/value pair
const auto result = unit.emplace(1, 100);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// 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));
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added test and doc.

return total_size;
}

std::size_t SkeletonMemoryManager::GetNumberOfSampleSlotsFromConfig(const std::string_view service_element_name,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're missing tests for this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added tests

/// 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(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're missing tests for this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added tests

Added death-test for LinearSearchMap for capacity
overflow.

SkeletonMemoryManager now uses
GetServiceElementInstanceDeployment helper.
{
return ShmSizeCalculationMode::kSimulation;
}
else if (shm_size_calc_mode_value == kShmSizeCalcModeAnalysis)
Added tests for ServiceDataControl for size
calculation.
Added tests for ServiceDataStorage for size
calculation.
@crimson11
crimson11 requested a review from bemerybmw August 7, 2026 14:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

4 participants