Skip to content

improve MessageTypeSupport performance.#562

Open
fujitatomoya wants to merge 1 commit intorollingfrom
fujitatomoya/improve-MessageTypeSupport_impl
Open

improve MessageTypeSupport performance.#562
fujitatomoya wants to merge 1 commit intorollingfrom
fujitatomoya/improve-MessageTypeSupport_impl

Conversation

@fujitatomoya
Copy link
Contributor

Description

The optimization follows biodranik's suggestion from the PR review, addressing the performance issue at its source.

Removed expensive operations:

  • ❌ std::regex_replace and std::regex("__")
  • ❌ std::ostringstream string concatenation

Replaced with efficient plain string operations:

  • ✅ Simple find/replace loop to replace "__" with "::"
  • ✅ reserve() to pre-allocate exact memory needed
  • ✅ Direct string concatenation with += operator

related to #561

Is this user-facing behavior change?

No,

Did you use Generative AI?

Yes, Claude Sonnet 4.6

Additional Information

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
@fujitatomoya fujitatomoya self-assigned this Mar 6, 2026
@fujitatomoya
Copy link
Contributor Author

@biodranik CCed

Copy link

@biodranik biodranik left a comment

Choose a reason for hiding this comment

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

Thanks! LGTM if it works.

@ahcorde
Copy link
Contributor

ahcorde commented Mar 6, 2026

Pulls: #562
Gist: https://gist.githubusercontent.com/ahcorde/b79cf81cdcba82bbe8b64dfbbfddae0e/raw/aae245497cfc359b3a20bb870967a5a8913e2461/ros2.repos
BUILD args: --packages-above-and-dependencies rmw_cyclonedds_cpp
TEST args: --packages-above rmw_cyclonedds_cpp
ROS Distro: rolling
Job: ci_launcher
ci_launcher ran: https://ci.ros2.org/job/ci_launcher/18372

  • Linux Build Status
  • Linux-aarch64 Build Status
  • Linux-rhel Build Status
  • Windows Build Status

@fujitatomoya
Copy link
Contributor Author

@ahcorde is this good to go?

@jmachowinski
Copy link
Contributor

I would like to see a performance measurement first.

@fujitatomoya
Copy link
Contributor Author

@jmachowinski do you have benchmark test? or anybody else does?

@jmachowinski
Copy link
Contributor

Nope, and I see this is a problem, because we have no way of figuring out, if we improve stuff, or introduce regressions. @mjcarroll recently tried to improve things, and afterwards we figured out, the benchmark was pointless.
For this specific change, one could do a quick test in compiler explore, and check if std::regex is really that slow (I expect it to be, but you never know).
This also looks like it will not improve the real performance of a system, as this constructor will only be called a few times during initialization of the serializers etc. And this is exactly my point, I think we are optimizing the wrong things here...

name += message_name;
name += '_';

this->setName(name.c_str());
Copy link
Contributor

Choose a reason for hiding this comment

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

setName accepts a std::string

Suggested change
this->setName(name.c_str());
this->setName(name));

Comment on lines 34 to 53
std::string message_namespace(this->members_->message_namespace_);
std::string message_name(this->members_->message_name_);

if (!message_namespace.empty()) {
// Find and replace C namespace separator with C++, in case this is using C typesupport
message_namespace = std::regex_replace(message_namespace, std::regex("__"), "::");
ss << message_namespace << "::";
std::string::size_type pos = 0;
while ((pos = message_namespace.find("__", pos)) != std::string::npos) {
message_namespace.replace(pos, 2, "::");
pos += 2;
}
}

std::string name;
name.reserve(
message_namespace.size() + 2 + 5 + message_name.size() + 1); // "::" + "dds_::" + "_"

if (!message_namespace.empty()) {
name += message_namespace;
name += "::";
}
Copy link
Contributor

Choose a reason for hiding this comment

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

The code flow is a bit odd, why not declare name first, and do everything related to the namespace in one if, and keep the 'namespace string' scope local to the if ?

Choose a reason for hiding this comment

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

Agree, the order should be

if (!message_namespace.empty()) {
  std::string name;
  name.reserve(...)
  // ...
}

message_namespace = std::regex_replace(message_namespace, std::regex("__"), "::");
ss << message_namespace << "::";
std::string::size_type pos = 0;
while ((pos = message_namespace.find("__", pos)) != std::string::npos) {
Copy link

Choose a reason for hiding this comment

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

Perhaps comment that this is like this for performance reasons.
People might read this snippet and try to clean as you go and introduce a regex :)

Choose a reason for hiding this comment

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

Any C++ developer should understand that:

  • There's a huge overhead when using std::regex
  • In-place/in-memory replacement is faster than any memory allocation + memory copying

Copy link
Contributor

Choose a reason for hiding this comment

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

For this one there are good arguments for both code versions. If it is really only one time, the performance does not matter, and I would argue that maintainability and simplicity out weight performance. If it is in a hot code path the proposed version is clearly the winner. Anyway I am fine with both versions.

Copy link

@biodranik biodranik left a comment

Choose a reason for hiding this comment

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

This also looks like it will not improve the real performance of a system, as this constructor will only be called a few times during initialization of the serializers etc. And this is exactly my point, I think we are optimizing the wrong things here...

Even if this is not a performance-critical place, avoiding unnecessary, avoidable overhead everywhere in the code would be a good example to new contributors (and LLMs) to produce a better quality, faster code.

FOSS should not be slow and ugly, right?

message_namespace = std::regex_replace(message_namespace, std::regex("__"), "::");
ss << message_namespace << "::";
std::string::size_type pos = 0;
while ((pos = message_namespace.find("__", pos)) != std::string::npos) {

Choose a reason for hiding this comment

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

Any C++ developer should understand that:

  • There's a huge overhead when using std::regex
  • In-place/in-memory replacement is faster than any memory allocation + memory copying

Comment on lines 34 to 53
std::string message_namespace(this->members_->message_namespace_);
std::string message_name(this->members_->message_name_);

if (!message_namespace.empty()) {
// Find and replace C namespace separator with C++, in case this is using C typesupport
message_namespace = std::regex_replace(message_namespace, std::regex("__"), "::");
ss << message_namespace << "::";
std::string::size_type pos = 0;
while ((pos = message_namespace.find("__", pos)) != std::string::npos) {
message_namespace.replace(pos, 2, "::");
pos += 2;
}
}

std::string name;
name.reserve(
message_namespace.size() + 2 + 5 + message_name.size() + 1); // "::" + "dds_::" + "_"

if (!message_namespace.empty()) {
name += message_namespace;
name += "::";
}

Choose a reason for hiding this comment

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

Agree, the order should be

if (!message_namespace.empty()) {
  std::string name;
  name.reserve(...)
  // ...
}

@mjcarroll
Copy link
Member

And this is exactly my point, I think we are optimizing the wrong things here...

I agree that this isn't going to have a huge impact, but getting this code cleaner won't hurt. I wouldn't encourage people to go out of their way to hunt for these things, but in this case it was uncovered when looking for other optimizations, so it seems low cost to just fix now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants