Skip to content

Instantly share code, notes, and snippets.

@piotrkubisa
Last active August 29, 2017 20:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save piotrkubisa/8ccc308086378da7d2f6c0d550aded33 to your computer and use it in GitHub Desktop.
Save piotrkubisa/8ccc308086378da7d2f6c0d550aded33 to your computer and use it in GitHub Desktop.
Benchmark - Calculate the Content-length from io.Reader stream
package calc
import (
"bytes"
"io"
"io/ioutil"
)
func BufferLen(r io.Reader) int {
var buf bytes.Buffer
io.Copy(&buf, r)
return buf.Len()
}
func IoutilLen(r io.Reader) int {
bs, err := ioutil.ReadAll(r)
if err != nil {
panic(err)
}
return len(bs)
}
func IoutilOmitLen(r io.Reader) int {
bs, _ := ioutil.ReadAll(r)
return len(bs)
}
// http://stackoverflow.com/questions/24562942/golang-how-do-i-determine-the-number-of-lines-in-a-file-efficiently
func ManualBufLen(r io.Reader) int {
buf := make([]byte, 1024)
count := 0
for {
c, err := r.Read(buf)
count += c
switch {
case err == io.EOF:
return count
case err != nil:
return count
}
}
}
func IoutilLikeLen(r io.Reader) int {
buf := bytes.NewBuffer(make([]byte, 0, bytes.MinRead))
defer func() {
e := recover()
if e == nil {
return
}
if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge {
// err = panicErr
} else {
panic(e)
}
}()
buf.ReadFrom(r)
return buf.Len()
}
package calc
import (
"io"
"strings"
"testing"
)
func openStream() io.Reader {
return strings.NewReader(`Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tortor ipsum, tempor eget lorem ac, porttitor porta urna. Quisque in dapibus lorem. Ut ut sapien sed metus convallis ullamcorper. Nam suscipit tortor et posuere lacinia. Aliquam in malesuada turpis, eu vestibulum ligula. Vestibulum venenatis nunc dolor, in dapibus dolor lacinia et. Sed nec interdum odio, id convallis ipsum. Sed pulvinar lorem sit amet ante viverra blandit. Nunc vitae egestas velit, malesuada fringilla dolor. Ut ex mauris, varius et viverra sit amet, feugiat in nisl. Fusce sed nisi non ipsum porttitor ornare. Sed sed suscipit purus. Etiam vel sem ut justo pulvinar tempor. Phasellus orci ex, elementum non vestibulum ut, semper ac ipsum. Fusce suscipit sollicitudin lacus tincidunt molestie.
Pellentesque aliquet tellus sed arcu rhoncus sagittis commodo in magna. Mauris felis lorem, interdum sed neque at, ultricies sollicitudin eros. Sed hendrerit velit eget commodo semper. Aliquam at leo facilisis, ultricies quam nec, fringilla diam. Pellentesque mi mauris, consectetur eget iaculis ut, efficitur in sapien. Phasellus sit amet mauris laoreet, feugiat tortor vitae, egestas risus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris eget nisi euismod, viverra mi sit amet, consectetur turpis. Morbi gravida dignissim mi sit amet commodo. Aenean sodales eu sapien et condimentum. Quisque dapibus nec lorem eu rhoncus. Curabitur faucibus ante in odio fringilla, et rhoncus mauris rutrum. Phasellus a metus vitae nisl condimentum pharetra sodales non turpis.
Aenean in egestas erat, vitae finibus diam. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aliquam et justo erat. Sed non semper nulla. Nunc at augue rhoncus, pharetra purus at, efficitur turpis. Curabitur sapien purus, molestie sed facilisis a, sodales ac elit. Aenean eu dictum est, ac auctor diam. Aliquam maximus placerat auctor. Pellentesque tristique nunc mauris, ut cursus dui elementum nec. Sed dapibus eros enim, et elementum magna consequat vel. Nunc sodales maximus ligula, ac luctus ex facilisis vel. Quisque vestibulum enim vitae maximus ullamcorper. Curabitur quis urna pulvinar, fermentum dui a, feugiat justo.
Mauris vel ante id magna gravida sodales ac vitae quam. Suspendisse efficitur leo vel convallis molestie. Curabitur pharetra varius odio, vitae tincidunt nibh congue ac. Quisque condimentum felis felis, aliquet tempor nunc iaculis id. Donec eu cursus velit. Praesent rutrum fermentum nunc non tempus. Vestibulum lacinia ipsum vitae diam porta, eu vulputate augue consequat. Integer tempor, massa ut accumsan tincidunt, ante metus venenatis massa, ut sagittis nibh dolor a magna. Interdum et malesuada fames ac ante ipsum primis in faucibus. Curabitur at tristique nulla. Nam sem orci, ultrices interdum massa sit amet, accumsan tristique libero. Pellentesque finibus iaculis ex, et commodo ex viverra in. Ut dignissim, risus nec gravida facilisis, nibh ligula convallis massa, at maximus lorem leo ac ligula. In condimentum quam eu est dictum, sed pharetra tellus viverra. Mauris at neque eu sapien commodo vestibulum. Vestibulum varius semper aliquet.
Nunc pellentesque lacus sit amet mi blandit rhoncus. Morbi pulvinar aliquet dolor quis mollis. Nullam vulputate feugiat consequat. In auctor scelerisque est, consequat dictum ipsum ullamcorper quis. Aliquam erat volutpat. Aenean vulputate lacus vel varius luctus. Suspendisse eget ante id turpis sodales commodo. Ut ut rutrum urna, vitae tincidunt diam. Integer ultricies sit amet sem at scelerisque. Nunc nec diam et velit accumsan faucibus elementum vel nisl. Nullam ultricies nec libero sed bibendum. Cras elit leo, posuere quis ex vel, sollicitudin interdum ipsum. Duis sed tortor elit. Proin vestibulum mi quis sodales ullamcorper. Vestibulum tincidunt facilisis felis, quis finibus dui. Cras ac purus metus.`)
}
func TestBufferLen(t *testing.T) {
// 3906+ new lines as octets is 3898
stringLen := 3898
if BufferLen(openStream()) != stringLen {
t.Fatal("BufferLen !=", stringLen)
}
if IoutilLen(openStream()) != stringLen {
t.Fatal("IoutilLen !=", stringLen)
}
if IoutilOmitLen(openStream()) != stringLen {
t.Fatal("IoutilOmitLen !=", stringLen)
}
if ManualBufLen(openStream()) != stringLen {
t.Fatal("ManualBufLen !=", stringLen)
}
if IoutilLikeLen(openStream()) != stringLen {
t.Fatal("IoutilLikeLen !=", stringLen)
}
}
func BenchmarkBufferLen(b *testing.B) {
for n := 0; n < b.N; n++ {
BufferLen(openStream())
}
}
func BenchmarkIoutilLen(b *testing.B) {
for n := 0; n < b.N; n++ {
IoutilLen(openStream())
}
}
func BenchmarkIoutilOmitLen(b *testing.B) {
for n := 0; n < b.N; n++ {
IoutilOmitLen(openStream())
}
}
func BenchmarkManualBufLen(b *testing.B) {
for n := 0; n < b.N; n++ {
ManualBufLen(openStream())
}
}
func BenchmarkIoutilLikeLen(b *testing.B) {
for n := 0; n < b.N; n++ {
IoutilLikeLen(openStream())
}
}
@piotrkubisa
Copy link
Author

piotrkubisa commented Feb 11, 2017

$ go test --bench=. --benchmem

BenchmarkBufferLen-4             1000000              1309 ns/op            4240 B/op          3 allocs/op
BenchmarkIoutilLen-4              500000              3978 ns/op           14368 B/op          5 allocs/op
BenchmarkIoutilOmitLen-4          500000              3948 ns/op           14368 B/op          5 allocs/op
BenchmarkManualBufLen-4          3000000               456 ns/op            1056 B/op          2 allocs/op
BenchmarkIoutilLikeLen-4          500000              3934 ns/op           14368 B/op          5 allocs/op
PASS
ok      calc     9.233s

@piotrkubisa
Copy link
Author

piotrkubisa commented Feb 12, 2017

Please note, while that defer function in IoutilLikeLen would be removed it won't change the results, but when the capacity of the buffer will be increased it could reach magical ~1200ns/op for cap=2048. As you can notice it won't dramatically change standings, it would be
worse than ManualBufLen case and slightly better than BufferLen (my favorite ❤️ ).

Nota bene, In some cases (e.g. unhandled bytes.ErrTooLarge) some solutions may be not applicable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment