Skip to content

Instantly share code, notes, and snippets.

@erikdubbelboer
Last active January 31, 2021 22:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save erikdubbelboer/027b80d4330aec55a80a832737b7437c to your computer and use it in GitHub Desktop.
Save erikdubbelboer/027b80d4330aec55a80a832737b7437c to your computer and use it in GitHub Desktop.
package main
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"strconv"
"sync"
"sync/atomic"
"time"
)
var (
useBigFile bool
fileSize int
client = &http.Client{
Transport: &http.Transport{
MaxIdleConnsPerHost: 1024,
},
}
)
type writerOnly struct {
io.Writer
}
func serve(w http.ResponseWriter, r *http.Request) {
f, err := os.Open(fmt.Sprintf("out%d.file", fileSize))
if err != nil {
panic(err)
}
w.Header().Add("Content-Length", strconv.Itoa(fileSize))
if useBigFile {
// http.ResponseWriter implements io.ReadFrom triggering sendfile to be tried.
if _, err = io.Copy(w, f); err != nil {
panic(err)
}
} else {
// writerOnly doesn't implement io.ReadFrom so a normal Read/Write loop is used.
if _, err = io.Copy(writerOnly{w}, f); err != nil {
panic(err)
}
}
if err := f.Close(); err != nil {
panic(err)
}
}
func bench() float64 {
seconds := 10
end := time.Now().Add(time.Second * time.Duration(seconds))
var requests int64
var wg sync.WaitGroup
worker := func() {
for time.Now().Before(end) {
resp, err := client.Get("http://localhost:8081/")
if err != nil {
panic(err)
}
if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil {
panic(err)
}
if err := resp.Body.Close(); err != nil {
panic(err)
}
atomic.AddInt64(&requests, 1)
}
wg.Done()
}
for i := 0; i < 32; i++ {
wg.Add(1)
go worker()
}
wg.Wait()
return float64(requests) / float64(seconds)
}
func add(r []int, from, to, inc int) []int {
for i := from; i <= to; i += inc {
r = append(r, i)
}
return r
}
func size(b int) string {
const unit = 1000
div, exp := int64(unit), 0
for n := b / unit; n >= unit; n /= unit {
div *= unit
exp++
}
return fmt.Sprintf("%.1f%cb", float64(b)/float64(div), "kmg"[exp])
}
func main() {
go func() {
if err := http.ListenAndServe("127.0.0.1:8081", http.HandlerFunc(serve)); err != nil {
panic(err)
}
}()
time.Sleep(time.Second) // Give the server some time to start.
fmt.Printf("%10s: %10s vs %10s %10s\n", "size", "sendfile", "r/w", "sendfile faster")
sizes := make([]int, 0)
sizes = add(sizes, 1_000, 10_000, 1_000) // 1kb - 10kb, 1kb increments
sizes = add(sizes, 20_000, 60_000, 10_000) // 20kb - 60kb, 10kb increments
sizes = add(sizes, 65_994, 65_995, 1)
sizes = add(sizes, 70_000, 100_000, 10_000) // 70kb - 100kb, 10kb increments
sizes = add(sizes, 200_000, 1_200_000, 100_000) // 200kb - 1.2mb, 100kb increments
sizes = add(sizes, 1_500_000, 3_000_000, 500_000) // 1.5mb - 3mb, 500kb increments
sizes = add(sizes, 100_000_000, 1_000_000_000, 100_000_000) // 100mb - 1gb, 100mb increments
for _, fileSize = range sizes {
if err := ioutil.WriteFile(fmt.Sprintf("out%d.file", fileSize), make([]byte, fileSize), 0666); err != nil {
panic(err)
}
time.Sleep(time.Second)
useBigFile = false
smallRate := bench()
time.Sleep(time.Second)
useBigFile = true
bigRate := bench()
fmt.Printf("%10s: %10.2f vs %10.2f diff %10.2f%%\n", size(fileSize), bigRate, smallRate, 100-smallRate/bigRate*100)
time.Sleep(time.Second)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment