Skip to content

rosidl: generate, cache, and distribute pre-parsed ASTs as JSON - #989

Open
wjwwood wants to merge 4 commits into
rollingfrom
wjwwood/pre-parsed-ast
Open

rosidl: generate, cache, and distribute pre-parsed ASTs as JSON#989
wjwwood wants to merge 4 commits into
rollingfrom
wjwwood/pre-parsed-ast

Conversation

@wjwwood

@wjwwood wjwwood commented Aug 24, 2026

Copy link
Copy Markdown
Member

Description

Currently, ROS 2 IDL parsing via rosidl_parser.parse_idl_file() parses .idl files from raw text with Lark every time an IDL is accessed. This pr trys to improve that by saving the result of that parse and installing it to the disk next to the idl files themselves, so future accesses can use that. This is similar to .pyc files in python or precompiled headers in C++.

In typical workflows:

  1. Within a single interface package, each generator backend (rosidl_generator_c, rosidl_generator_cpp, rosidl_typesupport_*, etc.) runs as an independent Python subprocess, resulting in 9–11 redundant parse cycles per message file.
  2. Across interface packages, every downstream package (such as sensor_msgs, nav_msgs, or user workspace packages) must repeatedly re-parse the .idl files of all upstream interface dependencies (std_msgs, geometry_msgs, builtin_interfaces).

This PR implements distributed pre-parsed AST caching and JSON serialization:

  • rosidl_parser.serialization: Adds bidirectional conversion between rosidl_parser.definition.IdlContent AST data structures and JSON-serializable dictionaries (idl_content_to_dict, dict_to_idl_content, save_ast_json, load_ast_json).
  • rosidl_parser.parser.parse_idl_file():
    • Adds in-process AST caching (_ast_cache).
    • Checks for a <name>.idl.json pre-parsed AST file alongside the .idl file. If present and up to date, it deserializes the AST directly, bypassing the slower grammar parser entirely.
  • rosidl_generator_type_description & rosidl_pycommon: Saves <name>.idl.json alongside adapted .idl files during interface generation in the build tree.
  • rosidl_cmake: Installs <name>.idl.json alongside .idl files into share/${PROJECT_NAME}/ (OPTIONAL), ensuring pre-parsed ASTs are available for all downstream package builds and binary package installs.

Empirical Benchmarks

1. Clean Rebuild Time (rm -rf build/<pkg>)

Package Definitions Baseline (rolling) Pre-Parsed AST Build Time Reduction
test_msgs 18 msgs/srvs/actions 68.58 s 66.00 s -2.59 s (-3.8%)
sensor_msgs 30 msgs/srvs 74.04 s 71.37 s -2.67 s (-3.6%)
px4_msgs 263 msgs 466.84 s (~7m 47s) 405.75 s (~6m 45s) -61.08 s (-13.1%, >1 min saved)

2. Repeated (No-Op) and Incremental Rebuilds

Package Scenario Baseline (rolling) Pre-Parsed AST Difference
test_msgs No-Op Rebuild 5.40 s 5.27 s -0.13 s
test_msgs Incremental (1 msg touched) 5.06 s 5.13 s +0.07 s
sensor_msgs No-Op Rebuild 5.45 s 5.74 s +0.29 s
sensor_msgs Incremental (1 msg touched) 7.95 s 8.16 s +0.21 s
px4_msgs No-Op Rebuild 11.35 s 12.17 s +0.82 s
px4_msgs Incremental (1 msg touched) 17.13 s 17.41 s +0.28 s

Note: No-op and single-file incremental rebuilds are dominated by CMake dependency checking and remain unchanged.

3. Installed Artifact Footprint & Disk Space Impact

Package Total Installed Files AST JSON Files Total Installed Size AST JSON Size Disk Space Increase
std_msgs 759 30 3,734 KB 23.1 KB +0.62%
test_msgs 562 22 11,627 KB 52.6 KB +0.45%
sensor_msgs 723 28 5,686 KB 61.8 KB +1.10%
px4_msgs 6,143 264 47,468 KB 732.8 KB +1.57%

The pre-parsed AST JSON files add < 1.5% to the installed size of interface packages.

Related to #931

Is this user-facing behavior change?

No, this is an internal build performance optimization.

Did you use Generative AI?

Yes, Gemini.

@wjwwood
wjwwood force-pushed the wjwwood/pre-parsed-ast branch from cdcccdb to 6d05f6d Compare August 24, 2026 11:00
@wjwwood

wjwwood commented Aug 24, 2026

Copy link
Copy Markdown
Member Author

Pulls: #989
Gist: https://gist.githubusercontent.com/wjwwood/71ae0d1a19743d282738f71b696653d3/raw/e47dc3ae9aa915d12b92c523f89b1e48b93c976a/ros2.repos
BUILD args: --packages-above-and-dependencies rosidl_parser rosidl_cmake rosidl_pycommon rosidl_generator_type_description test_msgs
TEST args: --packages-above rosidl_parser rosidl_cmake rosidl_pycommon rosidl_generator_type_description test_msgs
ROS Distro: rolling
Job: ci_launcher
ci_launcher ran: https://ci.ros2.org/job/ci_launcher/20191

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

@wjwwood
wjwwood requested a review from mjcarroll August 24, 2026 11:03
@wjwwood wjwwood self-assigned this Aug 24, 2026
@wjwwood

wjwwood commented Aug 25, 2026

Copy link
Copy Markdown
Member Author

Pulls: #989
Gist: https://gist.githubusercontent.com/wjwwood/00cab2bccc97cbb68047ef0d008aa06f/raw/e47dc3ae9aa915d12b92c523f89b1e48b93c976a/ros2.repos
BUILD args: --packages-above-and-dependencies rosidl_parser rosidl_cmake rosidl_pycommon rosidl_generator_type_description test_msgs
TEST args: --packages-above rosidl_parser rosidl_cmake rosidl_pycommon rosidl_generator_type_description test_msgs
ROS Distro: rolling
Job: ci_launcher
ci_launcher ran: https://ci.ros2.org/job/ci_launcher/20203

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

@wjwwood

wjwwood commented Aug 25, 2026

Copy link
Copy Markdown
Member Author

Pulls: #989
Gist: https://gist.githubusercontent.com/wjwwood/7da55d4f09311036d52c421ab3acf454/raw/e47dc3ae9aa915d12b92c523f89b1e48b93c976a/ros2.repos
BUILD args: --packages-above-and-dependencies rosidl_parser test_msgs rclpy
TEST args: --packages-above rosidl_parser test_msgs rclpy
ROS Distro: rolling
Job: ci_launcher
ci_launcher ran: https://ci.ros2.org/job/ci_launcher/20212

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

@wjwwood

wjwwood commented Aug 25, 2026

Copy link
Copy Markdown
Member Author

Pulls: #989
Gist: https://gist.githubusercontent.com/wjwwood/99dbd151ac2e8c8b427c17a818dc3150/raw/e47dc3ae9aa915d12b92c523f89b1e48b93c976a/ros2.repos
BUILD args: --packages-above-and-dependencies rosidl_parser test_msgs rclpy
TEST args: --packages-above rosidl_parser test_msgs rclpy
ROS Distro: rolling
Job: ci_launcher
ci_launcher ran: https://ci.ros2.org/job/ci_launcher/20216

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

- Add serialization and deserialization for rosidl_parser.definition ASTs to/from JSON.
- Add in-memory AST caching and fast loading from .idl.json files in parse_idl_file.
- Save .idl.json AST during interface generation in the build tree.
- Install .idl.json files alongside .idl files into share/${PROJECT_NAME}/.
- Add unit tests for serialization roundtrip and pre-parsed AST loading.

Signed-off-by: William Woodall <wjwwood@google.com>
- Add dict_to_nestable_type helper for Array and Sequence types
- Explicitly type elements list in idl_content_to_dict
- Narrow Message element types in test_parser.py

Signed-off-by: William Woodall <wjwwood@google.com>
…or pre-parsed ASTs

Signed-off-by: William Woodall <wjwwood@google.com>
Signed-off-by: William Woodall <wjwwood@google.com>
@wjwwood
wjwwood force-pushed the wjwwood/pre-parsed-ast branch from 0cee86a to 2de66c8 Compare August 27, 2026 10:55
@wjwwood

wjwwood commented Aug 27, 2026

Copy link
Copy Markdown
Member Author

Pulls: #989
Gist: https://gist.githubusercontent.com/wjwwood/9251b8b822d4ad472f0db855abe5b7f3/raw/e47dc3ae9aa915d12b92c523f89b1e48b93c976a/ros2.repos
BUILD args: --packages-above-and-dependencies rosidl_parser test_msgs rclpy
TEST args: --packages-above rosidl_parser test_msgs rclpy
ROS Distro: rolling
Job: ci_launcher
ci_launcher ran: https://ci.ros2.org/job/ci_launcher/20255

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

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.

1 participant