Skip to content

API for pre-allocating messages in the middleware #159

@mjcarroll

Description

@mjcarroll

Feature request

Feature description

In order to reduce dynamic memory allocations during publish and receipt of messages during the operation of a Node, this API would ideally allow for users of the RMW API to request the middleware to preallocate memory for predefined message types and sizes.

Implementation considerations

Implement a struct that hides the implementation of the buffer allocation that future calls to publish will take as an argument. If a publisher is called with the argument, then it will use the previously-allocated buffer rather than allocating a new one as necessary.

typedef struct RMW_PUBLIC_TYPE rmw_publisher_allocation_t
{
  const char * implementation_identifier;
  void * data;
} rmw_publisher_allocation_t;

typedef struct RMW_PUBLIC_TYPE rmw_subscription_allocation_t
{
  const char * implementation_identifier;
  void * data;
} rmw_subscription_allocation_t;

These can then be used by the rmw_publish and rmw_subscription methods to provide a reserved space for serializing/deserializing incoming/outgoing messages.

Before any messages were publish or take, these would need to be initialized via an API as follows:

rmw_init_publisher_allocation_t(rmw_publisher_allocation_t*, const rosidl_message_typesupport_t*, const rosidl_message_bounds_t*);
rmw_init_subscription_allocation_t(rmw_publisher_allocation_t*, const rosidl_message_typesupport_t*, const rosidl_message_bounds_t*)
rmw_fini_publisher_allocation_t(rmw_publisher_allocation_t*);
rmw_fini_subscription_allocation_t(rmw_subscription_allocation_t*)

This will also required additional information from the typesupport to get the serialized message sizes, also taking into account the potential size of unbounded fields. To account for unbounded fields, I propose adding a second structure rosidl_message_bounds_t that will give the user a way of specifying known fixed bounds for unbounded messages.

For fixed and bounded sized messages, the API should take a const rosidl_message_typesupport_t* which will then be allocated by the RMW implementation.

The rosidl_message_bounds_t structure would be on a per message basis and describe any of the unbounded fields. Besides the nominal case, there are three main cases to handle.

# Message with unbounded primitive
uint8[] data
struct Msg__bounds {
  size_t data__length = 0;
};
# Message with string array
string[] data
# special structure for delimiting string bounds
struct string__bounds {
  size_t bounds = 0;
};

struct Msg__bounds {
  size_t data__length = 0;
  string__bounds data__bounds;
};
# Message with unbounded array of complex structures
std_msgs/Header header[]
# special structure for delimiting string bounds
struct string__bounds {
  size_t bounds = 0;
};

struct std_msgs__msg__Header__bounds {
  string__bounds frame_id__bounds;
};

struct Msg__bounds {
  size_t header__length = 0;
  std_msgs__msg__Header__bounds header__bounds;
};

Serialized length can be retrieved via:

rmw_get_serialized_length(const rosidl_message_typesupport_t*, const rosidl_message_bounds_t*);

These structures can then be used in conjunction with rmw_publisher:

rmw_publisher_allocation_t alloc;
rmw_init_publisher_allocation(&alloc, ts, bounds);
# TODO: What happens when ros_message violates bounds given?
rmw_publish(publisher, ros_message, alloc);
# Also rmw_publish_serialized_message()

Or with rmw_subscriber:

rmw_subscription_allocation_t alloc;
rmw_init_subscription_allocation(&alloc, ts, bounds);
rmw_create_subscription(node, ts, topic, qos, ignore_local, alloc)

# TODO: What happens when incoming message violates bounds?
rmw_take()

Open Questions:

  • What do we do when a message violates bounds?
  • Drop message
  • Error and allow a retry
  • Realloc and retry

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions