Axum : better version of this code?

Hi,

After I finally failed at porting my C++ libs to Rust, I decided to make them communicate via ipc.
It's much slower but it works.

I still wonder if I will keep the current C++ server serving C++ libs results and the Axum serving rust code.
That would be much faster.

Now, I use Axum and I have the following working code (tested, it does what it is supposed to, but I have the feeling that I do WAY too many allocations.

using ZMq in itself is bad enough in this context, but I am still learning Rust so I am open to criticism:

async fn manage(body: Bytes) -> impl IntoResponse {
    // --------------------
    // Create a new socket
    // --------------------
    let mut socket = zeromq::ReqSocket::new(); // <- Yeah... socket creation everytime. Way to cache that block ?
    socket
        .connect("ipc:///path/to/ipc/file")
        .await
        .expect("Failed to connect");

    // --------------------------------
    // Send my buffer to the C++ app
    // --------------------------------
    socket.send(body.into()).await.unwrap();

    // --------------------------------
    // Read the answer
    // --------------------------------
    let repl = socket.recv().await.unwrap();

    // --------------------------------
    // Set the final buffer
    // --------------------------------
    let mut v_out: Vec<u8> = vec![];

    // --------------------------------
    // Copy every messages received from Zmq
    // into the out vector
    // --------------------------------
    for message_part in repl.iter() {
        for byte in message_part.into_iter() { // <- Zmq returns Vec<Bytes>; Can we do better than this copy? 
            v_out.push(*byte);
        }
    }

    // --------------------------------
    // Set headers
    // --------------------------------
    let mut headers = HeaderMap::new();

    headers.insert(XXX, "My value here".parse().unwrap()); // these headers are always the same. Can we do better ?

    // --------------------------------
    // Do answer
    // --------------------------------
    return (headers, v_out);
}

Ok, I commented the code with my questions at the hot spots.

The unwrap() will be replaced with match.

In C++ I would have made the recurring variables on top of the file, as const, to avoid reallocation.

But in Rust, I am not sure how to optimize this.

Any help/opinion ?

Thanks,

Some quick wins:

async fn manage(body: Bytes) -> impl IntoResponse {

Don't use impl IntoResponse if you know the concrete response type (i.e. axum::Json<Vec<u8>).

for message_part in repl.iter() {
    for byte in message_part.into_iter() { // <- Zmq returns Vec<Bytes>; Can we do better than this copy? 
        v_out.push(*byte);
    }
}

You don't need to iterate over each byte from each message. You can simply do Vec::extend_from_slice: Vec in std::vec - Rust.

// these headers are always the same. Can we do better ?

I guess you could create a response type for your handler and implement the IntoResponse and IntoResponseParts traits.

2 Likes

Thanks Moy,

May you expand on

Don't use impl IntoResponse

Please?

Does it impact the runtime or just the compilation time or dev time (to understand what's going on) ?

It modified my code with the extend_from_slice. It's nicer :+1:
Thanks

Just the compilation time. Plus I honestly don't like to use abstractions if they don't bring anything to the table.

1 Like

Ok fair enough moy,

Thanks a lot for your clarification :saluting_face:

It doesn't really affect compilation time. RPIT is not generics.

But doesn't it involve at least a lookup throughout all of the implementors of the trait until it finds one that fulfils the one with a signature that matches the returned expression from the handler's body?

No idea how much of a hit it results in -- probably depends on how many compilation workers you have and some amount of luck -- but yes, RPIT requires more non-local analysis to figure out the opaque type.[1]

(The consideration of "RPIT or not" isn't just compilation time. If your consumers want to store the return type, concrete types are better. Or if you want to leak your variance, say.)


  1. I imagine it's less costly than monomorphization overall, but cannot back up that intuition in any way. ↩ī¸Ž

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.