mqtt-rs icon indicating copy to clipboard operation
mqtt-rs copied to clipboard

A performanсe when publishing many packets to the same topic

Open YuriK75 opened this issue 4 years ago • 15 comments

Hi! As I see, for publish a packet from client - I must:

  1. Make a topic String (or string slice) - contains own copy of data in heap
  2. Make a TopicName which "eats" topic String (or copies string to own copy in heap) and checks it with RegExp
  3. Make a payload - also own copy in heap
  4. Make a PublishPacket which "eats" TopicName and payload
  5. Serialize the packet to stream, which does not "eat" packet
  6. 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 and serializes 1st and 2nd arguments to 3rd...

Yuri

YuriK75 avatar Feb 11 '21 09:02 YuriK75

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.

zonyitoo avatar Feb 11 '21 11:02 zonyitoo

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.

Spoonbender avatar Feb 11 '21 17:02 Spoonbender

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>

YuriK75 avatar Feb 12 '21 10:02 YuriK75

Yes, I have already tried both @Spoonbender and @YuriK75 's proposal locally, yesterday.

  1. Splits Packet into OutgoingPacket and IncomingPacket is good, but one problem have to be solved: how to define Pa cket::Payload? Or we should just remove payload() and payload_ref() member functions, and PacketError<T>'s definition of PayloadError, which are breaking changes.
  2. impls Encodable for PublishPacketRef only is good, but what is the Err type? Normally it should be PacketError<T> as all the other *Packet types.

I haven't decided yet, maybe both of yours ideas should be adopted, which may lead to a huge API breaking chage.

zonyitoo avatar Feb 12 '21 14:02 zonyitoo

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 avatar Feb 12 '21 14:02 zonyitoo

@zonyitoo maybe a cow will help with the fields that are either owned or borrowed?

Spoonbender avatar Feb 12 '21 15:02 Spoonbender

Packet::decode_packet requires Self to have 'static lifetime.

zonyitoo avatar Feb 13 '21 16:02 zonyitoo

It should be ok to remove the Packet trait .. Hmm ... Very complicated.

zonyitoo avatar Feb 13 '21 17:02 zonyitoo

Please check if this commit works for you.

zonyitoo avatar Feb 20 '21 22:02 zonyitoo

Please check if this commit works for you.

Hmmm... so many changes ... I think my issue much less difficult to solve...

  1. Splitting Packet trait - ok, it is reasonable change!
  2. New PublishPacketRef struct - at most ok.
  3. 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

YuriK75 avatar Feb 25 '21 11:02 YuriK75

TopicNameRef is not a new struct. But I think it is safe to remove because nobody is actually using it.

zonyitoo avatar Feb 26 '21 01:02 zonyitoo

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 .

YuriK75 avatar Feb 26 '21 05:02 YuriK75

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.

zonyitoo avatar Feb 26 '21 16:02 zonyitoo

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...

YuriK75 avatar Mar 01 '21 13:03 YuriK75

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?

zonyitoo avatar Mar 01 '21 16:03 zonyitoo