go benchmark
Practical Go Benchmarks - StackImpact
This collection of practical performance benchmarks of Go packages and algorithms aims to help developers write fast and efficient programs.
The following benchmarks evaluate various functionalities with a focus on the usability of benchmark results. Environment: Go 1.10, Linux, Intel® Core™ i7-4770HQ CPU @ 2.20GHz.
- String Concatenation
- Numeric Conversions
- Regular Expressions
- Sorting
- Random Numbers
- Random Strings
- Slice Appending
- Map Access
- Object Creation
- Hash Functions
- Base64
- File I/O
- Serialization
- Compression
- URL Parsing
- Templates
- HTTP Server
See also:
String Concatenation
This benchmark evaluates the performance of string concatenation using the +
operator, the bytes.Buffer and the strings.Builder when building a 1,000-character string. The implementations using the bytes.Buffer
and the strings.Builder
are the fastest.
package main
import (
"bytes"
"strings"
"testing"
)
var strLen int = 1000
func BenchmarkConcatString(b *testing.B) {
var str string
i := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
str += "x"
i++
if i >= strLen {
i = 0
str = ""
}
}
}
func BenchmarkConcatBuffer(b *testing.B) {
var buffer bytes.Buffer
i := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
buffer.WriteString("x")
i++
if i >= strLen {
i = 0
buffer = bytes.Buffer{}
}
}
}
func BenchmarkConcatBuilder(b *testing.B) {
var builder strings.Builder
i := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
builder.WriteString("x")
i++
if i >= strLen {
i = 0
builder = strings.Builder{}
}
}
}
$ go test -bench=. -benchmem
BenchmarkConcatString-4 10,000,000 159 ns/op 530 B/op 0 allocs/op
BenchmarkConcatBuffer-4 200,000,000 10 ns/op 2 B/op 0 allocs/op
BenchmarkConcatBuilder-4 100,000,000 11 ns/op 2 B/op 0 allocs/op
Numeric Conversions
This benchmark evaluates the performance of parsing strings to bool
, int64
and float64
types using the Go strconv package.
package main
import (
"strconv"
"testing"
)
func BenchmarkParseBool(b *testing.B) {
for n := 0; n < b.N; n++ {
_, err := strconv.ParseBool("true")
if err != nil {
panic(err)
}
}
}
func BenchmarkParseInt(b *testing.B) {
for n := 0; n < b.N; n++ {
_, err := strconv.ParseInt("7182818284", 10, 64)
if err != nil {
panic(err)
}
}
}
func BenchmarkParseFloat(b *testing.B) {
for n := 0; n < b.N; n++ {
_, err := strconv.ParseFloat("3.1415926535", 64)
if err != nil {
panic(err)
}
}
}
$ go test -bench=. -benchmem
BenchmarkParseBool-4 300,000,000 4 ns/op 0 B/op 0 allocs/op
BenchmarkParseInt-4 50,000,000 25 ns/op 0 B/op 0 allocs/op
BenchmarkParseFloat-4 50,000,000 40 ns/op 0 B/op 0 allocs/op
Regular Expressions
This benchmark evaluates the performance of regular expression matching using the Go regexp package for compiled and uncompiled regular expressions. The example uses a simple email validation regexp. As expected, the compiled regexp matching is much faster.
package main
import (
"regexp"
"testing"
)
var testRegexp string = `^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+$`
func BenchmarkMatchString(b *testing.B) {
for n := 0; n < b.N; n++ {
_, err := regexp.MatchString(testRegexp, "jsmith@example.com")
if err != nil {
panic(err)
}
}
}
func BenchmarkMatchStringCompiled(b *testing.B) {
r, err := regexp.Compile(testRegexp)
if err != nil {
panic(err)
}
b.ResetTimer()
for n := 0; n < b.N; n++ {
r.MatchString("jsmith@example.com")
}
}
$ go test -bench=. -benchmem
BenchmarkMatchString-4 100,000 17,380 ns/op 42,752 B/op 70 allocs/op
BenchmarkMatchStringCompiled-4 2,000,000 843 ns/op 0 B/op 0 allocs/op
Sorting
This benchmark evaluates the performance of sorting 1,000, 10,000, 100,000 and 1,000,000-int
elements using the built-in sorting algorithm from the Go sort package. The time complexity is documented to be O(n*log(n)), which can be observed in the results.
package main
import (
"math/rand"
"sort"
"testing"
)
func generateSlice(n int) []int {
s := make([]int, 0, n)
for i := 0; i < n; i++ {
s = append(s, rand.Intn(1e9))
}
return s
}
func BenchmarkSort1000(b *testing.B) {
for n := 0; n < b.N; n++ {
b.StopTimer()
s := generateSlice(1000)
b.StartTimer()
sort.Ints(s)
}
}
func BenchmarkSort10000(b *testing.B) {
for n := 0; n < b.N; n++ {
b.StopTimer()
s := generateSlice(10000)
b.StartTimer()
sort.Ints(s)
}
}
func BenchmarkSort100000(b *testing.B) {
for n := 0; n < b.N; n++ {
b.StopTimer()
s := generateSlice(100000)
b.StartTimer()
sort.Ints(s)
}
}
func BenchmarkSort1000000(b *testing.B) {
for n := 0; n < b.N; n++ {
b.StopTimer()
s := generateSlice(1000000)
b.StartTimer()
sort.Ints(s)
}
}
$ go test -bench=. -benchmem
BenchmarkSort1000-4 10,000 121,720 ns/op 32 B/op 1 allocs/op
BenchmarkSort10000-4 1,000 1,477,141 ns/op 32 B/op 1 allocs/op
BenchmarkSort100000-4 100 19,211,037 ns/op 32 B/op 1 allocs/op
BenchmarkSort1000000-4 5 220,539,215 ns/op 32 B/op 1 allocs/op
Random Numbers
This benchmark compares the performance of pseudorandom number generation using the Go math/rand and crypto/rand packages. The random number generation using the math/rand
package is considerably faster than the cryptographically secure random number generation using the crypto/rand
package.
package main
import (
crand "crypto/rand"
"math/big"
"math/rand"
"testing"
)
func BenchmarkMathRand(b *testing.B) {
for n := 0; n < b.N; n++ {
rand.Int63()
}
}
func BenchmarkCryptoRand(b *testing.B) {
for n := 0; n < b.N; n++ {
_, err := crand.Int(crand.Reader, big.NewInt(27))
if err != nil {
panic(err)
}
}
}
$ go test -bench=. -benchmem
BenchmarkMathRand-4 50,000,000 23 ns/op 0 B/op 0 allocs/op
BenchmarkCryptoRand-4 1,000,000 1,336 ns/op 161 B/op 5 allocs/op
Random Strings
This benchmark compares the performance of 16-character, uniformly distributed random string generation based on the Go math/rand and crypto/rand. The random string generation with using math/rand
package is faster than the cryptographically secure random string generation using the crypto/rand
package.
package main
import (
crand "crypto/rand"
"math/rand"
"testing"
)
// 64 letters
const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"
func randomBytes(n int) []byte {
bytes := make([]byte, n)
_, err := rand.Read(bytes)
if err != nil {
panic(err)
}
return bytes
}
func cryptoRandomBytes(n int) []byte {
bytes := make([]byte, n)
_, err := crand.Read(bytes)
if err != nil {
panic(err)
}
return bytes
}
func randomString(bytes []byte) string {
for i, b := range bytes {
bytes[i] = letters[b%64]
}
return string(bytes)
}
func BenchmarkMathRandString(b *testing.B) {
for n := 0; n < b.N; n++ {
randomString(randomBytes(16))
}
}
func BenchmarkCryptoRandString(b *testing.B) {
for n := 0; n < b.N; n++ {
randomString(cryptoRandomBytes(16))
}
}
$ go test -bench=. -benchmem
BenchmarkMathRandString-4 10,000,000 119 ns/op 32 B/op 2 allocs/op
BenchmarkCryptoRandString-4 2,000,000 864 ns/op 32 B/op 2 allocs/op
Slice Appending
This benchmark evaluates the performance of appending a byte
to a slice with and without slice preallocation.
package main
import (
"testing"
)
var numItems int = 1000000
func BenchmarkSliceAppend(b *testing.B) {
s := make([]byte, 0)
i := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
s = append(s, 1)
i++
if i == numItems {
b.StopTimer()
i = 0
s = make([]byte, 0)
b.StartTimer()
}
}
}
func BenchmarkSliceAppendPrealloc(b *testing.B) {
s := make([]byte, 0, numItems)
i := 0
b.ResetTimer()
for n := 0; n < b.N; n++ {
s = append(s, 1)
i++
if i == numItems {
b.StopTimer()
i = 0
s = make([]byte, 0, numItems)
b.StartTimer()
}
}
}
$ go test -bench=. -benchmem
BenchmarkSliceAppend-4 1,000,000,000 2 ns/op 5 B/op 0 allocs/op
BenchmarkSliceAppendPrealloc-4 2,000,000,000 1 ns/op 0 B/op 0 allocs/op
Map Access
This benchmark evaluates the access performance of maps with int
vs. string
keys for 1,000,000-item maps.
package main
import (
"math/rand"
"strconv"
"testing"
)
var NumItems int = 1000000
func BenchmarkMapStringKeys(b *testing.B) {
m := make(map[string]string)
k := make([]string, 0)
for i := 0; i < NumItems; i++ {
key := strconv.Itoa(rand.Intn(NumItems))
m[key] = "value" + strconv.Itoa(i)
k = append(k, key)
}
i := 0
l := len(m)
b.ResetTimer()
for n := 0; n < b.N; n++ {
if _, ok := m[k[i]]; ok {
}
i++
if i >= l {
i = 0
}
}
}
func BenchmarkMapIntKeys(b *testing.B) {
m := make(map[int]string)
k := make([]int, 0)
for i := 0; i < NumItems; i++ {
key := rand.Intn(NumItems)
m[key] = "value" + strconv.Itoa(i)
k = append(k, key)
}
i := 0
l := len(m)
b.ResetTimer()
for n := 0; n < b.N; n++ {
if _, ok := m[k[i]]; ok {
}
i++
if i >= l {
i = 0
}
}
}
$ go test -bench=. -benchmem
BenchmarkMapStringKeys-4 20,000,000 107 ns/op 0 B/op 0 allocs/op
BenchmarkMapIntKeys-4 20,000,000 65 ns/op 0 B/op 0 allocs/op
Object Creation
This benchmark evaluates the performance of object creation vs. object reuse using sync.Pool.
package main
import (
"sync"
"testing"
)
type Book struct {
Title string
Author string
Pages int
Chapters []string
}
var pool = sync.Pool{
New: func() interface{} {
return &Book{}
},
}
func BenchmarkNoPool(b *testing.B) {
var book *Book
for n := 0; n < b.N; n++ {
book = &Book{
Title: "The Art of Computer Programming, Vol. 1",
Author: "Donald E. Knuth",
Pages: 672,
}
}
_ = book
}
func BenchmarkPool(b *testing.B) {
for n := 0; n < b.N; n++ {
book := pool.Get().(*Book)
book.Title = "The Art of Computer Programming, Vol. 1"
book.Author = "Donald E. Knuth"
book.Pages = 672
pool.Put(book)
}
}
$ go test -bench=. -benchmem
BenchmarkNoPool-4 30,000,000 45 ns/op 64 B/op 1 allocs/op
BenchmarkPool-4 100,000,000 22 ns/op 0 B/op 0 allocs/op
Hash Functions
This benchmark compares the performance of multiple hash functions, including MD5, SHA1, SHA256, SHA512, SHA3-256, SHA3-512, BLAKE2d-256 and BLAKE2d-256 from internal and external Go crypto subpackages on random one-kilobyte data.
package main
import (
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"golang.org/x/crypto/blake2b"
"golang.org/x/crypto/sha3"
"hash"
"math/rand"
"testing"
)
func benchmarkHash(b *testing.B, h hash.Hash) {
data := make([]byte, 1024)
rand.Read(data)
b.ResetTimer()
for n := 0; n < b.N; n++ {
h.Write(data)
h.Sum(nil)
}
}
func BenchmarkMD5(b *testing.B) {
benchmarkHash(b, md5.New())
}
func BenchmarkSHA1(b *testing.B) {
benchmarkHash(b, sha1.New())
}
func BenchmarkSHA256(b *testing.B) {
benchmarkHash(b, sha256.New())
}
func BenchmarkSHA512(b *testing.B) {
benchmarkHash(b, sha512.New())
}
func BenchmarkSHA3256(b *testing.B) {
benchmarkHash(b, sha3.New256())
}
func BenchmarkSHA3512(b *testing.B) {
benchmarkHash(b, sha3.New512())
}
func BenchmarkBLAKE2b256(b *testing.B) {
h, _ := blake2b.New256(nil)
benchmarkHash(b, h)
}
func BenchmarkBLAKE2b512(b *testing.B) {
h, _ := blake2b.New512(nil)
benchmarkHash(b, h)
}
$ go test -bench=. -benchmem
BenchmarkMD5-4 1,000,000 1,783 ns/op 16 B/op 1 allocs/op
BenchmarkSHA1-4 1,000,000 1,504 ns/op 32 B/op 1 allocs/op
BenchmarkSHA256-4 500,000 3,201 ns/op 32 B/op 1 allocs/op
BenchmarkSHA512-4 500,000 2,596 ns/op 64 B/op 1 allocs/op
BenchmarkSHA3256-4 300,000 4,485 ns/op 512 B/op 3 allocs/op
BenchmarkSHA3512-4 200,000 7,722 ns/op 576 B/op 3 allocs/op
BenchmarkBLAKE2b256-4 1,000,000 1,311 ns/op 32 B/op 1 allocs/op
BenchmarkBLAKE2b512-4 1,000,000 1,260 ns/op 64 B/op 1 allocs/op
Base64
This benchmark evaluates the performance of Base64 encoding and decoding using the Go encoding/base64 package on one-kilobyte data.
package main
import (
"encoding/base64"
"math/rand"
"testing"
)
func BenchmarkEncode(b *testing.B) {
data := make([]byte, 1024)
rand.Read(data)
b.ResetTimer()
for n := 0; n < b.N; n++ {
base64.StdEncoding.EncodeToString([]byte(data))
}
}
func BenchmarkDecode(b *testing.B) {
data := make([]byte, 1024)
rand.Read(data)
encoded := base64.StdEncoding.EncodeToString([]byte(data))
b.ResetTimer()
for n := 0; n < b.N; n++ {
_, err := base64.StdEncoding.DecodeString(encoded)
if err != nil {
panic(err)
}
}
}
$ go test -bench=. -benchmem
BenchmarkEncode-4 1,000,000 1,876 ns/op 2,816 B/op 2 allocs/op
BenchmarkDecode-4 500,000 2,957 ns/op 2,560 B/op 2 allocs/op
File I/O
This benchmark evaluates the performance of file writing and reading a 1-MB text file line by line with and without buffering. The bufio package is used for buffered I/O.
package main
import (
"bufio"
"io"
"os"
"testing"
)
func BenchmarkWriteFile(b *testing.B) {
for n := 0; n < b.N; n++ {
f, err := os.Create("/tmp/test.txt")
if err != nil {
panic(err)
}
for i := 0; i < 100000; i++ {
f.WriteString("some text!\n")
}
f.Close()
}
}
func BenchmarkWriteFileBuffered(b *testing.B) {
for n := 0; n < b.N; n++ {
f, err := os.Create("/tmp/test.txt")
if err != nil {
panic(err)
}
w := bufio.NewWriter(f)
for i := 0; i < 100000; i++ {
w.WriteString("some text!\n")
}
w.Flush()
f.Close()
}
}
func BenchmarkReadFile(b *testing.B) {
for n := 0; n < b.N; n++ {
f, err := os.Open("/tmp/test.txt")
if err != nil {
panic(err)
}
b := make([]byte, 10)
_, err = f.Read(b)
for err == nil {
_, err = f.Read(b)
}
if err != io.EOF {
panic(err)
}
f.Close()
}
}
func BenchmarkReadFileBuffered(b *testing.B) {
for n := 0; n < b.N; n++ {
f, err := os.Open("/tmp/test.txt")
if err != nil {
panic(err)
}
r := bufio.NewReader(f)
_, err = r.ReadString('\n')
for err == nil {
_, err = r.ReadString('\n')
}
if err != io.EOF {
panic(err)
}
f.Close()
}
}
$ go test -bench=. -benchmem
BenchmarkWriteFile-4 10 127,205,360 ns/op 118 B/op 4 allocs/op
BenchmarkWriteFileBuffered-4 300 5,978,219 ns/op 4,208 B/op 5 allocs/op
BenchmarkReadFile-4 20 53,226,890 ns/op 115 B/op 4 allocs/op
BenchmarkReadFileBuffered-4 200 7,518,203 ns/op 3,204,217 B/op 200,005 allocs/op
Serialization
This benchmark evaluates the performance of serialization and deserialization using the json, protobuf, msgp and gob packages. The Protocol Buffers and MessagePack types need to be generated first.
package main
import (
"bytes"
"encoding/gob"
"encoding/json"
"github.com/golang/protobuf/proto"
"io/ioutil"
"testing"
)
type Book struct {
Title string `json:"title"`
Author string `json:"author"`
Pages int `json:"num_pages"`
Chapters []string `json:"chapters"`
}
/*
syntax = "proto2";
package main;
message BookProto {
required string title = 1;
required string author = 2;
optional int64 pages = 3;
repeated string chapters = 4;
}
*/
// protoc --go_out=. book.proto
/*
//go:generate msgp -tests=false
type BookDef struct {
Title string `msg:"title"`
Author string `msg:"author"`
Pages int `msg:"num_pages"`
Chapters []string `msg:"chapters"`
}
*/
// go generate
func generateObject() *Book {
return &Book{
Title: "The Art of Computer Programming, Vol. 2",
Author: "Donald E. Knuth",
Pages: 784,
Chapters: []string{"Random numbers", "Arithmetic"},
}
}
func generateMessagePackObject() *BookDef {
obj := generateObject()
return &BookDef{
Title: obj.Title,
Author: obj.Author,
Pages: obj.Pages,
Chapters: obj.Chapters,
}
}
func generateProtoBufObject() *BookProto {
obj := generateObject()
return &BookProto{
Title: proto.String(obj.Title),
Author: proto.String(obj.Author),
Pages: proto.Int64(int64(obj.Pages)),
Chapters: obj.Chapters,
}
}
func BenchmarkJSONMarshal(b *testing.B) {
obj := generateObject()
b.ResetTimer()
for n := 0; n < b.N; n++ {
_, err := json.Marshal(obj)
if err != nil {
panic(err)
}
}
}
func BenchmarkJSONUnmarshal(b *testing.B) {
out, err := json.Marshal(generateObject())
if err != nil {
panic(err)
}
obj := &Book{}
b.ResetTimer()
for n := 0; n < b.N; n++ {
err = json.Unmarshal(out, obj)
if err != nil {
panic(err)
}
}
}
func BenchmarkProtoBufMarshal(b *testing.B) {
obj := generateProtoBufObject()
b.ResetTimer()
for n := 0; n < b.N; n++ {
_, err := proto.Marshal(obj)
if err != nil {
panic(err)
}
}
}
func BenchmarkProtoBufUnmarshal(b *testing.B) {
out, err := proto.Marshal(generateProtoBufObject())
if err != nil {
panic(err)
}
obj := &BookProto{}
b.ResetTimer()
for n := 0; n < b.N; n++ {
err = proto.Unmarshal(out, obj)
if err != nil {
panic(err)
}
}
}
func BenchmarkMessagePackMarshal(b *testing.B) {
obj := generateMessagePackObject()
b.ResetTimer()
for n := 0; n < b.N; n++ {
_, err := obj.MarshalMsg(nil)
if err != nil {
panic(err)
}
}
}
func BenchmarkMessagePackUnmarshal(b *testing.B) {
obj := generateMessagePackObject()
msg, err := obj.MarshalMsg(nil)
if err != nil {
panic(err)
}
obj = &BookDef{}
b.ResetTimer()
for n := 0; n < b.N; n++ {
_, err = obj.UnmarshalMsg(msg)
if err != nil {
panic(err)
}
}
}
func BenchmarkGobMarshal(b *testing.B) {
obj := generateObject()
enc := gob.NewEncoder(ioutil.Discard)
b.ResetTimer()
for n := 0; n < b.N; n++ {
err := enc.Encode(obj)
if err != nil {
panic(err)
}
}
}
func BenchmarkGobUnmarshal(b *testing.B) {
obj := generateObject()
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
err := enc.Encode(obj)
if err != nil {
panic(err)
}
for n := 0; n < b.N; n++ {
err = enc.Encode(obj)
if err != nil {
panic(err)
}
}
dec := gob.NewDecoder(&buf)
b.ResetTimer()
for n := 0; n < b.N; n++ {
err = dec.Decode(&Book{})
if err != nil {
panic(err)
}
}
}
$ go test -bench=. -benchmem
BenchmarkJSONMarshal-4 1,000,000 1,239 ns/op 640 B/op 3 allocs/op
BenchmarkJSONUnmarshal-4 500,000 3,249 ns/op 432 B/op 8 allocs/op
BenchmarkProtoBufMarshal-4 3,000,000 504 ns/op 552 B/op 5 allocs/op
BenchmarkProtoBufUnmarshal-4 2,000,000 692 ns/op 432 B/op 10 allocs/op
BenchmarkMessagePackMarshal-4 10,000,000 134 ns/op 160 B/op 1 allocs/op
BenchmarkMessagePackUnmarshal-4 5,000,000 252 ns/op 112 B/op 4 allocs/op
BenchmarkGobMarshal-4 2,000,000 737 ns/op 32 B/op 1 allocs/op
BenchmarkGobUnmarshal-4 1,000,000 1,005 ns/op 272 B/op 8 allocs/op
Compression
This benchmark evaluates the performance of compressing and decompressing 100 KB of JSON data using the Go compress/gzip package.
package main
import (
"bytes"
"compress/gzip"
"io/ioutil"
"testing"
)
func BenchmarkWrite(b *testing.B) {
data, err := ioutil.ReadFile("test.json")
if err != nil {
panic(err)
}
zw := gzip.NewWriter(ioutil.Discard)
b.ResetTimer()
for n := 0; n < b.N; n++ {
_, err = zw.Write(data)
if err != nil {
panic(err)
}
}
}
func BenchmarkRead(b *testing.B) {
data, err := ioutil.ReadFile("test.json")
if err != nil {
panic(err)
}
var buf bytes.Buffer
zw := gzip.NewWriter(&buf)
_, err = zw.Write(data)
if err != nil {
panic(err)
}
err = zw.Close()
if err != nil {
panic(err)
}
r := bytes.NewReader(buf.Bytes())
zr, _ := gzip.NewReader(r)
b.ResetTimer()
for n := 0; n < b.N; n++ {
r.Reset(buf.Bytes())
zr.Reset(r)
_, err := ioutil.ReadAll(zr)
if err != nil {
panic(err)
}
}
}
$ go test -bench=. -benchmem
BenchmarkWrite-4 500 2,869,299 ns/op 1,627 B/op 0 allocs/op
BenchmarkRead-4 2,000 748,719 ns/op 261,088 B/op 22 allocs/op
URL Parsing
This benchmark evaluates the performance of URL parsing using the Go net/url package.
package main
import (
"net/url"
"testing"
)
func BenchmarkParse(b *testing.B) {
testUrl := "https://www.example.com/path/file.html?param1=value1¶m2=123"
b.ResetTimer()
for n := 0; n < b.N; n++ {
_, err := url.Parse(testUrl)
if err != nil {
panic(err)
}
}
}
$ go test -bench=. -benchmem
BenchmarkParse-4 3,000,000 413 ns/op 128 B/op 1 allocs/op
Templates
This benchmark evaluates the performance of template execution using the Go text/template package.
package main
import (
"io/ioutil"
"testing"
"text/template"
)
var bookTemplate string = `
Title: {{.Title}},
Author: {{.Author}}
{{ if .Pages}}
Number of pages: {{ .Pages }}.
{{ end }}
{{ range .Chapters }}
{{ . }},
{{ end }}
`
type Book struct {
Title string `json:"title"`
Author string `json:"author"`
Pages int `json:"num_pages"`
Chapters []string `json:"chapters"`
}
var book *Book = &Book{
Title: "The Art of Computer Programming, Vol. 3",
Author: "Donald E. Knuth",
Pages: 800,
Chapters: []string{"Sorting", "Searching"},
}
func BenchmarkExecute(b *testing.B) {
t := template.Must(template.New("book").Parse(bookTemplate))
for n := 0; n < b.N; n++ {
err := t.Execute(ioutil.Discard, book)
if err != nil {
panic(err)
}
}
}
$ go test -bench=. -benchmem
BenchmarkExecute-4 500,000 2,986 ns/op 168 B/op 12 allocs/op
HTTP Server
This benchmark evaluates the performance of HTTP and HTTPS local round trips using the default ServeMux from the Go net/http package.
package main
import (
"crypto/tls"
"io/ioutil"
"net"
"net/http"
"testing"
)
var httpServer *http.Server
var httpsServer *http.Server
type testHandler struct {
}
func (th *testHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
w.Write([]byte("OK.\n"))
}
func startHTTPServer() {
if httpServer != nil {
return
}
httpServer = &http.Server{
Handler: &testHandler{},
}
listener, err := net.Listen("tcp", ":8080")
if err != nil {
panic(err)
}
go func() {
err := httpServer.Serve(listener)
if err != nil {
panic(err)
}
}()
}
func startHTTPSServer() {
if httpsServer != nil {
return
}
httpsServer = &http.Server{
Handler: &testHandler{},
}
listener, err := net.Listen("tcp", ":8443")
if err != nil {
panic(err)
}
go func() {
err := httpServer.ServeTLS(listener, "server.crt", "server.key")
if err != nil {
panic(err)
}
}()
}
func sendRequest(client *http.Client, addr string) {
res, err := client.Get(addr)
if err != nil {
panic(err)
}
if res.StatusCode != 200 {
panic("request failed")
}
_, err = ioutil.ReadAll(res.Body)
if err != nil {
panic(err)
}
err = res.Body.Close()
if err != nil {
panic(err)
}
}
func BenchmarkHTTP(b *testing.B) {
startHTTPServer()
client := &http.Client{}
b.ResetTimer()
for n := 0; n < b.N; n++ {
sendRequest(client, "http://127.0.0.1:8080/")
}
}
func BenchmarkHTTPNoKeepAlive(b *testing.B) {
startHTTPServer()
client := &http.Client{
Transport: &http.Transport{
DisableKeepAlives: true,
},
}
b.ResetTimer()
for n := 0; n < b.N; n++ {
sendRequest(client, "http://127.0.0.1:8080/")
}
}
func BenchmarkHTTPSNoKeepAlive(b *testing.B) {
startHTTPSServer()
client := &http.Client{
Transport: &http.Transport{
DisableKeepAlives: true,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
}
b.ResetTimer()
for n := 0; n < b.N; n++ {
sendRequest(client, "https://127.0.0.1:8443/")
}
}
$ go test -bench=. -benchmem
BenchmarkHTTP-4 10,000 189,912 ns/op 5,736 B/op 70 allocs/op
BenchmarkHTTPNoKeepAlive-4 5,000 359,027 ns/op 17,204 B/op 123 allocs/op
BenchmarkHTTPSNoKeepAlive-4 300 4,052,008 ns/op 116,289 B/op 843 allocs/op