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

Avoid the limitation of not being able to import rclpy C extensions at module level [alternative] #420

Closed

Conversation

ivanpauno
Copy link
Member

Alternative to #417.

In this case, instead of installing the C extensions as a module of rclpy package, I installed them in a new package rclpy_impl.

In this way, we don't need to "hack" the imports while testing.
This solve the same problem as #417, and also, it should solve the testing problems of rclpy on windows when launch_testing_ros is present.

…t module level

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
@ivanpauno ivanpauno self-assigned this Sep 6, 2019
@ivanpauno ivanpauno added enhancement New feature or request in review Waiting for review (Kanban column) labels Sep 6, 2019
@ivanpauno
Copy link
Member Author

ivanpauno commented Sep 6, 2019

CI up to rclpy (on windows, also built up to launch_testing_ros)

@ivanpauno ivanpauno changed the title Avoid the limitation of not being able to import rclpy C extensions a… Avoid the limitation of not being able to import rclpy C extensions at module level [alternative] Sep 6, 2019
@ivanpauno
Copy link
Member Author

So, this PR is solving all the test failures on Windows CI, plus allowing to import the C extensions at module level.
#417 just solves the second problem, but as proved in this CI run, it doesn't solve the first one.

Copy link
Member

@dirk-thomas dirk-thomas left a comment

Choose a reason for hiding this comment

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

As far as I understand this patch the C extension would immediately be loaded when import rclpy is invoked. I thought the general goal was to delay that until any function from the C extension is actually being used?

@@ -67,7 +67,7 @@ function(configure_python_c_extension_library _library_name)
)

install(TARGETS ${_library_name}
DESTINATION "${PYTHON_INSTALL_DIR}/${PROJECT_NAME}"
DESTINATION "${PYTHON_INSTALL_DIR}/${PROJECT_NAME}_impl"
Copy link
Member

Choose a reason for hiding this comment

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

This package doesn't own the namespace rclpy_impl. If another package with that name installs its files they will collide with this one.

@hidmic
Copy link
Contributor

hidmic commented Sep 7, 2019

I thought the general goal was to delay that until any function from the C extension is actually being used?

Hmm, why is that a goal?

@dirk-thomas
Copy link
Member

why is that a goal?

As the comments on the local imports state: "to avoid loading extensions on module import".

First of all if you import any Python module it doesn't need to load the C extensions yet - hence it is faster and lighter weight. Second it avoid the race condition between any customization you might want to do before trying to load the C extensions. Third any tool which needs to import the Python modules but doesn't want to ever call any of the API can do so without the need to have / load the C extensions.

@hidmic
Copy link
Contributor

hidmic commented Sep 7, 2019

First of all if you import any Python module it doesn't need to load the C extensions yet - hence it is faster and lighter weight.

Is there an observable difference in speed? Do we have a measure of how bigger the memory footprint is? Correct me if I'm wrong but I'd expect rmw implementations to introduce the largest overhead in dynamic library loading and we're already deferring loading for those (at least on Linux, not sure about the other platforms?).

Second it avoid the race condition between any customization you might want to do before trying to load the C extensions.

What kind of customization? Are we doing so? Could a downstream package achieve the same effect by delaying rclpy imports?

Third any tool which needs to import the Python modules but doesn't want to ever call any of the API can do so without the need to have / load the C extensions.

Which goes back to the first statement, unless we apply that same logic to every single module import. I'd like to further understand why we need to give special treatment for an otherwise regular CPython module. It certainly makes it non-standard and creates issues from time to time -- which are easy to fix, sure, but only once you've understood all the implications of how we build and install rclpy.

@ivanpauno
Copy link
Member Author

@hidmic @dirk-thomas I've worked in a lazy import alternative, but I first want to finish with this discussion.

I understand @hidmic point, I think we're doing kind of a "premature optimization".
In general, _rclpy C extension will be loaded sooner or later, so I don't think is bad to load it in a global import.
The others, like _rclpy_action, should be only loaded when the module that uses it is loaded (rclpy.action in this case).
This is not currently happening, as rclpy.impl.implementation_singleton is doing all the imports in the same place, so if any C extension is loaded the others too.


There is currently two problems:

  • The installed package folder structure is different than the package source directory structure, so pytest doesn't find the C extensions if we don't do something
    • A way to solve the problem, is to split each C extensions in its own package.
  • Where to do the imports of the C extensions? Use lazy import?
    • I would move each import to the module where is used and I would avoid doing lazy import (I don't see much performance benefit).

@dirk-thomas
Copy link
Member

I think we're doing kind of a "premature optimization".

The primary goal for doing lazy load is not performance improvement. It is to avoid the current race and in use cases which don't need the C extension not requiring it to be loaded.

In general, _rclpy C extension will be loaded sooner or later, so I don't think is bad to load it in a global import.

I think you are missing some use cases:

  • An application might just be interested in some constants or helper functions defined in rclpy, e.g.
    HIDDEN_NODE_PREFIX = '_'
    In that case it would never trigger API which needs the C extensions.
  • A linter doing static code analysis needs to import all modules but doesn't execute any API.

The others, like _rclpy_action, should be only loaded when the module that uses it is loaded (rclpy.action in this case).
This is not currently happening, as rclpy.impl.implementation_singleton is doing all the imports in the same place, so if any C extension is loaded the others too.

With my proposal to convert the globally initialized handles into functions providing a lazily loaded singleton instance of the C extension that would not be the case anymore and each library would be loaded only when needed and on demand.

The installed package folder structure is different than the package source directory structure, so pytest doesn't find the C extensions if we don't do something

  • A way to solve the problem, is to split each C extensions in its own package.

See my above comment why I don't think that is feasible. Also the current approach would work just fine as is. In the cases it currently fails due to the race it would work once we do lazy loading.

@ivanpauno
Copy link
Member Author

Closing, because this is outdated.
We should still investigate ways of solving this issue.

@ivanpauno ivanpauno closed this May 22, 2020
@dirk-thomas
Copy link
Member

We should still investigate ways of solving this issue.

@ivanpauno Is there any open ticket for this? If not, please create one and reference the previous approaches.

Also is the branch still needed?

@ivanpauno ivanpauno deleted the ivanpauno/avoid-limiting-impl-import-alternative branch May 26, 2020 12:43
@ivanpauno
Copy link
Member Author

@ivanpauno Is there any open ticket for this? If not, please create one and reference the previous approaches.

See #560.

Also is the branch still needed?

Deleted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request in review Waiting for review (Kanban column)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants