Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pydrake] Centralize pybind11 includes for ODR safety #19571

Merged

Conversation

jwnimmer-tri
Copy link
Collaborator

@jwnimmer-tri jwnimmer-tri commented Jun 10, 2023

For convenience and easier ODR-safety, consolidate the common pybind11 includes (especially the includes that specialize trait structs) into our central pydrake_pybind.h header. This avoids hazards where some modules forget to include the proper headers and experience compile-time or runtime failures as a result.

Towards #19250.

Tested locally for possible performance regressions (see below).


Checking for performance regression of a full rebuild:

Build time on master:

  • git checkout master
  • bazel build //bindings/...
  • patch pybind11/package.BUILD.bazel to force a bindings rebuild [1]
  • time bazel build //bindings/...

Result:

INFO: Elapsed time: 170.486s, Critical Path: 135.65s
INFO: 99 processes: 1 internal, 98 linux-sandbox.

Build time on pull request:

  • git checkout pydrake-canonical-includes
  • bazel build //bindings/...
  • patch pybind11/package.BUILD.bazel to force a bindings rebuild [1]
  • time bazel build //bindings/...

Result:

INFO: Elapsed time: 151.502s, Critical Path: 150.37s
INFO: 99 processes: 1 internal, 98 linux-sandbox.

I believe the critical path changes are just the luck of when multibody_tree_py.cc starts compiling.

[1] the patch:

--- a/tools/workspace/pybind11/package.BUILD.bazel
+++ b/tools/workspace/pybind11/package.BUILD.bazel
@@ -43,6 +43,7 @@ cc_library(
         "include/pybind11/stl_bind.h",
     ],
     includes = ["include"],
+    defines = ["TRIGGER_REBUILD=YES_PLEASE_master"],
     deps = [
         "@eigen",
         "@python",

Checking performance of a single-file incremental rebuild:

Neither of these two targets show any performance slow-down of a rebuild with the extra included headers:

bazel build //bindings/pydrake/common:schema_py
bazel build //bindings/pydrake/common:eigen_geometry_py

This change is Reviewable

@jwnimmer-tri jwnimmer-tri added priority: low status: single reviewer ok https://drake.mit.edu/reviewable.html release notes: none This pull request should not be mentioned in the release notes labels Jun 10, 2023
@jwnimmer-tri
Copy link
Collaborator Author

+@xuchenhan-tri for both reviews per schedule, please.

\CC @EricCousineau-TRI in case you'd like to weigh in

Copy link
Contributor

@xuchenhan-tri xuchenhan-tri left a comment

Choose a reason for hiding this comment

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

:lgtm: with one question:

Can you clarify the criterion for what includes make the cut to pydrake_pybind.h? For example, I see that "pybind11/eval.h" is excluded and not sure why. Is it not common enough? Does that undermines ODR-safety goal that we are trying to achieve here?

Reviewed 100 of 100 files at r1, all commit messages.
Reviewable status: :shipit: complete! all discussions resolved, LGTM from assignee xuchenhan-tri(platform)

Copy link
Collaborator Author

@jwnimmer-tri jwnimmer-tri left a comment

Choose a reason for hiding this comment

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

Reviewable status: 1 unresolved discussion

a discussion (no related file):
Working

Can you clarify the criterion for what includes make the cut to pydrake_pybind.h? For example, I see that "pybind11/eval.h" is excluded and not sure why. Is it not common enough? Does that undermines ODR-safety goal that we are trying to achieve here?


For convenience and easier ODR-safety, consolidate the common pybind11
includes (especially the includes that specialize trait structs) into
our central pydrake_pybind header. This avoids hazards where some
modules forget to include the proper headers and experience compile-
time or runtime failures as a result.
@jwnimmer-tri jwnimmer-tri force-pushed the pydrake-canonical-includes branch from 463e6d2 to ef58f8f Compare June 12, 2023 20:42
@jwnimmer-tri jwnimmer-tri removed the status: single reviewer ok https://drake.mit.edu/reviewable.html label Jun 12, 2023
Copy link
Collaborator Author

@jwnimmer-tri jwnimmer-tri left a comment

Choose a reason for hiding this comment

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

+@rpoyner-tri for platform review of bindings/pydrake/pydrake_pybind.h only, please -(status: single reviewer ok), to check the clarity there with >1 person.

Reviewable status: LGTM missing from assignee rpoyner-tri(platform)

a discussion (no related file):

Previously, jwnimmer-tri (Jeremy Nimmer) wrote…

Working

Can you clarify the criterion for what includes make the cut to pydrake_pybind.h? For example, I see that "pybind11/eval.h" is excluded and not sure why. Is it not common enough? Does that undermines ODR-safety goal that we are trying to achieve here?

Done.


Copy link
Contributor

@xuchenhan-tri xuchenhan-tri left a comment

Choose a reason for hiding this comment

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

Reviewed 2 of 2 files at r2, all commit messages.
Reviewable status: LGTM missing from assignee rpoyner-tri(platform)

Copy link
Contributor

@rpoyner-tri rpoyner-tri left a comment

Choose a reason for hiding this comment

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

:lgtm: with some follow-up questions

Reviewed 98 of 100 files at r1, 2 of 2 files at r2, all commit messages.
Reviewable status: 1 unresolved discussion


bindings/pydrake/pydrake_pybind.h line 5 at r2 (raw file):

#include <utility>

// Here we include a lot of the pybind11 API, to ensure that all code in pydrake

btw I'm satisfied that this patch does the things the comment describes:

  • includes type_caster headers
  • includes ADL headers
  • does not include headers we don't need (yet?)
  • does not include headers that not involved in specializations

It looks then to me like I should verify that this header is seen by every pydrake C++ translation unit.

Are there any correctness invariants that I've missed?

Copy link
Collaborator Author

@jwnimmer-tri jwnimmer-tri left a comment

Choose a reason for hiding this comment

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

Reviewable status: 1 unresolved discussion


bindings/pydrake/pydrake_pybind.h line 5 at r2 (raw file):

Are there any correctness invariants that I've missed?

Those seem like the right ones to me.

It looks then to me like I should verify that this header is seen by every pydrake C++ translation unit.

Every TU that includes pybind11 stuff, yes. If there is any C++ code in pydrake that doesn't use pybind11 at all, it would be safe without including this.

Copy link
Contributor

@rpoyner-tri rpoyner-tri left a comment

Choose a reason for hiding this comment

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

Reviewable status: 1 unresolved discussion


bindings/pydrake/pydrake_pybind.h line 5 at r2 (raw file):

Previously, jwnimmer-tri (Jeremy Nimmer) wrote…

Are there any correctness invariants that I've missed?

Those seem like the right ones to me.

It looks then to me like I should verify that this header is seen by every pydrake C++ translation unit.

Every TU that includes pybind11 stuff, yes. If there is any C++ code in pydrake that doesn't use pybind11 at all, it would be safe without including this.

There is one TU that might be on the bubble, and maybe has escaped attention so far: bindings/bazel_workaround_4594_libdrake_py.cc

Clearly it's an outlier and maybe doesn't need this header reform. I'm not quite far enough in the headspace to make a judgment. WDYT?

Copy link
Collaborator Author

@jwnimmer-tri jwnimmer-tri left a comment

Choose a reason for hiding this comment

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

Reviewable status: 1 unresolved discussion


bindings/pydrake/pydrake_pybind.h line 5 at r2 (raw file):

Previously, rpoyner-tri (Rick Poyner (rico)) wrote…

There is one TU that might be on the bubble, and maybe has escaped attention so far: bindings/bazel_workaround_4594_libdrake_py.cc

Clearly it's an outlier and maybe doesn't need this header reform. I'm not quite far enough in the headspace to make a judgment. WDYT?

Since it doesn't define any types (only a module doc), I think it poses no risk.

My guess is that trying to capture that in a comment somewhere would cause more confusion that help. WDYT?

Copy link
Contributor

@rpoyner-tri rpoyner-tri left a comment

Choose a reason for hiding this comment

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

Reviewable status: 1 unresolved discussion


bindings/pydrake/pydrake_pybind.h line 5 at r2 (raw file):

Previously, jwnimmer-tri (Jeremy Nimmer) wrote…

Since it doesn't define any types (only a module doc), I think it poses no risk.

My guess is that trying to capture that in a comment somewhere would cause more confusion that help. WDYT?

It's on the bubble -- a pretty trivial module. I was thinking to just use the drake_pybind header for consistency across the board, and not comment on it.

Copy link
Collaborator Author

@jwnimmer-tri jwnimmer-tri left a comment

Choose a reason for hiding this comment

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

Reviewable status: 1 unresolved discussion


bindings/pydrake/pydrake_pybind.h line 5 at r2 (raw file):

Previously, rpoyner-tri (Rick Poyner (rico)) wrote…

It's on the bubble -- a pretty trivial module. I was thinking to just use the drake_pybind header for consistency across the board, and not comment on it.

Given that the point of that module is to not be infected with pydrake (see bindings/BUILD.bazel docs), I am skeptical about using a pydrake header there. The compiler might allow it, but I still come down on more-confusing-than-helpful for that change as well.

Copy link
Contributor

@rpoyner-tri rpoyner-tri left a comment

Choose a reason for hiding this comment

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

Reviewable status: :shipit: complete! all discussions resolved, LGTM from assignees rpoyner-tri(platform),xuchenhan-tri(platform)


bindings/pydrake/pydrake_pybind.h line 5 at r2 (raw file):

Previously, jwnimmer-tri (Jeremy Nimmer) wrote…

Given that the point of that module is to not be infected with pydrake (see bindings/BUILD.bazel docs), I am skeptical about using a pydrake header there. The compiler might allow it, but I still come down on more-confusing-than-helpful for that change as well.

I'm good then.

@rpoyner-tri
Copy link
Contributor

@drake-jenkins-bot linux-focal-clang-bazel-experimental-everything-release please.

@jwnimmer-tri jwnimmer-tri merged commit efff8ba into RobotLocomotion:master Jun 13, 2023
@jwnimmer-tri jwnimmer-tri deleted the pydrake-canonical-includes branch June 13, 2023 16:10
RussTedrake pushed a commit to RussTedrake/drake that referenced this pull request Jul 3, 2023
…n#19571)

For convenience and easier ODR-safety, consolidate the common pybind11
includes (especially the includes that specialize trait structs) into
our central pydrake_pybind header. This avoids hazards where some
modules forget to include the proper headers and experience compile-
time or runtime failures as a result.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
priority: low release notes: none This pull request should not be mentioned in the release notes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants