-
Notifications
You must be signed in to change notification settings - Fork 391
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
iox-#14 chunk header design with custom header #403
iox-#14 chunk header design with custom header #403
Conversation
Signed-off-by: Mathias Kraus <mathias.kraus@apex.ai>
oh, if someone doesn't know that feature, you can select |
## Considerations | ||
|
||
- it's not uncommon to record chunks for a later replay -> detect incompatibilities on replay | ||
- iceoryx runs on multiple platforms -> endianness of recorded chunks might differ |
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.
btw. we could store an endianness indicator in the ChunkHeader
, e.g. by replacing m_reserved2
and m_reserved3
with a uint16_t m_endianness = 0xA55A
. On big endian platforms the memory would be ... m_chunkHeaderVersion m_reserved1 0xA5 0x5A
and on little endian ... m_chunkHeaderVersion m_reserved1 0x5A 0xA5
. Alternatively, one byte only could be used as flag.
I just don't know if it makes sense to store it in the ChunkHeader
or shift the responsibility to the record framework
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.
Good point @sculpordwarf @frank-schmidtner What do you think, should this be take care of by the recording framework? Any preferences?
|
||
### Integration Into Publisher/Subscriber API | ||
|
||
The `Publisher` has additional template parameters for the custom header and payload alignment. Alternatively this could also be done with the `allocate` method, but that increases the risk of using it wrong by accident. |
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.
For me this is fixed for a specific publisher. So no need to have it in the allocate call
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.
@budrus I would disagree here.
Isn't the idea of the untyped publisher to acquire memory for any arbitrary type and the type and amount of memory can change with every call? With this also the alignment can change with every call!
For me it seems quite natural to have then a second argument in allocate the define the alignment.
untypedPublisher.allocate(memSize, alignment)
In the case of the typed publisher I totally agree with you but here we just call loan
.
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.
I think elfenpiff is right. After thinking a bit about this, it might be helpful to have this in allocate. Maybe we have to write down some use cases for the untyped publisher.
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.
Puuh, You really see a use case where one publisher is used with different alignments for different samples? Quite academic IMHO. If you need a special alignment that is > 32 byte, I would assume that every sample would have the same alignment as there is always the same processing for each sample and this has a specific alignment requirement
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.
Let's say you would like to send samples which are containing data for vector operations implemented with AVX512 or SSE2/3/4. Then you need an alignment of 64 bytes. And currently we explicitly allow the user to send a bool as next sample where an alignment of 1 would suffice.
I think we should go all in in this case and support really the stuff the untyped publisher promises the expert user.
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.
Like elfenpiff said, we can already specify the payload size when a sample is loaned, therefore it makes sense to also be able to specify the alignment. I would specify the custom header with the publisher/subscriber, though.
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.
The untyped subscriber is very likely used when iceoryx is integrated in a bigger framework like eCAL. At least I would go for a default as for most of them it will be don't care
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, default alignment would be 8 bytes
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.
OK, go for it
- untyped API and C bindings | ||
- PoC is needed | ||
- user defined sequence number | ||
- this can probably be done by a `ChunkHeaderHook` |
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.
Then the user defined sequence number would be part of the custom chunk header. Maybe this is the best for now. If iceoryx is used in a framework that wants to have additional features based on the sequence number having an additional custom chunk header there is quite likely (e.g. there are timestamps etc.)
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.
either that or the ChunkHeaderHook
could also be used without a custom header, to overwrite the sequence number in ChunkHeader
. I'm not yet sure if this is a good idea though. maybe we don't want to give the user write access to the ChunkHeader
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.
I would only allow access to the custom chunk header in the hook. The chunk header is then a header only iceoryx writes. Having these separation of concerns makes sense for me when we have a custom header
### ChunkHeader Methods | ||
|
||
- `void* payload()` returns a pointer to the payload | ||
- `template <typename T> T* customHeader()` returns a pointer to the custom header |
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.
what is returned if there is no custom header?
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.
I suppose we could use SFINAE (enable_if
meta programming magic) to prevent the user calling this method in case there is no custom header.
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.
if we store a flag, a nullptr. Maybe an optional<T*>
would be better
- currently we specify the payload size and the size of the `ChunkHeader` is added automatically | ||
- with the new approach, part of the specified payload size might be used for the custom header | ||
- part of the specified payload might also be used as padding for the payload alignment | ||
- shall we change this back to just specifying the full chunk size? |
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.
I would keep it as simple as possible and would just specify the full chunk size.
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.
Great work and a good example for our future design documents!
## Considerations | ||
|
||
- it's not uncommon to record chunks for a later replay -> detect incompatibilities on replay | ||
- iceoryx runs on multiple platforms -> endianness of recorded chunks might differ |
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.
Good point @sculpordwarf @frank-schmidtner What do you think, should this be take care of by the recording framework? Any preferences?
- iceoryx runs on multiple platforms -> endianness of recorded chunks might differ | ||
- for tracing, a chunk should be uniquely identifiable -> store origin and sequence number | ||
- the chunk is located in the shared memory, which will be mapped to arbitrary positions in the address space of various processes -> no absolute pointer are allowed | ||
- aligning the `ChunkHeader` to 32 bytes will ensure that all member are on the same cache line and will improve performance |
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.
Why 32 byte?
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.
historical reasons. Up until now the chunks are aligned to 32 bytes to ensure that we also support an alignment of up to 32 bytes for the payload. With this change, the alignment of the payload can be specified, so it's not necessary anymore. It might help with performance if the ChunkHeader
is not split at two cache lines and 32 is currently the size of the ChunkHeader
. We might want to trade this for more memory efficiency in case we need to increase the ChunkHeader
to e.g. 40 bytes, then the alignment would enforce a size of 64 bytes with 24 padding bytes. In this case it might be better to have an alignment of 8
bytes with a potential cost of distributing the ChunkHeader
on multiple cache lines. This could also be a compile time option to shift the decision to the user.
``` | ||
|
||
- **m_chunkSize** is the size of the whole chunk | ||
- **m_chunkHeaderVersion** is used to detect incompatibilities for record&replay functionality |
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.
Couldn't this be a compile time thing? How are we dealing with this currently? Using iceoryx_posh/include/iceoryx_posh/version/version_info.hpp
?
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, it would be ca compile time thing, so this should probably be a const uint8_t m_chunkHeaderVersion
. Have to think about the implications about this, e.g. the ChunkHeader
wouldn't be copy asignable anymore
### ChunkHeader Methods | ||
|
||
- `void* payload()` returns a pointer to the payload | ||
- `template <typename T> T* customHeader()` returns a pointer to the custom header |
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.
I suppose we could use SFINAE (enable_if
meta programming magic) to prevent the user calling this method in case there is no custom header.
Signed-off-by: Mathias Kraus <mathias.kraus@apex.ai>
Signed-off-by: Mathias Kraus <mathias.kraus@apex.ai>
iceoryx_posh/doc/chunk_header.md
Outdated
``` | ||
// publisher option 1 | ||
auto pub = iox::popo::UntypedPublisher<MyHeader>(serviceDescription); | ||
|
||
// publisher option 2 | ||
auto customHeaderSize = sizeOf(MyHeader); | ||
auto pub = iox::popo::UntypedPublisher(serviceDescription, customHeaderSize); | ||
|
||
auto payloadSize = sizeof(MyPayload); | ||
auto payloadAlignment = alignof(MyPayload); | ||
pub.loan(payloadSize, payloadAlignment) | ||
.and_then([&](auto& sample) { | ||
sample.getHeader()->customHeader<MyHeader>().data = 42; | ||
auto payload = new (sample.get()) MyPayload(); | ||
payload->data = 73; | ||
sample.publish(); | ||
}) | ||
.or_else([](iox::popo::AllocationError) { | ||
// Do something with error. | ||
}); | ||
|
||
// subscriber option 1 | ||
auto pub = iox::popo::UntypedPublisher<MyHeader>(serviceDescription); | ||
|
||
// subscriber option 2 | ||
auto customHeaderSize = sizeOf(MyHeader); | ||
auto pub = iox::popo::UntypedSubscriber(serviceDescription, customHeaderSize); | ||
sub->take() | ||
.and_then([](auto& sample){ | ||
std::cout << "Custom header data: " << sample.getHeader()->customHeader<MyHeader>().data << std::endl; | ||
std::cout << "Payload data: " << static_cast<const MyPayload*>(sample->get()).data << std::endl; | ||
}); | ||
``` | ||
- option 1 has the benefit to catch a wrong alignment of the custom header and would be necessary if we make the `Sample` aware of the custom header |
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.
@rex-schilasky
this is an adjustment of our current untyped API. The payload size is accessible by the ChunkHeader
.
|
||
// subscriber option 2 | ||
auto customHeaderSize = sizeOf(MyHeader); | ||
auto pub = iox::popo::UntypedSubscriber(serviceDescription, customHeaderSize); |
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.
Maybe avoiding a template here makes sense. And default for customHeaderSize would be zero. Then there is no change in the API if the custom header is not used
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.
I would still lean towards option 1, since it's more save and I can't think of a use case to change the header between samples. The small change would be to use UntypedPublisher<>(serviceDescription)
if the custom header is not used
Signed-off-by: Mathias Kraus <mathias.kraus@apex.ai>
- is it necessary to store a flag in `ChunkHeader` if a custom extension is used? | ||
- we could maintain a list of known header IDs or ranges of IDs similar to `IANA` https://tools.ietf.org/id/draft-cotton-tsvwg-iana-ports-00.html#privateports | ||
- the IDs could be stored in the `ChunkHeader` and everything with an ID larger than `0xC000` is free to use | ||
- to make this somewhat save, the `ChunkHeaderHook` must be mandatory with e.g. a `virtual uint16_t getId() = 0;` method which will be called in the `ChunkSender` | ||
- do we want to store the version of the custom extension in the `ChunkHeader`, similarly to `m_chunkHeaderVersion` | ||
- for record&replay the custom header is totally opaque, which might lead to problems if e.g. a timestamp is stored in the custom header and needs to be updated for the replay | ||
- if we maintain a list of known custom header IDs and also store the custom header version, a record&replay framework could implement the required conversions |
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.
what do you think of this? Could nicely solve some issues
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.
For me fine for now. The remaining questions and options are to be addressed in the implementation and integration
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.
Concept looks sound.
Good write-up!
Pre-Review Checklist for the PR Author
iox-#123-this-is-a-branch
)iox-#123 commit text
)git commit -s
)Notes for Reviewer
This is a test for a workflow with a design document PR before a full implementation.
It's the first PR for a custom extension to the chunk header as requested by @rex-schilasky and also needed for the request response feature #27.
I would like to start implementing this up to the integration into the publisher. For that maybe a PoC is needed. I'm quite confident that the proposed design for the ChunkHeader works regardless of the final decision how to use it in the publisher. So there will be at least two further PRs for this issue.
If the implementation requires design changes. The document will be updated with the corresponding PR.
Checklist for the PR Reviewer
Post-review Checklist for the PR Author
Post-review Checklist for the Eclipse Committer
References