ros2 / ros2/rclcpp

High CPU use of executor

Open
#1,637 2 comments 1 reaction 1 assignee View on GitHub

@wjwwood is already working on this.

Since May 13, 2021.

Dominant language
C++
Stars
805
Forks
564
Avg merge
1d 17h
Merged PRs (30d)
27

Description

Report

Ubuntu 20.04
Rolling
Both packages and my own build
Commit: 61fcc766f84848c5effabc999b639d684b63814a
Independent of rmw implementation (tested both FastDDS and CycloneDDS).

Steps to reproduce issue

This was first reported and confirmed by me when recording a rosbag2 (which uses SingleThreadedExecutor). Investigating CPU load, I noticed that the majority of resource use is due to executor function get_next_executable(), meaning that even with empty callbacks (no actual work to execute) the CPU load remains very high (around 70% on my machine). The rosbag2 comes from a real (automotive) use case and amounts to about 4k executables (subscriptions) per second.

To reproduce, it should be enough to use the spin() function with enough traffic to ensure high amount of executables. Performance package in rosbag2 could be used to automate running of desired number of publishers.

Expected behavior

Executor should us less CPU for acquiring the next executable. This is important e. g. in the case of rosbag2 it affects how the recorded system performs.

Actual behavior

Executor has a high CPU consumption even when subscription callbacks are empty (just to acquire next executables).

rolling_spin_empty_callbacks_cyclonedds

Additional information

A partial work around is to use spin_some() or spin_all() followed by a short (e. g. 1 ms) sleep in a while (rclcpp::ok()) loop, instead of a spin().
Note that spin_once() with a similar sleep won't work as well since we want to execute a higher number of executables each second than it would permit.

With the case of ~4k executables per second, when calling wait_for_work, 3.3k calls to rcl_wait per each second are made, so it is mostly only one executable that is returned each time, which seems quite inefficient. I am not sure if this is by design (since it perhaps minimizes latency), certainly collections used to gather a bunch of executables for each rcl_wait call are underused.

When a 1 ms sleep is introduced after we miss the cache (before/after rcl_wait), only ~600 calls to rcl_wait per second are made while successfully executing the same number of callbacks per second.

Sleeping (and chrono steady clock) of course can have their behavior dependency on platform so it is hard to suggest this as an executor level change, but certainly a factor to be aware of.

Perhaps another type of executor (Events based) would be more suitable for this type of use-case / requirements for low CPU consumption. There is quite some work ongoing https://github.com/ros2/design/pull/305.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.