-
Notifications
You must be signed in to change notification settings - Fork 13.6k
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
[WIP] uORB::Subscription subscribe directly to uORB device node object #11176
Conversation
Note - this is still early and needs quite a bit more work. I'd mainly like to start getting feedback on the overall design/direction and any other thoughts. |
Nice work! I think this is viable but I might not have all the context because I don't know why these file descriptors were used in the first place. I looked at the diff but it's quite big and seems to contain a lot of overall cleanup, so it's hard to see the meat of the change. What I wonder is if this changes anything about the semantics of polling on topics. I was always assuming that was one of the reasons file descriptors were used. |
What's the impact of this on the FastRTPS communications? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your analysis is spot-on! And it's exactly where we need to reduce overhead to get a lean system.
Some initial thoughts:
- I don't see major problems with the design. Obviously we need to be careful with Subscription, since it accesses uORB internals now (e.g. see next point, and also regarding address separation if we ever do that).
- We'll need to check the impact on concurrency/thread-safety.
- I'd make the default
Subscription
class not contain the topic data, and add aSubscriptionData
(or whatever name) class that contains it, to encourage the use of subscription w/o data (for the cases where we can keep the data on the stack for example). - can we avoid the virtual overhead of Subscription?
- Detail: if we could keep the
add_topic
in Logger generic to add all instances automatically, I'd prefer that. No big deal though.
I also have a template variation in mind on the actual ORB_ID type that would remove for need for storing metadata in memory for a total of 8 bytes per subscription.
I was also thinking of doing that but never got to it so far.
What I wonder is if this changes anything about the semantics of polling on topics.
I assume we'd still use the existing interface for that?
@@ -104,7 +166,7 @@ typedef SubscriptionBase SubscriptionTiny; | |||
/** | |||
* The subscription base class as a list node. | |||
*/ | |||
class __EXPORT SubscriptionNode : public SubscriptionBase, public ListNode<SubscriptionNode *> | |||
class SubscriptionNode : public SubscriptionInterval, public ListNode<SubscriptionNode *> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While at it, can you remove the linked list?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I'd like to, but need to review existing uses (mostly LPE at this point).
I don't know the full history, but in theory it's nice if you can rely on existing system facilities for nearly everything (open/close/read/write/poll). We've gradually grown out various levels of wrapper and obfuscation though.
The first commit is the uORB change itself, the rest are updates to use it. In particular look at uORBDeviceNode.
For now we'd continue using a regular subscription and poll, but we'll be able to do it through these eventually. It could be something like an alternate update call that blocks (eg |
None initially, but uORB::Subscription could grow a blocking update method that would then be good to consider within the RTPS bridge. |
Good idea.
Probably not in the first pass, but I think we'll be able to get there eventually. At the moment SubscriptionBase is fairly flexible to support the mavlink and logger usage, but a template version intended for embedding directly in modules (the vast majority of our orb usage) can be much simpler.
Kind of a side note, but it seems to me that topics should be explicitly multi-capable or single only. I think I'll come back to this idea later. For logger I was trying to avoid creating 4x SubscriptionBases for every topic when only a small portion of them are multi. Any suggestions?
We need to update the generation to produce something like the enum class you created for params. I was also hoping to find a way to tie the topic and data structure together (not sure if this will be controversial) to make the declaration trivial. For example
Yes for now, but I'd like to add a blocking update function here as well (later). I think we can do slightly better than the performance of NuttX poll, while making it hard to misuse. |
738d2b0
to
20b2896
Compare
20b2896
to
8d66a16
Compare
8c90f4c
to
cc1575e
Compare
32193b9
to
8d054a6
Compare
7104888
to
3735558
Compare
3735558
to
13b1ce2
Compare
- TODO: polled topic
- uORB::SubscriptionBase -> uORB::Subscription - uORB::Subscription -> uORB::SubscriptionData
13b1ce2
to
a7528d8
Compare
Implemented! #12040 |
TL;DR - we can save a relatively large amount of memory (maybe 20KB) and some cpu by updating uORB to bypass file descriptors.
Background
On NuttX PX4 uORB uses file descriptors to subscribe to a publication (open file), copy new data (read file), and various other operations (ioctl). This is flexible, but requires a relatively large amount of memory on a small system that doesn't scale well with additional tasks. It's also led to the invention of virtual files to extend px4 to Linux and QuRT.
On NuttX a file descriptor requires...
= 36 bytes per orb subscribe
The larger problem is that each task has an array of file descriptors (16 bytes each) and we currently have a maximum of 54 file descriptors per task (CONFIG_NFILE_DESCRIPTORS), which still isn't enough for some logging configurations. A typical system has 20-25 tasks, which is 17-20 KB of memory.
Proposal
In this pull request I've updated the uORB Subscription wrapper class to be able to "subscribe" directly to a uORB device node. This bypasses the file descriptor issues entirely and offers a number of advantages.
Memory
Instead of a file descriptor the uORB::Subscription class now contains a pointer to the device node, required metadata to find the node before it exists (more on this later), and the equivalent of SubscriberData (13 bytes total). I also have a template variation in mind on the actual ORB_ID type that would remove for need for storing metadata in memory for a total of 8 bytes per subscription.
This PR updates the mavlink and logger modules to use uORB::Scription and also reduces the number of file descriptors from 54 -> 30. The result on a pixracer configured as a generic multicopter is an ~11 KB memory savings, and a ~4% decrease in cpu.
master
PR (includes a 6 KB increase in the logger buffer, total free ~ 11 KB more than master)
A secondary benefit is that subscribing via uORB::Subscription will not force the creation of a uORB::DeviceNode before publication. Device nodes created with no publishers is another few kilobytes of wasted memory in a typical system.
Performance
Additionally, bypassing file descriptors is also slightly faster. It doesn't seem like much, but these functions are called thousands of times every second. Below you can see the difference system wide after having only converted mavlink and logger. This will be helpful when the high rate modules are updated to use uORB::Subscription.
master
PR (same test, but using uORB::Subscription)
Overall system before
Overall system after (includes 6KB increase in logger buffer)
Future Ideas
TODO