June 26, 2021 By IBM Instana Team 12 min read

This collection of practical performance benchmarks of Go packages and algorithms is designed 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:

  • Benchmark profiling with pprof
  • Go performance tuning

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 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 1 KB 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 1 KB 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&param2=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

IBM Instana and Go benchmarks

In conclusion, the IBM Instana platform stands as a powerful observability tool that empowers organizations to gain deep insights into their systems and applications. With its comprehensive monitoring capabilities, the IBM Instana solution helps businesses proactively detect and resolve issues, optimize performance and facilitate the smooth operation of their infrastructure.

Furthermore, the collection of practical performance benchmarks for Go packages adds immense value to developers using the Go programming language. By providing standardized measurements and comparisons, these benchmarks assist in making informed decisions about package selection, identifying bottlenecks and optimizing code for optimal performance.

Combined, the IBM Instana platform and the practical performance benchmarks for Go packages contribute to enhancing system reliability, scalability and overall application performance. With these tools at their disposal, organizations and developers can confidently navigate the complexities of modern software environments and deliver exceptional user experiences.

Sign up for a free, two-week trial of IBM Instana
Was this article helpful?
YesNo

More from IBM Instana

Achieving operational efficiency through Instana’s Intelligent Remediation

3 min read - With digital transformation all around us, application environments are ever growing leading to greater complexity. Organizations are turning to observability to help them proactively address performance issues efficiently and are leveraging generative AI to gain a competitive edge in delivering exceptional user experiences. This is where Instana’s Intelligent Remediation comes in, as it enhances application performance and resolves issues, before they have a chance to impact customers. Now generally available: Instana’s Intelligent Remediation Announced at IBM Think 2024, I’m happy…

Probable Root Cause: Accelerating incident remediation with causal AI 

5 min read - It has been proven time and time again that a business application’s outages are very costly. The estimated cost of an average downtime can run USD 50,000 to 500,000 per hour, and more as businesses are actively moving to digitization. The complexity of applications is growing as well, so Site Reliability Engineers (SREs) require hours—and sometimes days—to identify and resolve problems.   To alleviate this problem, we have introduced the new feature Probable Root Cause as part of Intelligent Incident…

Observe GenAI with IBM Instana Observability

6 min read - The emergence of generative artificial intelligence (GenAI), powered by large language models (LLMs) has accelerated the widespread adoption of artificial intelligence. GenAI is proving to be very effective in tackling a variety of complex use cases with AI systems operating at levels that are comparable to humans. Organisations are quickly realizing the value of AI and its transformative potential for business, adding trillions of dollars to the economy. Given this emerging landscape, IBM Instana Observability is on a mission to…

IBM Newsletters

Get our newsletters and topic updates that deliver the latest thought leadership and insights on emerging trends.
Subscribe now More newsletters