Skip to content
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

missing content-length and ioutil.ReadAll usage #94

Open
axyz opened this issue May 13, 2018 · 0 comments
Open

missing content-length and ioutil.ReadAll usage #94

axyz opened this issue May 13, 2018 · 0 comments

Comments

@axyz
Copy link

axyz commented May 13, 2018

@aryszka @rgritti
after profiling and load testing a skrop based app I found a possible source of considerable performance penalties:

buf, err := ioutil.ReadAll(rsp.Body)

when the image get read from the body and saved on the stateBag ioutil.ReadAll is used. This will start from a very small buffer and will keep duplicating the size in order to fit the image. Considering the source images are usually pretty big in terms of size, even with an exponential growth the buffer will need to be doubled a lot of times. To do that internally the Bytes.MakeSlice function will be called. This function seems not to be very efficient and under an even very moderate load will rapidly occupy the biggest part of the flame graph (usually around 70/80%). Even when the initial slice size is similar to the input and the buffer is doubled a single time, the performance impact is very high.

To avoid the usage of ReadAll something like this can be used:

buf := bytes.NewBuffer(make([]byte, 0, rsp.ContentLength)) // eventually some extra bytes could be added to make sure no makeSlice will happen
_, err := buf.ReadFrom(rsp.Body)

// use buf.Bytes() to pass around the read bytes

of course if content-length is not set in the source there is not much we can do, but one idea may be to have an option to have a fallback initial buffer size that can be customized accordingly to the average size of the inputs.

Playing around with this approach I also discovered another strange behavior:

rsp.Body = ioutil.NopCloser(bytes.NewReader(buf))

during the FinalizeResponse the body is set to a buffer reader, I was expecting that the content-length would be set automatically accordingly to the size of the buffer, but it is actually not set, this means that having a loopback filter that will have to read from the body will lead to the same fallback case where we cannot rely on content-length in order to initialize our buffer. I tried to manually set rsp.ConentLength to the length of the buffer, but I'm not sure that is the most idiomatic way to solve the issue and it also look like the buffer size is not enough to avoid a buffer doubling and some extra bytes need to be added (tried with 4k and it never called makeSlice)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant