A performanсe when publishing many packets to the same topic
Hi! As I see, for publish a packet from client - I must:
- Make a topic String (or string slice) - contains own copy of data in heap
- Make a TopicName which "eats" topic String (or copies string to own copy in heap) and checks it with RegExp
- Make a payload - also own copy in heap
- Make a PublishPacket which "eats" TopicName and payload
- Serialize the packet to stream, which does not "eat" packet
- Drop the packet
When I need (and many others probaly too) to publish different (millions by day) payloads to the same (thousands) topics - i can't reuse TopicName for doing 1-2 steps once and doing only 3-6 steps for every publish.
Also i can't store a packet for later reuse - it does not support payload replacing.
So this architecture breaks performance.
If You can add ability for using in packet shared ref to TopicName (and for payload may be too, but it is not important) - it will be great!
Or add convenience fn which receives &TopicName, payload: &[u8], &mut Vec
Yuri
It seems that a PublishPacketRef will do the trick.
pub struct PublishPacketRef<'a> {
fixed_header: FixedHeader,
topic_name: &'a TopicNameRef,
packet_identifier: Option<PacketIdentifier>,
payload: &'a [u8],
}
And impls Packet for it. Very simple solution, but the Packet trait implies that the struct could be be Encodable and Decodable, but PublishPacketRef couldn't be Decodable.
Hmmm perhaps split Packet to OutgoingPacket and IncomingPacket (Packet impls both), just so we can have this optimization of published packets?
The scenario @YuriK75 presented is quite common, and avoiding these allocations and deallocations would be great.
What about impl only Encodable for PublishPacketRef? That PublishPacketRef will live very shortly - may be it is too complex for solving one small task... May the only function will do all - at once will take all parameters and encode it to Write.
pub fn publish<W: Write>(
// ... FixedHeader or any other
topic_name: &TopicName,
packet_identifier: Option<PacketIdentifier>,
payload: &[u8],
writer: &mut W) -> Result<(), PublishPacket::Err>
Yes, I have already tried both @Spoonbender and @YuriK75 's proposal locally, yesterday.
- Splits
PacketintoOutgoingPacketandIncomingPacketis good, but one problem have to be solved: how to definePa cket::Payload? Or we should just removepayload()andpayload_ref()member functions, andPacketError<T>'s definition ofPayloadError, which are breaking changes. implsEncodableforPublishPacketRefonly is good, but what is theErrtype? Normally it should bePacketError<T>as all the other*Packettypes.
I haven't decided yet, maybe both of yours ideas should be adopted, which may lead to a huge API breaking chage.
May the only function will do all - at once will take all parameters and encode it to Write.
Yes, that is a simpler solution, but only be useful for this case.
@zonyitoo maybe a cow will help with the fields that are either owned or borrowed?
Packet::decode_packet requires Self to have 'static lifetime.
It should be ok to remove the Packet trait .. Hmm ... Very complicated.
Please check if this commit works for you.
Please check if this commit works for you.
Hmmm... so many changes ... I think my issue much less difficult to solve...
- Splitting Packet trait - ok, it is reasonable change!
- New PublishPacketRef struct - at most ok.
- New TopicNameRef tuple - wrong idea, completely unneeded type. I think in PublishPacketRef::topic_name You should use &TopicName instead of &TopicNameRef. And unsafe code in TopicNameRef::new() with dirty C-style pointers and unusing borrow checker feature - completely bad idea. Remove TopicNameRef at all, please!
Yuri
TopicNameRef is not a new struct. But I think it is safe to remove because nobody is actually using it.
I forgot to say "thank you". You made a lot of work for me!
пт, 26 февр. 2021 г. в 04:11, ty [email protected]:
TopicNameRef is not a new struct. But I think it is safe to remove because nobody is actually using it.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/zonyitoo/mqtt-rs/issues/52#issuecomment-786339129, or unsubscribe https://github.com/notifications/unsubscribe-auth/AOLFLPHLT6T2YP3CCCFKHO3TA3YN3ANCNFSM4XOQPHTA .
I just double checked the design of TopicNameRef. It is required because it is used in TopicFilterRef, which is "new type" for adding special methods for &str.
It is ok to left it there, you can just pass &TopicName as the first argument of PublishPacketRef::new.
As for me, PublishPacketRef should use &TopicName in topic_name field instead of &TopicNameRef. It will be the only usable solution for me.
Can You change it?
I think TopicNameRef can be everywhere replaced by &TopicName (& is the same thing as "Ref"). TopicNameRef is some mistake struct... But You decide, may be i am wrong...
TopicNameRef is for making a str type that is a VALID Topic Name.
https://github.com/zonyitoo/mqtt-rs/blob/e910579f1b02086ecf1ae6ba33c620051d824b84/src/topic_filter.rs#L274-L278
It will be the only usable solution for me.
Could you provide a code snippet about this? Is this design causing unfixable compile errors? Are there any uncomfortable use cases?