Skip to content

improve MessageTypeSupport performance. - #562

Merged
mjcarroll merged 2 commits into
rollingfrom
fujitatomoya/improve-MessageTypeSupport_impl
Mar 17, 2026
Merged

improve MessageTypeSupport performance.#562
mjcarroll merged 2 commits into
rollingfrom
fujitatomoya/improve-MessageTypeSupport_impl

Conversation

@fujitatomoya

Copy link
Copy Markdown
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
Copy Markdown
Contributor Author

@biodranik CCed

@biodranik biodranik left a comment

Copy link
Copy Markdown

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

ahcorde commented Mar 6, 2026

Copy link
Copy Markdown
Contributor

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
Copy Markdown
Contributor Author

@ahcorde is this good to go?

@jmachowinski

Copy link
Copy Markdown
Contributor

I would like to see a performance measurement first.

@fujitatomoya

Copy link
Copy Markdown
Contributor Author

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

@jmachowinski

Copy link
Copy Markdown
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...

Comment thread rmw_cyclonedds_cpp/src/MessageTypeSupport_impl.hpp Outdated
Comment thread rmw_cyclonedds_cpp/src/MessageTypeSupport_impl.hpp Outdated
Comment thread rmw_cyclonedds_cpp/src/MessageTypeSupport_impl.hpp

@biodranik biodranik left a comment

Copy link
Copy Markdown

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?

Comment thread rmw_cyclonedds_cpp/src/MessageTypeSupport_impl.hpp
Comment thread rmw_cyclonedds_cpp/src/MessageTypeSupport_impl.hpp Outdated
@mjcarroll

Copy link
Copy Markdown
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.

@jmachowinski

Copy link
Copy Markdown
Contributor

Pulls: #562
Gist: https://gist.githubusercontent.com/jmachowinski/5ecaa4010cc60f753995728f036dca60/raw/aae245497cfc359b3a20bb870967a5a8913e2461/ros2.repos
BUILD args: --cmake-args -DRMW_IMPLEMENTATION=rmw_cyclonedds_cpp
TEST args:
ROS Distro: rolling
Job: ci_launcher
ci_launcher ran: https://ci.ros2.org/job/ci_launcher/18491

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

@jmachowinski jmachowinski mentioned this pull request Mar 16, 2026
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
@fujitatomoya

Copy link
Copy Markdown
Contributor Author

we can see the improvement,

tomoyafujita@~/DVT/02_TEST >uname -srm
Linux 6.17.0-14-generic x86_64

tomoyafujita@~/DVT/02_TEST >grep 'model name' /proc/cpuinfo | head -1
model name      : Intel(R) Core(TM) i7-14700KF

tomoyafujita@~/DVT/02_TEST >g++ --version | head -1
g++ (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0

tomoyafujita@~/DVT/02_TEST >./bench
Correctness check passed.

=== MessageTypeSupport getName() benchmark ===
Iterations: 1000000 x 7 types = 7000000 calls each

OLD (regex + ostringstream): 3221.22 ms
NEW (find/replace + +=):     368.914 ms

OLD per call: 460.175 ns
NEW per call: 52.702 ns

Speedup: 8.73164x

with the following simulated benchmark program.

// bench_message_type_support.cpp
// Simulates the old vs new MessageTypeSupport constructor
// from ros2/rmw_cyclonedds PR #562 (commit 8e0ed9d)
//
// Build & run:
//   g++ -O2 -o bench bench_message_type_support.cpp && time ./bench

#include <iostream>
#include <string>
#include <regex>
#include <sstream>
#include <chrono>
#include <vector>
#include <cstring>

// Prevent compiler from optimizing away the result
static volatile size_t sink = 0;

// ============================================================
// OLD: std::regex_replace + std::ostringstream
// (MessageTypeSupport_impl.hpp before PR #562)
// ============================================================
std::string getName_old(const char* message_namespace_, const char* message_name_) {
    std::ostringstream ss;
    std::string message_namespace(message_namespace_);
    std::string message_name(message_name_);
    if (!message_namespace.empty()) {
        // Find and replace C namespace separator with C++
        message_namespace = std::regex_replace(message_namespace, std::regex("__"), "::");
        ss << message_namespace << "::";
    }
    ss << "dds_::" << message_name << "_";
    return ss.str();
}

// ============================================================
// NEW: null check + find/replace loop + reserve + direct +=
// (MessageTypeSupport_impl.hpp after PR #562, commit 8e0ed9d)
// ============================================================
std::string getName_new(const char* message_namespace_, const char* message_name_) {
    std::string message_name(message_name_);

    std::string name;
    if (message_namespace_ != nullptr && message_namespace_[0] != '\0') {
        std::string message_namespace(message_namespace_);
        // Find and replace C namespace separator with C++
        std::string::size_type pos = 0;
        while ((pos = message_namespace.find("__", pos)) != std::string::npos) {
            message_namespace.replace(pos, 2, "::");
            pos += 2;
        }
        name.reserve(
            message_namespace.size() + 2 + 5 + message_name.size() + 1);
        name += message_namespace;
        name += "::";
    } else {
        name.reserve(5 + message_name.size() + 1);
    }
    name += "dds_::";
    name += message_name;
    name += '_';
    return name;
}

int main() {
    // Realistic ROS 2 type namespaces (C typesupport uses "__" separator)
    std::vector<std::pair<const char*, const char*>> test_cases = {
        {"sensor_msgs__msg",              "PointCloud2"},
        {"geometry_msgs__msg",            "PoseStamped"},
        {"nav_msgs__msg",                 "OccupancyGrid"},
        {"tf2_msgs__msg",                 "TFMessage"},
        {"",                              "String"},          // empty namespace
        {nullptr,                         "Header"},          // null namespace
        {"very__deep__nested__ns__msg",   "Custom"},
    };

    const int ITERATIONS = 1'000'000;

    // Verify both produce same output
    for (auto& [ns, name] : test_cases) {
        const char* safe_ns = ns ? ns : "";
        std::string old_result = getName_old(safe_ns, name);
        std::string new_result = getName_new(ns, name);
        if (old_result != new_result) {
            std::cerr << "MISMATCH for ns=\"" << safe_ns << "\" name=\"" << name << "\"\n"
                      << "  old: " << old_result << "\n"
                      << "  new: " << new_result << "\n";
            return 1;
        }
    }
    std::cout << "Correctness check passed.\n\n";

    // --- Benchmark OLD ---
    auto t0 = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < ITERATIONS; i++) {
        for (auto& [ns, name] : test_cases) {
            const char* safe_ns = ns ? ns : "";
            sink += getName_old(safe_ns, name).size();
        }
    }
    auto t1 = std::chrono::high_resolution_clock::now();

    // --- Benchmark NEW ---
    auto t2 = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < ITERATIONS; i++) {
        for (auto& [ns, name] : test_cases) {
            sink += getName_new(ns, name).size();
        }
    }
    auto t3 = std::chrono::high_resolution_clock::now();

    double old_ms = std::chrono::duration<double, std::milli>(t1 - t0).count();
    double new_ms = std::chrono::duration<double, std::milli>(t3 - t2).count();
    long total_calls = (long)ITERATIONS * test_cases.size();

    std::cout << "=== MessageTypeSupport getName() benchmark ===\n";
    std::cout << "Iterations: " << ITERATIONS << " x " << test_cases.size()
              << " types = " << total_calls << " calls each\n\n";

    std::cout << "OLD (regex + ostringstream): " << old_ms << " ms\n";
    std::cout << "NEW (find/replace + +=):     " << new_ms << " ms\n\n";

    std::cout << "OLD per call: " << (old_ms / total_calls) * 1e6 << " ns\n";
    std::cout << "NEW per call: " << (new_ms / total_calls) * 1e6 << " ns\n\n";

    std::cout << "Speedup: " << old_ms / new_ms << "x\n";

    return 0;
}

i guess this is worth to take??? @mjcarroll @jmachowinski @Timple @biodranik wdyt?

@mjcarroll

Copy link
Copy Markdown
Member

Almost 10x faster, why not :)

@mjcarroll mjcarroll left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM with green CI

@fujitatomoya

Copy link
Copy Markdown
Contributor Author
  • Linux Build Status
  • Linux-aarch64 Build Status
  • Linux-rhel Build Status
  • Windows Build Status

@jmachowinski

Copy link
Copy Markdown
Contributor

@fujitatomoya The CI run did not use cyclone as default DDS, but I think we are good to merge anyway.

@mjcarroll

Copy link
Copy Markdown
Member

Windows unrelated, merging.

@mjcarroll
mjcarroll merged commit fd48d58 into rolling Mar 17, 2026
2 checks passed
@fujitatomoya

Copy link
Copy Markdown
Contributor Author

@Mergifyio backport kilted jazzy humble

@mergify

mergify Bot commented Mar 20, 2026

Copy link
Copy Markdown

backport kilted jazzy humble

✅ Backports have been created

Details

mergify Bot pushed a commit that referenced this pull request Mar 20, 2026
* improve MessageTypeSupport performance.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>

* address review comments.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
(cherry picked from commit fd48d58)
mergify Bot pushed a commit that referenced this pull request Mar 20, 2026
* improve MessageTypeSupport performance.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>

* address review comments.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
(cherry picked from commit fd48d58)
mergify Bot pushed a commit that referenced this pull request Mar 20, 2026
* improve MessageTypeSupport performance.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>

* address review comments.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
(cherry picked from commit fd48d58)
ahcorde pushed a commit that referenced this pull request Mar 23, 2026
(cherry picked from commit fd48d58)

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
ahcorde pushed a commit that referenced this pull request Mar 23, 2026
(cherry picked from commit fd48d58)

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
ahcorde pushed a commit that referenced this pull request Mar 23, 2026
(cherry picked from commit fd48d58)

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
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