Which one of the following methods is faster Tokio Async Task and Hyper HTTP response handling?

I noticed there are several ways of spawning async task and sending response in Hyper

// pseudo code

// spawning async task
// 1
let runtime = tokio::runtime
let handle = runtime.handle()
let handle2 = handle.clone()
runtime.block_on(
    handle.spawn(async move {
        loop {
            handle2.spawn(async move {

            }
        }
    })
)

// 2
let runtime = tokio::runtime
let handle = runtime.handle()
let handle2 = handle
runtime.block_on(
    tokio::task::spawn(async move {
        loop {
            tokio::task::spawn(async move {

            }
        }
    })
)


// sending response
// 1
hyper::Response::new(String)

// 2
hyper::Response::new(http_body_util::Full::from(Vec<u8>))

// 3
hyper::Response::new(http_body_util::Full::from(hyper::body::Bytes::from(Vec<u8>)))

// 4
hyper::Response::new(http_body_util::Full::from(Vec<u8>).boxed())

What are the pros and cons of that methods? and which one is the highest performance?

No difference.

1 Like

Wait is that real sis?

I asked AI for some initial knowledge before posting this question that may gain deeper information. Here is what it gave me

// 1
hyper::Response::new(String) 
// it may cause copy, and it needs utf8 validation process

// 2
hyper::Response::new(http_body_util::Full::from(Vec<u8>))
// it may causes copy

// 3
hyper::Response::new(http_body_util::Full::from(hyper::body::Bytes::from(Vec<u8>)))
// it supports zero copy because clone will only increment reference counter

// 4
hyper::Response::new(http_body_util::Full::from(Vec<u8>).boxed())
// it may causes copy. it has dynamic dispatch and 1 heap allocation
// when I checked the source code, it does have a dyn and box like this

pub struct BoxBody<D, E> {
    inner: Pin<Box<dyn Body<Data = D, Error = E> + Send + Sync + 'static>>,
}

I am not completely sure about the copying part here. Is the body really being copied when passed through multiple network layers? Or is it just moved (so no extra copy happens)?

Is there a deeper explanation of how each of these four approaches actually behave under the hood?

Alice is tokio's maintainer, so the terse answer you've got is the best you will get.

Your AI has hallucinated a bunch of inaccuracies, completely unaware that Full takes ownership without cloning, and that the underlying Buf only needs AsRef<[u8]>, so there's no validation/conversion happening.

2 Likes

Yeah, after continously rereading the source code, I think I am finally understand it

Number 1 takes string then convert it with .into_bytes().into() then consume it in a body streaming

Number 2 and 3 are the same, just http_body_util provide ready to use of number 1 method for other type

Number 4 is the one that more different, it takes Box + dyn which is 1 additional heap allocation and a dynamic dispatch which is not used in 3 previous methods that I think number 4 may be the slowest of the 4 methods, where the other 3 are equal performance. Maybe the fastest is Full<Vec>. I tried to benchmark them, it is not easy to differ the gap as the 3 of them continously swap possition :< where number 4 consistently below them, anyway this journey helped me understand it more deeper, I know now that I can implement my own with impl Body, following the source code and after rereading it continously to understand it, it is not that hard, the comment in the code really helped understanding it. Thank youuu

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.