2026-07-19 00:11:36 +02:00
2018-11-04 21:01:50 +01:00
2018-12-21 03:00:14 +01:00
2026-04-25 16:23:52 +02:00
2025-12-22 22:31:26 +01:00
2024-12-05 19:50:06 +01:00
2025-01-02 19:26:34 +01:00
2018-11-04 21:01:50 +01:00
2022-08-31 17:35:30 +02:00
2025-12-22 22:31:26 +01:00
2023-04-21 17:01:32 +02:00
2025-12-22 22:31:26 +01:00
2018-12-21 03:40:26 +01:00
2018-11-04 21:01:50 +01:00
2026-04-25 16:23:52 +02:00
2018-11-04 21:01:50 +01:00
2026-04-25 16:23:52 +02:00
2026-07-05 21:42:45 +02:00
2025-12-22 22:31:26 +01:00
2024-05-11 11:42:37 +02:00
2018-10-25 01:14:42 +02:00
2026-07-19 00:11:36 +02:00
2026-07-19 00:11:36 +02:00
2026-07-19 00:11:36 +02:00
2026-07-05 21:42:45 +02:00
2026-07-19 00:11:36 +02:00
2026-07-05 21:42:45 +02:00

Go Benchmarks

test DOI Go Report Card License: MIT

In programming in general, and in Golang in particular, many roads lead to Rome. From time to time I ask myself which of these ways is the fastest. In Golang there is a wonderful solution, with go test -bench you can measure the speed very easily and quickly. In order for you to benefit from it too, I will publish such benchmarks in this repository in the future.

ToC

Golang?

I published another repository where I show some Golang examples. If you're interested in new programming languages, you should definitely take a look at Golang:

Is it any good?

Yes

Benchmark Results

Golang Version: go version go1.26.4 darwin/arm64
Hardware Spec: Apple MacBook Pro 16-Inch M2 Max 2023 (?) (buy)

base64

// Package base64 benchmarks some base64 functions.
// On all tested systems it's faster to decode a
// base64 encoded string instead of a check via regex.
package base64

import (
	"encoding/base64"
	"regexp"
	"testing"
)

func base64decode(s string) bool {
	_, err := base64.StdEncoding.DecodeString(s)
	return err == nil
}

func base64regex(s string) bool {
	matched, _ := regexp.MatchString(`^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$`, s)
	return matched
}

func BenchmarkBase64decode(b *testing.B) {
	isNotBase64 := `Invalid string`
	isBase64 := `VmFsaWQgc3RyaW5nCg==`

	for n := 0; n < b.N; n++ {
		base64decode(isNotBase64)
		base64decode(isBase64)
	}
}

func BenchmarkBase64regex(b *testing.B) {
	isNotBase64 := `Invalid string`
	isBase64 := `VmFsaWQgc3RyaW5nCg==`

	for n := 0; n < b.N; n++ {
		base64regex(isNotBase64)
		base64regex(isBase64)
	}
}
$ go test -bench . -benchmem
goos: darwin
goarch: arm64
pkg: github.com/SimonWaldherr/golang-benchmarks/base64
cpu: Apple M2 Max
BenchmarkBase64decode-12    	22453183	        53.33 ns/op	      32 B/op	       2 allocs/op
BenchmarkBase64regex-12     	  132718	      9306 ns/op	   21883 B/op	     198 allocs/op
PASS
ok  	github.com/SimonWaldherr/golang-benchmarks/base64	3.916s

between

// Package between compares the performance of checking
// if a number is between two other numbers via regex
// and by parsing the number as integers.
package between

import (
	"regexp"
	"simonwaldherr.de/go/golibs/as"
	"simonwaldherr.de/go/ranger"
	"testing"
)

func BenchmarkNumberRegEx(b *testing.B) {
	re := ranger.Compile(89, 1001)
	re = "^(" + re + ")$"
	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		matched, err := regexp.MatchString(re, "404")
		if !matched || err != nil {
			b.Log("Error in Benchmark")
		}

		matched, err = regexp.MatchString(re, "2000")
		if matched || err != nil {
			b.Log("Error in Benchmark")
		}
	}
}

func BenchmarkFulltextRegEx(b *testing.B) {
	re := ranger.Compile(89, 1001)
	re = " (" + re + ") "
	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		matched, err := regexp.MatchString(re, "lorem ipsum 404 dolor sit")
		if !matched || err != nil {
			b.Log("Error in Benchmark")
		}

		matched, err = regexp.MatchString(re, "lorem ipsum 2000 dolor sit")
		if matched || err != nil {
			b.Log("Error in Benchmark")
		}
	}
}

func BenchmarkNumberParse(b *testing.B) {
	for n := 0; n < b.N; n++ {
		i1 := as.Int("404")
		i2 := as.Int("2000")

		if i1 < 89 || i1 > 1001 {
			b.Log("Error in Benchmark")
		}

		if !(i2 < 89 || i2 > 1001) {
			b.Log("Error in Benchmark")
		}
	}
}

func BenchmarkFulltextParse(b *testing.B) {
	re := regexp.MustCompile("[0-9]+")
	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		i1 := as.Int(re.FindString("lorem ipsum 404 dolor sit"))
		i2 := as.Int(re.FindString("lorem ipsum 2000 dolor sit"))

		if i1 < 89 || i1 > 1001 {
			b.Log("Error in Benchmark")
		}

		if !(i2 < 89 || i2 > 1001) {
			b.Log("Error in Benchmark")
		}
	}
}
$ go test -bench . -benchmem
goos: darwin
goarch: arm64
pkg: github.com/SimonWaldherr/golang-benchmarks/between
cpu: Apple M2 Max
BenchmarkNumberRegEx-12      	  184639	      6869 ns/op	   16851 B/op	     142 allocs/op
BenchmarkFulltextRegEx-12    	  216879	      5570 ns/op	   12067 B/op	     104 allocs/op
BenchmarkNumberParse-12      	31583726	        38.71 ns/op	       0 B/op	       0 allocs/op
BenchmarkFulltextParse-12    	 2453598	       500.8 ns/op	      32 B/op	       2 allocs/op
PASS
ok  	github.com/SimonWaldherr/golang-benchmarks/between	6.841s

caseinsensitivecompare

package caseinsensitivecompare

import (
	"strings"
	"testing"
)

var caseInsensitiveCompareResult bool

func BenchmarkEqualFold(b *testing.B) {
	b.ReportAllocs()
	for n := 0; n < b.N; n++ {
		caseInsensitiveCompareResult = strings.EqualFold("abc", "ABC")
		caseInsensitiveCompareResult = strings.EqualFold("ABC", "ABC")
		caseInsensitiveCompareResult = strings.EqualFold("1aBcD", "1AbCd")
	}
}

func BenchmarkToUpper(b *testing.B) {
	b.ReportAllocs()
	for n := 0; n < b.N; n++ {
		caseInsensitiveCompareResult = strings.ToUpper("abc") == strings.ToUpper("ABC")
		caseInsensitiveCompareResult = strings.ToUpper("ABC") == strings.ToUpper("ABC")
		caseInsensitiveCompareResult = strings.ToUpper("1aBcD") == strings.ToUpper("1AbCd")
	}
}

func BenchmarkToLower(b *testing.B) {
	b.ReportAllocs()
	for n := 0; n < b.N; n++ {
		caseInsensitiveCompareResult = strings.ToLower("abc") == strings.ToLower("ABC")
		caseInsensitiveCompareResult = strings.ToLower("ABC") == strings.ToLower("ABC")
		caseInsensitiveCompareResult = strings.ToLower("1aBcD") == strings.ToLower("1AbCd")
	}
}
$ go test -bench . -benchmem
goos: darwin
goarch: arm64
pkg: github.com/SimonWaldherr/golang-benchmarks/caseinsensitivecompare
cpu: Apple M2 Max
BenchmarkEqualFold-12    	79624160	        14.88 ns/op	       0 B/op	       0 allocs/op
BenchmarkToUpper-12      	10302300	       115.9 ns/op	      24 B/op	       3 allocs/op
BenchmarkToLower-12      	 8338840	       149.2 ns/op	      40 B/op	       5 allocs/op
PASS
ok  	github.com/SimonWaldherr/golang-benchmarks/caseinsensitivecompare	4.985s

concat

// Package concat benchmarks the performance of
// various string concatenation methods.
// Instead of just concatenating a string to another string
// it is also possible (and much faster) to use
// a bytes buffer.
package concat

import (
	"bytes"
	"strings"
	"testing"
)

const concatParts = 64

var concatResult string

func BenchmarkConcatString(b *testing.B) {
	b.ReportAllocs()

	for n := 0; n < b.N; n++ {
		var str string
		for i := 0; i < concatParts; i++ {
			str += "x"
		}
		concatResult = str
	}
}

func BenchmarkConcatBuffer(b *testing.B) {
	b.ReportAllocs()

	for n := 0; n < b.N; n++ {
		var buffer bytes.Buffer
		for i := 0; i < concatParts; i++ {
			buffer.WriteString("x")
		}
		concatResult = buffer.String()
	}
}

func BenchmarkConcatBuilder(b *testing.B) {
	b.ReportAllocs()

	for n := 0; n < b.N; n++ {
		var builder strings.Builder
		for i := 0; i < concatParts; i++ {
			builder.WriteString("x")
		}
		concatResult = builder.String()
	}
}

func BenchmarkConcatBuilderGrow(b *testing.B) {
	b.ReportAllocs()

	for n := 0; n < b.N; n++ {
		var builder strings.Builder
		builder.Grow(concatParts)
		for i := 0; i < concatParts; i++ {
			builder.WriteString("x")
		}
		concatResult = builder.String()
	}
}

func BenchmarkConcat(b *testing.B) {
	b.Run("String", func(b *testing.B) {
		b.ReportAllocs()
		for n := 0; n < b.N; n++ {
			var str string
			for i := 0; i < concatParts; i++ {
				str += "x"
			}
			concatResult = str
		}
	})

	b.Run("Buffer", func(b *testing.B) {
		b.ReportAllocs()
		for n := 0; n < b.N; n++ {
			var buffer bytes.Buffer
			for i := 0; i < concatParts; i++ {
				buffer.WriteString("x")
			}
			concatResult = buffer.String()
		}
	})

	b.Run("Builder", func(b *testing.B) {
		b.ReportAllocs()
		for n := 0; n < b.N; n++ {
			var builder strings.Builder
			for i := 0; i < concatParts; i++ {
				builder.WriteString("x")
			}
			concatResult = builder.String()
		}
	})

	b.Run("BuilderGrow", func(b *testing.B) {
		b.ReportAllocs()
		for n := 0; n < b.N; n++ {
			var builder strings.Builder
			builder.Grow(concatParts)
			for i := 0; i < concatParts; i++ {
				builder.WriteString("x")
			}
			concatResult = builder.String()
		}
	})
}
$ go test -bench . -benchmem
goos: darwin
goarch: arm64
pkg: github.com/SimonWaldherr/golang-benchmarks/concat
cpu: Apple M2 Max
BenchmarkConcatString-12         	  852015	      1395 ns/op	    2400 B/op	      63 allocs/op
BenchmarkConcatBuffer-12         	 4648881	       256.4 ns/op	     128 B/op	       2 allocs/op
BenchmarkConcatBuilder-12        	 7261530	       171.8 ns/op	     120 B/op	       4 allocs/op
BenchmarkConcatBuilderGrow-12    	 8945671	       134.8 ns/op	      64 B/op	       1 allocs/op
BenchmarkConcat/String-12        	  759433	      1443 ns/op	    2400 B/op	      63 allocs/op
BenchmarkConcat/Buffer-12        	 4670925	       260.9 ns/op	     128 B/op	       2 allocs/op
BenchmarkConcat/Builder-12       	 7177146	       169.7 ns/op	     120 B/op	       4 allocs/op
BenchmarkConcat/BuilderGrow-12   	 8887440	       135.8 ns/op	      64 B/op	       1 allocs/op
PASS
ok  	github.com/SimonWaldherr/golang-benchmarks/concat	12.059s

contains

// Package contains tests various ways of checking
// if a string is contained in another string.
package contains

import (
	"bytes"
	"regexp"
	"strings"
	"testing"
)

// strings.Contains
func contains() bool {
	return strings.Contains("Lorem Ipsum", "em Ip")
}

func containsNot() bool {
	return strings.Contains("Lorem Ipsum", "Dolor")
}

func TestContains(t *testing.T) {
	if contains() == false {
		t.Error("ERROR: contains")
	}
	if containsNot() == true {
		t.Error("ERROR: contains not")
	}
}

func BenchmarkContains(b *testing.B) {
	for n := 0; n < b.N; n++ {
		contains()
	}
}

func BenchmarkContainsNot(b *testing.B) {
	for n := 0; n < b.N; n++ {
		containsNot()
	}
}

// bytes.Contains
func containsBytes() bool {
	return bytes.Contains([]byte("Lorem Ipsum"), []byte("em Ip"))
}

func containsBytesNot() bool {
	return bytes.Contains([]byte("Lorem Ipsum"), []byte("Dolor"))
}

func TestContainsBytes(t *testing.T) {
	if containsBytes() == false {
		t.Error("ERROR: bytes contains")
	}
	if containsBytesNot() == true {
		t.Error("ERROR: bytes contains not")
	}
}

func BenchmarkContainsBytes(b *testing.B) {
	for n := 0; n < b.N; n++ {
		containsBytes()
	}
}

func BenchmarkContainsBytesNot(b *testing.B) {
	for n := 0; n < b.N; n++ {
		containsBytesNot()
	}
}

// regexp.MustCompile + regexp.MatchString
func compileMatch(re *regexp.Regexp) bool {
	matched := re.MatchString("Lorem Ipsum")
	return matched
}

func compileMatchNot(re *regexp.Regexp) bool {
	matched := re.MatchString("Lorem Ipsum")
	return matched
}

func TestCompileMatch(t *testing.T) {
	re1 := regexp.MustCompile("em Ip")
	re2 := regexp.MustCompile("Dolor")
	if compileMatch(re1) == false {
		t.Error("ERROR: compile match")
	}
	if compileMatchNot(re2) == true {
		t.Error("ERROR: compile match not")
	}
}

func BenchmarkCompileMatch(b *testing.B) {
	re := regexp.MustCompile("em Ip")
	for n := 0; n < b.N; n++ {
		compileMatch(re)
	}
}

func BenchmarkCompileMatchNot(b *testing.B) {
	re := regexp.MustCompile("Dolor")
	for n := 0; n < b.N; n++ {
		compileMatchNot(re)
	}
}

// regexp.MatchString
func match() bool {
	matched, _ := regexp.MatchString("em Ip", "Lorem Ipsum")
	return matched
}

func matchNot() bool {
	matched, _ := regexp.MatchString("Dolor", "Lorem Ipsum")
	return matched
}

func TestMatch(t *testing.T) {
	if match() == false {
		t.Error("ERROR: match")
	}
	if matchNot() == true {
		t.Error("ERROR: match not")
	}
}

func BenchmarkMatch(b *testing.B) {
	for n := 0; n < b.N; n++ {
		match()
	}
}

func BenchmarkMatchNot(b *testing.B) {
	for n := 0; n < b.N; n++ {
		matchNot()
	}
}

// BenchmarkContainsMethods benchmarks different methods to check substring presence.
func BenchmarkContainsMethods(b *testing.B) {
	b.Run("Strings.Contains", func(b *testing.B) {
		str := "Lorem Ipsum"
		substr := "em Ip"
		for n := 0; n < b.N; n++ {
			_ = strings.Contains(str, substr)
			_ = strings.Contains(str, "Dolor")
		}
	})

	b.Run("Bytes.Contains", func(b *testing.B) {
		str := []byte("Lorem Ipsum")
		substr := []byte("em Ip")
		for n := 0; n < b.N; n++ {
			_ = bytes.Contains(str, substr)
			_ = bytes.Contains(str, []byte("Dolor"))
		}
	})

	b.Run("RegexMatchString", func(b *testing.B) {
		re := regexp.MustCompile(`em Ip`)
		for n := 0; n < b.N; n++ {
			_ = re.MatchString("Lorem Ipsum")
			_ = re.MatchString("Dolor")
		}
	})

	b.Run("RegexMatch", func(b *testing.B) {
		for n := 0; n < b.N; n++ {
			_, _ = regexp.MatchString(`em Ip`, "Lorem Ipsum")
			_, _ = regexp.MatchString(`em Ip`, "Dolor")
		}
	})
}
$ go test -bench . -benchmem
goos: darwin
goarch: arm64
pkg: github.com/SimonWaldherr/golang-benchmarks/contains
cpu: Apple M2 Max
BenchmarkContains-12            	201775992	         5.825 ns/op	       0 B/op	       0 allocs/op
BenchmarkContainsNot-12         	100000000	        10.16 ns/op	       0 B/op	       0 allocs/op
BenchmarkContainsBytes-12       	187531548	         6.432 ns/op	       0 B/op	       0 allocs/op
BenchmarkContainsBytesNot-12    	100000000	        10.79 ns/op	       0 B/op	       0 allocs/op
BenchmarkCompileMatch-12        	25729263	        47.36 ns/op	       0 B/op	       0 allocs/op
BenchmarkCompileMatchNot-12     	42720841	        28.09 ns/op	       0 B/op	       0 allocs/op
BenchmarkMatch-12               	 1639306	       728.4 ns/op	    1398 B/op	      17 allocs/op
BenchmarkMatchNot-12            	 1749469	       701.1 ns/op	    1398 B/op	      17 allocs/op
BenchmarkContainsMethods/Strings.Contains-12         	70008436	        17.14 ns/op	       0 B/op	       0 allocs/op
BenchmarkContainsMethods/Bytes.Contains-12           	67084857	        17.83 ns/op	       0 B/op	       0 allocs/op
BenchmarkContainsMethods/RegexMatchString-12         	17729251	        69.20 ns/op	       0 B/op	       0 allocs/op
BenchmarkContainsMethods/RegexMatch-12               	  808454	      1440 ns/op	    2797 B/op	      34 allocs/op
PASS
ok  	github.com/SimonWaldherr/golang-benchmarks/contains	17.365s

concurrency_counter

package concurrency_counter

import (
	"fmt"
	"runtime"
	"sync"
	"sync/atomic"
	"testing"
)

// Benchmarks comparing common concurrency counter patterns.
// Variants:
// - sync.Mutex (write)
// - sync.RWMutex (write + read-heavy)
// - atomic.AddInt64
// - channels (various buffer sizes)
// - worker-pool (fixed workers consuming jobs)

func BenchmarkMutexParallel(b *testing.B) {
	var mu sync.Mutex
	var counter int64

	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			mu.Lock()
			counter++
			mu.Unlock()
		}
	})
}

func BenchmarkRWMutexWriteParallel(b *testing.B) {
	var mu sync.RWMutex
	var counter int64

	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			mu.Lock()
			counter++
			mu.Unlock()
		}
	})
}

func BenchmarkRWMutexReadParallel(b *testing.B) {
	var mu sync.RWMutex
	var counter int64

	// populate counter
	counter = 42

	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			mu.RLock()
			_ = counter
			mu.RUnlock()
		}
	})
}

func BenchmarkAtomicParallel(b *testing.B) {
	var counter int64

	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			atomic.AddInt64(&counter, 1)
		}
	})
}

func BenchmarkChannelBufferedSizes(b *testing.B) {
	sizes := []int{0, 1, 16, 1024}

	for _, sz := range sizes {
		b.Run(fmt.Sprintf("buf=%d", sz), func(b *testing.B) {
			ch := make(chan struct{}, sz)
			var wg sync.WaitGroup
			var counter int64

			wg.Add(1)
			go func() {
				for range ch {
					atomic.AddInt64(&counter, 1)
				}
				wg.Done()
			}()

			b.RunParallel(func(pb *testing.PB) {
				for pb.Next() {
					ch <- struct{}{}
				}
			})

			close(ch)
			wg.Wait()
		})
	}
}

func BenchmarkWorkerPool(b *testing.B) {
	workers := runtime.GOMAXPROCS(0)

	b.Run(fmt.Sprintf("workers=%d", workers), func(b *testing.B) {
		jobs := make(chan struct{}, 1024)
		var wg sync.WaitGroup
		var counter int64

		wg.Add(workers)
		for i := 0; i < workers; i++ {
			go func() {
				for range jobs {
					atomic.AddInt64(&counter, 1)
				}
				wg.Done()
			}()
		}

		b.RunParallel(func(pb *testing.PB) {
			for pb.Next() {
				jobs <- struct{}{}
			}
		})

		close(jobs)
		wg.Wait()
	})
}
$ go test -bench . -benchmem
goos: darwin
goarch: arm64
pkg: github.com/SimonWaldherr/golang-benchmarks/concurrency_counter
cpu: Apple M2 Max
BenchmarkMutexParallel-12           	 9291856	       120.6 ns/op	       0 B/op	       0 allocs/op
BenchmarkRWMutexWriteParallel-12    	12324819	        99.30 ns/op	       0 B/op	       0 allocs/op
BenchmarkRWMutexReadParallel-12     	11253630	       108.3 ns/op	       0 B/op	       0 allocs/op
BenchmarkAtomicParallel-12          	21872348	        55.59 ns/op	       0 B/op	       0 allocs/op
BenchmarkChannelBufferedSizes/buf=0-12         	 3489591	       337.9 ns/op	       0 B/op	       0 allocs/op
BenchmarkChannelBufferedSizes/buf=1-12         	 4211640	       289.5 ns/op	       0 B/op	       0 allocs/op
BenchmarkChannelBufferedSizes/buf=16-12        	 6641338	       179.7 ns/op	       0 B/op	       0 allocs/op
BenchmarkChannelBufferedSizes/buf=1024-12      	 9428004	       128.2 ns/op	       0 B/op	       0 allocs/op
BenchmarkWorkerPool/workers=12-12              	17087697	        69.67 ns/op	       0 B/op	       0 allocs/op
PASS
ok  	github.com/SimonWaldherr/golang-benchmarks/concurrency_counter	12.524s

embed

package embed

import (
	_ "embed"
	"io/ioutil"
	"os"
	"testing"
)

//go:embed example.txt
var embeddedFile []byte

func BenchmarkEmbed(b *testing.B) {
	for i := 0; i < b.N; i++ {
		// Access the embedded file
		_ = embeddedFile
	}
}

func BenchmarkReadFile(b *testing.B) {
	for i := 0; i < b.N; i++ {
		// Read the file from disk
		data, err := os.ReadFile("example.txt")
		if err != nil {
			b.Fatalf("failed to read file: %v", err)
		}
		_ = data
	}
}

func BenchmarkIoutilReadFile(b *testing.B) {
	for i := 0; i < b.N; i++ {
		// Read the file using ioutil
		data, err := ioutil.ReadFile("example.txt")
		if err != nil {
			b.Fatalf("failed to read file: %v", err)
		}
		_ = data
	}
}
$ go test -bench . -benchmem
goos: darwin
goarch: arm64
pkg: github.com/SimonWaldherr/golang-benchmarks/embed
cpu: Apple M2 Max
BenchmarkEmbed-12             	1000000000	         0.3083 ns/op	       0 B/op	       0 allocs/op
BenchmarkReadFile-12          	  109200	     11171 ns/op	     840 B/op	       5 allocs/op
BenchmarkIoutilReadFile-12    	  109106	     11392 ns/op	     840 B/op	       5 allocs/op
PASS
ok  	github.com/SimonWaldherr/golang-benchmarks/embed	3.444s

floodfill

// Package floodfill benchmarks various flood fill implementations.
package main

import (
	"testing"
)

// Rekursive Implementierung
func floodFillRecursive(image [][]int, sr int, sc int, newColor int) [][]int {
	oldColor := image[sr][sc]
	if oldColor == newColor {
		return image
	}
	floodFillRecurse(image, sr, sc, oldColor, newColor)
	return image
}

func floodFillRecurse(image [][]int, sr int, sc int, oldColor int, newColor int) {
	if sr < 0 || sr >= len(image) || sc < 0 || sc >= len(image[0]) || image[sr][sc] != oldColor {
		return
	}
	image[sr][sc] = newColor

	floodFillRecurse(image, sr+1, sc, oldColor, newColor)
	floodFillRecurse(image, sr-1, sc, oldColor, newColor)
	floodFillRecurse(image, sr, sc+1, oldColor, newColor)
	floodFillRecurse(image, sr, sc-1, oldColor, newColor)
}

// Iterative Implementierung mit Stack (DFS)
func floodFillDFS(image [][]int, sr int, sc int, newColor int) [][]int {
	oldColor := image[sr][sc]
	if oldColor == newColor {
		return image
	}

	stack := [][]int{{sr, sc}}
	for len(stack) > 0 {
		current := stack[len(stack)-1]
		stack = stack[:len(stack)-1]

		r, c := current[0], current[1]
		if r < 0 || r >= len(image) || c < 0 || c >= len(image[0]) || image[r][c] != oldColor {
			continue
		}

		image[r][c] = newColor

		stack = append(stack, []int{r + 1, c})
		stack = append(stack, []int{r - 1, c})
		stack = append(stack, []int{r, c + 1})
		stack = append(stack, []int{r, c - 1})
	}
	return image
}

// Iterative Implementierung mit Queue (BFS)
func floodFillBFS(image [][]int, sr int, sc int, newColor int) [][]int {
	oldColor := image[sr][sc]
	if oldColor == newColor {
		return image
	}

	queue := [][]int{{sr, sc}}
	for len(queue) > 0 {
		current := queue[0]
		queue = queue[1:]

		r, c := current[0], current[1]
		if r < 0 || r >= len(image) || c < 0 || c >= len(image[0]) || image[r][c] != oldColor {
			continue
		}

		image[r][c] = newColor

		queue = append(queue, []int{r + 1, c})
		queue = append(queue, []int{r - 1, c})
		queue = append(queue, []int{r, c + 1})
		queue = append(queue, []int{r, c - 1})
	}
	return image
}

// Iterative Implementierung mit Stack (4-Wege-Verbindung)
func floodFillStack4Way(image [][]int, sr int, sc int, newColor int) [][]int {
	oldColor := image[sr][sc]
	if oldColor == newColor {
		return image
	}

	stack := [][]int{{sr, sc}}
	directions := [][]int{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}
	for len(stack) > 0 {
		current := stack[len(stack)-1]
		stack = stack[:len(stack)-1]

		r, c := current[0], current[1]
		if r < 0 || r >= len(image) || c < 0 || c >= len(image[0]) || image[r][c] != oldColor {
			continue
		}

		image[r][c] = newColor

		for _, dir := range directions {
			stack = append(stack, []int{r + dir[0], c + dir[1]})
		}
	}
	return image
}

// Komplexes Beispielbild
var complexImage = [][]int{
	{0, 0, 0, 0, 0, 0},
	{0, 1, 1, 0, 1, 0},
	{0, 1, 0, 0, 1, 0},
	{0, 1, 1, 1, 1, 0},
	{0, 0, 0, 0, 0, 0},
	{0, 1, 1, 0, 1, 1},
}

// Benchmark für die rekursive Implementierung
func BenchmarkFloodFillRecursive(b *testing.B) {
	for i := 0; i < b.N; i++ {
		image := make([][]int, len(complexImage))
		for j := range complexImage {
			image[j] = make([]int, len(complexImage[j]))
			copy(image[j], complexImage[j])
		}
		floodFillRecursive(image, 1, 1, 2)
	}
}

// Benchmark für die iterative Implementierung mit Stack (DFS)
func BenchmarkFloodFillDFS(b *testing.B) {
	for i := 0; i < b.N; i++ {
		image := make([][]int, len(complexImage))
		for j := range complexImage {
			image[j] = make([]int, len(complexImage[j]))
			copy(image[j], complexImage[j])
		}
		floodFillDFS(image, 1, 1, 2)
	}
}

// Benchmark für die iterative Implementierung mit Queue (BFS)
func BenchmarkFloodFillBFS(b *testing.B) {
	for i := 0; i < b.N; i++ {
		image := make([][]int, len(complexImage))
		for j := range complexImage {
			image[j] = make([]int, len(complexImage[j]))
			copy(image[j], complexImage[j])
		}
		floodFillBFS(image, 1, 1, 2)
	}
}

// Benchmark für die iterative Implementierung mit Stack (4-Wege-Verbindung)
func BenchmarkFloodFillStack4Way(b *testing.B) {
	for i := 0; i < b.N; i++ {
		image := make([][]int, len(complexImage))
		for j := range complexImage {
			image[j] = make([]int, len(complexImage[j]))
			copy(image[j], complexImage[j])
		}
		floodFillStack4Way(image, 1, 1, 2)
	}
}
$ go test -bench . -benchmem
goos: darwin
goarch: arm64
pkg: github.com/SimonWaldherr/golang-benchmarks/floodfill
cpu: Apple M2 Max
BenchmarkFloodFillRecursive-12    	 4974576	       207.7 ns/op	     432 B/op	       7 allocs/op
BenchmarkFloodFillDFS-12          	 1401861	       873.0 ns/op	    1744 B/op	      48 allocs/op
BenchmarkFloodFillBFS-12          	 1000000	      1061 ns/op	    2704 B/op	      53 allocs/op
BenchmarkFloodFillStack4Way-12    	 1325946	       914.5 ns/op	    1744 B/op	      48 allocs/op
PASS
ok  	github.com/SimonWaldherr/golang-benchmarks/floodfill	6.878s

foreach

// Package foreach benchmarks ranging over slices and maps.
package foreach

import (
	"testing"
)

var amap map[int]string
var aslice []string

func init() {
	amap = map[int]string{
		0: "lorem",
		1: "ipsum",
		2: "dolor",
		3: "sit",
		4: "amet",
	}

	aslice = []string{
		"lorem",
		"ipsum",
		"dolor",
		"sit",
		"amet",
	}
}

func forMap() {
	for i := 0; i < len(amap); i++ {
		_ = amap[i]
	}
}

func rangeMap() {
	for _, v := range amap {
		_ = v
	}
}

func rangeSlice() {
	for _, v := range aslice {
		_ = v
	}
}

func rangeSliceKey() {
	for k := range aslice {
		_ = aslice[k]
	}
}

func BenchmarkForMap(b *testing.B) {
	for n := 0; n < b.N; n++ {
		forMap()
	}
}

func BenchmarkRangeMap(b *testing.B) {
	for n := 0; n < b.N; n++ {
		rangeMap()
	}
}

func BenchmarkRangeSlice(b *testing.B) {
	for n := 0; n < b.N; n++ {
		rangeSlice()
	}
}

func BenchmarkRangeSliceKey(b *testing.B) {
	for n := 0; n < b.N; n++ {
		rangeSliceKey()
	}
}
$ go test -bench . -benchmem
goos: darwin
goarch: arm64
pkg: github.com/SimonWaldherr/golang-benchmarks/foreach
cpu: Apple M2 Max
BenchmarkForMap-12           	62622891	        18.89 ns/op	       0 B/op	       0 allocs/op
BenchmarkRangeMap-12         	27815659	        43.15 ns/op	       0 B/op	       0 allocs/op
BenchmarkRangeSlice-12       	435543494	         2.744 ns/op	       0 B/op	       0 allocs/op
BenchmarkRangeSliceKey-12    	441164779	         2.737 ns/op	       0 B/op	       0 allocs/op
PASS
ok  	github.com/SimonWaldherr/golang-benchmarks/foreach	6.570s

hash

// Package hash benchmarks various hashing algorithms.
// Especially with hashing algorithms, faster is not always better.
// One should always decide on the basis of the respective requirements.
package hash

import (
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"hash"
	"hash/adler32"
	"hash/crc32"
	"hash/crc64"
	"hash/fnv"
	"math/rand"
	"testing"

	"github.com/jzelinskie/whirlpool"
	"github.com/reusee/mmh3"
	"github.com/zeebo/blake3"
	"golang.org/x/crypto/bcrypt"
	"golang.org/x/crypto/blake2b"
	"golang.org/x/crypto/md4"
	"golang.org/x/crypto/ripemd160"
	"golang.org/x/crypto/sha3"
)

func benchmarkHashAlgo(b *testing.B, h hash.Hash) {
	data := make([]byte, 2048)
	rand.Read(data)

	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		h.Reset()
		h.Write(data)
		_ = h.Sum(nil)
	}
}

func benchmarkBCryptHashAlgo(b *testing.B, cost int) {
	data := make([]byte, 2048)
	rand.Read(data)

	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		bcrypt.GenerateFromPassword(data, cost)
	}
}

func BenchmarkAdler32(b *testing.B) {
	benchmarkHashAlgo(b, adler32.New())
}

func BenchmarkBCryptCost4(b *testing.B) {
	benchmarkBCryptHashAlgo(b, 4)
}

func BenchmarkBCryptCost10(b *testing.B) {
	benchmarkBCryptHashAlgo(b, 10)
}

func BenchmarkBCryptCost16(b *testing.B) {
	benchmarkBCryptHashAlgo(b, 16)
}

/*
func BenchmarkBCryptCost22(b *testing.B) {
	benchmarkBCryptHashAlgo(b, 22)
}

func BenchmarkBCryptCost28(b *testing.B) {
	benchmarkBCryptHashAlgo(b, 28)
}

func BenchmarkBCryptCost31(b *testing.B) {
	benchmarkBCryptHashAlgo(b, 31)
}
*/

func BenchmarkBlake2b256(b *testing.B) {
	h, err := blake2b.New256(nil)
	if err != nil {
		b.Fatal(err)
	}
	benchmarkHashAlgo(b, h)
}

func BenchmarkBlake2b512(b *testing.B) {
	h, err := blake2b.New512(nil)
	if err != nil {
		b.Fatal(err)
	}
	benchmarkHashAlgo(b, h)
}

func BenchmarkBlake3256(b *testing.B) {
	benchmarkHashAlgo(b, blake3.New())
}

func BenchmarkMMH3(b *testing.B) {
	benchmarkHashAlgo(b, mmh3.New128())
}

func BenchmarkCRC32(b *testing.B) {
	benchmarkHashAlgo(b, crc32.NewIEEE())
}

func BenchmarkCRC64ISO(b *testing.B) {
	benchmarkHashAlgo(b, crc64.New(crc64.MakeTable(crc64.ISO)))
}

func BenchmarkCRC64ECMA(b *testing.B) {
	benchmarkHashAlgo(b, crc64.New(crc64.MakeTable(crc64.ECMA)))
}

func BenchmarkFnv32(b *testing.B) {
	benchmarkHashAlgo(b, fnv.New32())
}

func BenchmarkFnv32a(b *testing.B) {
	benchmarkHashAlgo(b, fnv.New32a())
}

func BenchmarkFnv64(b *testing.B) {
	benchmarkHashAlgo(b, fnv.New64())
}

func BenchmarkFnv64a(b *testing.B) {
	benchmarkHashAlgo(b, fnv.New64a())
}

func BenchmarkFnv128(b *testing.B) {
	benchmarkHashAlgo(b, fnv.New128())
}

func BenchmarkFnv128a(b *testing.B) {
	benchmarkHashAlgo(b, fnv.New128a())
}

func BenchmarkMD4(b *testing.B) {
	benchmarkHashAlgo(b, md4.New())
}

func BenchmarkMD5(b *testing.B) {
	benchmarkHashAlgo(b, md5.New())
}

func BenchmarkSHA1(b *testing.B) {
	benchmarkHashAlgo(b, sha1.New())
}

func BenchmarkSHA224(b *testing.B) {
	benchmarkHashAlgo(b, sha256.New224())
}

func BenchmarkSHA256(b *testing.B) {
	benchmarkHashAlgo(b, sha256.New())
}

func BenchmarkSHA384(b *testing.B) {
	benchmarkHashAlgo(b, sha512.New384())
}

func BenchmarkSHA512(b *testing.B) {
	benchmarkHashAlgo(b, sha512.New())
}

func BenchmarkSHA3256(b *testing.B) {
	benchmarkHashAlgo(b, sha3.New256())
}

func BenchmarkSHA3512(b *testing.B) {
	benchmarkHashAlgo(b, sha3.New512())
}

func BenchmarkRIPEMD160(b *testing.B) {
	benchmarkHashAlgo(b, ripemd160.New())
}

func BenchmarkWhirlpool(b *testing.B) {
	benchmarkHashAlgo(b, whirlpool.New())
}

func BenchmarkSHA256Parallel(b *testing.B) {
	b.RunParallel(func(pb *testing.PB) {
		data := make([]byte, 2048)
		rand.Read(data)
		for pb.Next() {
			h := sha256.New()
			h.Write(data)
			h.Sum(nil)
		}
	})
}
$ go test -bench . -benchmem
goos: darwin
goarch: arm64
pkg: github.com/SimonWaldherr/golang-benchmarks/hash
cpu: Apple M2 Max
BenchmarkAdler32-12           	 1737417	       682.8 ns/op	       8 B/op	       1 allocs/op
BenchmarkBCryptCost4-12       	606368766	         1.978 ns/op	       0 B/op	       0 allocs/op
BenchmarkBCryptCost10-12      	604480965	         2.005 ns/op	       0 B/op	       0 allocs/op
BenchmarkBCryptCost16-12      	605540822	         1.978 ns/op	       0 B/op	       0 allocs/op
BenchmarkBlake2b256-12        	  462399	      2582 ns/op	      32 B/op	       1 allocs/op
BenchmarkBlake2b512-12        	  461582	      2595 ns/op	      64 B/op	       1 allocs/op
BenchmarkBlake3256-12         	  402945	      2977 ns/op	      32 B/op	       1 allocs/op
BenchmarkMMH3-12              	 3553189	       337.4 ns/op	      16 B/op	       1 allocs/op
BenchmarkCRC32-12             	 4898815	       245.2 ns/op	       8 B/op	       1 allocs/op
BenchmarkCRC64ISO-12          	 1000000	      1145 ns/op	       8 B/op	       1 allocs/op
BenchmarkCRC64ECMA-12         	 1000000	      1147 ns/op	       8 B/op	       1 allocs/op
BenchmarkFnv32-12             	  481435	      2481 ns/op	       8 B/op	       1 allocs/op
BenchmarkFnv32a-12            	  483480	      2484 ns/op	       8 B/op	       1 allocs/op
BenchmarkFnv64-12             	  487173	      2466 ns/op	       8 B/op	       1 allocs/op
BenchmarkFnv64a-12            	  488792	      2473 ns/op	       8 B/op	       1 allocs/op
BenchmarkFnv128-12            	  182648	      6550 ns/op	      24 B/op	       2 allocs/op
BenchmarkFnv128a-12           	  159720	      7647 ns/op	      24 B/op	       2 allocs/op
BenchmarkMD4-12               	  405478	      2993 ns/op	      16 B/op	       1 allocs/op
BenchmarkMD5-12               	  388405	      3103 ns/op	      16 B/op	       1 allocs/op
BenchmarkSHA1-12              	 1387693	       867.0 ns/op	      24 B/op	       1 allocs/op
BenchmarkSHA224-12            	 1381546	       869.8 ns/op	      32 B/op	       1 allocs/op
BenchmarkSHA256-12            	 1381494	       868.9 ns/op	      32 B/op	       1 allocs/op
BenchmarkSHA384-12            	  790014	      1509 ns/op	      48 B/op	       1 allocs/op
BenchmarkSHA512-12            	  793952	      1516 ns/op	      64 B/op	       1 allocs/op
BenchmarkSHA3256-12           	  473754	      2589 ns/op	      32 B/op	       1 allocs/op
BenchmarkSHA3512-12           	  264368	      4534 ns/op	      64 B/op	       1 allocs/op
BenchmarkRIPEMD160-12         	  193940	      6178 ns/op	      24 B/op	       1 allocs/op
BenchmarkWhirlpool-12         	   46864	     26365 ns/op	      64 B/op	       1 allocs/op
BenchmarkSHA256Parallel-12    	11531286	       101.2 ns/op	      32 B/op	       1 allocs/op
PASS
ok  	github.com/SimonWaldherr/golang-benchmarks/hash	40.480s

index

// Package index benchmarks access on maps with various data types as keys.
package index

import (
	"math/rand"
	"strconv"
	"testing"
)

var NumItems int = 1000000

var ms map[string]string
var ks []string

var mi map[int]string
var ki []int

func initMapStringIndex() {
	ms = make(map[string]string)
	ks = make([]string, 0)

	for i := 0; i < NumItems; i++ {
		key := strconv.Itoa(rand.Intn(NumItems))
		ms[key] = "value" + strconv.Itoa(i)
		ks = append(ks, key)
	}
}

func initMapIntIndex() {
	mi = make(map[int]string)
	ki = make([]int, 0)

	for i := 0; i < NumItems; i++ {
		key := rand.Intn(NumItems)
		mi[key] = "value" + strconv.Itoa(i)
		ki = append(ki, key)
	}
}

func init() {
	initMapStringIndex()
	initMapIntIndex()
}

func BenchmarkMapStringKeys(b *testing.B) {
	i := 0

	for n := 0; n < b.N; n++ {
		if _, ok := ms[ks[i]]; ok {
		}

		i++
		if i >= NumItems {
			i = 0
		}
	}
}

func BenchmarkMapIntKeys(b *testing.B) {
	i := 0

	for n := 0; n < b.N; n++ {
		if _, ok := mi[ki[i]]; ok {
		}

		i++
		if i >= NumItems {
			i = 0
		}
	}
}

func BenchmarkMapStringIndex(b *testing.B) {
	i := 0

	for n := 0; n < b.N; n++ {
		_ = ms[ks[i]]

		i++
		if i >= NumItems {
			i = 0
		}
	}
}

func BenchmarkMapIntIndex(b *testing.B) {
	i := 0

	for n := 0; n < b.N; n++ {
		_ = mi[ki[i]]

		i++
		if i >= NumItems {
			i = 0
		}
	}
}
$ go test -bench . -benchmem
goos: darwin
goarch: arm64
pkg: github.com/SimonWaldherr/golang-benchmarks/index
cpu: Apple M2 Max
BenchmarkMapStringKeys-12     	18385731	        61.03 ns/op	       0 B/op	       0 allocs/op
BenchmarkMapIntKeys-12        	38224066	        27.43 ns/op	       0 B/op	       0 allocs/op
BenchmarkMapStringIndex-12    	19761651	        62.21 ns/op	       0 B/op	       0 allocs/op
BenchmarkMapIntIndex-12       	35550814	        29.30 ns/op	       0 B/op	       0 allocs/op
PASS
ok  	github.com/SimonWaldherr/golang-benchmarks/index	5.625s

json

package json

import (
	"encoding/json"
	"math"
	"math/big"
	"testing"
	"time"
)

type Data struct {
	String   string
	Time     time.Time
	Int      int
	Int8     int8
	Int16    int16
	Int32    int32
	Int64    int64
	Boolean  bool
	Float32  float32
	Float64  float64
	BigInt   big.Int
	BigFloat big.Float
}

func BenchmarkJsonMarshal(b *testing.B) {
	for n := 0; n < b.N; n++ {
		var d = Data{
			String:   "",
			Time:     time.Now(),
			Int:      math.MaxInt32,
			Int8:     math.MaxInt8,
			Int16:    math.MaxInt16,
			Int32:    math.MaxInt32,
			Int64:    math.MaxInt64,
			Boolean:  false,
			Float32:  math.MaxFloat32,
			Float64:  math.MaxFloat64,
			BigInt:   *big.NewInt(math.MaxInt64),
			BigFloat: *big.NewFloat(math.MaxFloat64),
		}

		_, err := json.Marshal(d)
		if err != nil {
			b.Error(err)
			b.Fail()
			return
		}
	}
}

func BenchmarkJsonUnmarshal(b *testing.B) {
	str := `
{
  "String": "",
  "Time": "2019-10-30T16:41:29.853426+07:00",
  "Int": 2147483647,
  "Int8": 127,
  "Int16": 32767,
  "Int32": 2147483647,
  "Int64": 9223372036854775807,
  "Boolean": false,
  "Float32": 3.4028235e+38,
  "Float64": 1.7976931348623157e+308,
  "BigInt": 9999999999999999999,
  "BigFloat": "2.7976931348623157e+308"
}
`

	for n := 0; n < b.N; n++ {
		var d Data
		err := json.Unmarshal([]byte(str), &d)
		if err != nil {
			b.Error(err)
			b.Fail()
			return
		}
	}
}
$ go test -bench . -benchmem
goos: darwin
goarch: arm64
pkg: github.com/SimonWaldherr/golang-benchmarks/json
cpu: Apple M2 Max
BenchmarkJsonMarshal-12      	 1693124	       713.6 ns/op	     480 B/op	       5 allocs/op
BenchmarkJsonUnmarshal-12    	  317344	      3811 ns/op	    1816 B/op	      27 allocs/op
PASS
ok  	github.com/SimonWaldherr/golang-benchmarks/json	3.381s

math

// Package math compares the speed of various mathematical operations.
package math

import (
	"sync"
	"sync/atomic"
	"testing"
)

func BenchmarkMathInt8(b *testing.B) {
	var intVal int8
	for n := 0; n < b.N; n++ {
		intVal = intVal + 2
	}
}

func BenchmarkMathInt32(b *testing.B) {
	var intVal int32
	for n := 0; n < b.N; n++ {
		intVal = intVal + 2
	}
}

func BenchmarkMathInt64(b *testing.B) {
	var intVal int64
	for n := 0; n < b.N; n++ {
		intVal = intVal + 2
	}
}

func BenchmarkMathAtomicInt32(b *testing.B) {
	var intVal int32
	for n := 0; n < b.N; n++ {
		atomic.AddInt32(&intVal, 2)
	}
}

func BenchmarkMathAtomicInt64(b *testing.B) {
	var intVal int64
	for n := 0; n < b.N; n++ {
		atomic.AddInt64(&intVal, 2)
	}
}

type IntMutex struct {
	v   int64
	mux sync.Mutex
}

func BenchmarkMathMutexInt(b *testing.B) {
	var m IntMutex
	for n := 0; n < b.N; n++ {
		m.mux.Lock()
		m.v = m.v + 2
		m.mux.Unlock()
	}
}

func BenchmarkMathFloat32(b *testing.B) {
	var floatVal float32
	for n := 0; n < b.N; n++ {
		floatVal = floatVal + 2
	}
}

func BenchmarkMathFloat64(b *testing.B) {
	var floatVal float64
	for n := 0; n < b.N; n++ {
		floatVal = floatVal + 2
	}
}
$ go test -bench . -benchmem
goos: darwin
goarch: arm64
pkg: github.com/SimonWaldherr/golang-benchmarks/math
cpu: Apple M2 Max
BenchmarkMathInt8-12           	1000000000	         0.3090 ns/op	       0 B/op	       0 allocs/op
BenchmarkMathInt32-12          	1000000000	         0.3063 ns/op	       0 B/op	       0 allocs/op
BenchmarkMathInt64-12          	1000000000	         0.3066 ns/op	       0 B/op	       0 allocs/op
BenchmarkMathAtomicInt32-12    	288225333	         4.171 ns/op	       0 B/op	       0 allocs/op
BenchmarkMathAtomicInt64-12    	287992080	         4.177 ns/op	       0 B/op	       0 allocs/op
BenchmarkMathMutexInt-12       	142091926	         8.266 ns/op	       0 B/op	       0 allocs/op
BenchmarkMathFloat32-12        	1000000000	         0.3058 ns/op	       0 B/op	       0 allocs/op
BenchmarkMathFloat64-12        	1000000000	         0.3078 ns/op	       0 B/op	       0 allocs/op
PASS
ok  	github.com/SimonWaldherr/golang-benchmarks/math	7.170s

parse

// Package parse benchmarks parsing.
package parse

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("1337", 10, 64)
		if err != nil {
			panic(err)
		}
	}
}

func BenchmarkParseFloat(b *testing.B) {
	for n := 0; n < b.N; n++ {
		_, err := strconv.ParseFloat("3.141592653589793238462643383", 64)
		if err != nil {
			panic(err)
		}
	}
}
$ go test -bench . -benchmem
goos: darwin
goarch: arm64
pkg: github.com/SimonWaldherr/golang-benchmarks/parse
cpu: Apple M2 Max
BenchmarkParseBool-12     	519294775	         2.368 ns/op	       0 B/op	       0 allocs/op
BenchmarkParseInt-12      	100000000	        10.74 ns/op	       0 B/op	       0 allocs/op
BenchmarkParseFloat-12    	18974682	        63.64 ns/op	       0 B/op	       0 allocs/op
PASS
ok  	github.com/SimonWaldherr/golang-benchmarks/parse	4.144s

random

// Package random compares math/rand with crypto/rand.
// math/rand is much faster than crypto/rand, but it
// returns only a pseudo random number.
package random

import (
	crand "crypto/rand"
	"encoding/base64"
	"io"
	"math/big"
	mrand "math/rand"
	"testing"
)

var (
	randomIntResult    int64
	randomBigIntResult *big.Int
	randomBytesResult  []byte
	randomStringResult string
)

type mathRandReader struct{}

func (mathRandReader) Read(p []byte) (int, error) {
	return mrand.Read(p)
}

func BenchmarkMathRand(b *testing.B) {
	b.ReportAllocs()
	for n := 0; n < b.N; n++ {
		randomIntResult = mrand.Int63n(0xFFFF)
	}
}

func BenchmarkCryptoRand(b *testing.B) {
	limit := big.NewInt(0xFFFF)
	b.ReportAllocs()

	for n := 0; n < b.N; n++ {
		r, err := crand.Int(crand.Reader, limit)
		if err != nil {
			panic(err)
		}
		randomBigIntResult = r
	}
}

func BenchmarkMathRandBytes(b *testing.B) {
	reader := mathRandReader{}
	b.ReportAllocs()

	for n := 0; n < b.N; n++ {
		r, err := GenerateRandomBytes(reader, 32)
		if err != nil {
			panic(err)
		}
		randomBytesResult = r
	}
}

func BenchmarkCryptoRandBytes(b *testing.B) {
	b.ReportAllocs()
	for n := 0; n < b.N; n++ {
		r, err := GenerateRandomBytes(crand.Reader, 32)
		if err != nil {
			panic(err)
		}
		randomBytesResult = r
	}
}

func BenchmarkMathRandString(b *testing.B) {
	reader := mathRandReader{}
	b.ReportAllocs()

	for n := 0; n < b.N; n++ {
		r, err := GenerateRandomString(reader, 32)
		if err != nil {
			panic(err)
		}
		randomStringResult = r
	}
}

func BenchmarkCryptoRandString(b *testing.B) {
	b.ReportAllocs()
	for n := 0; n < b.N; n++ {
		r, err := GenerateRandomString(crand.Reader, 32)
		if err != nil {
			panic(err)
		}
		randomStringResult = r
	}
}

func GenerateRandomBytes(r io.Reader, n int) ([]byte, error) {
	data := make([]byte, n)
	_, err := io.ReadFull(r, data)
	if err != nil {
		return nil, err
	}

	return data, nil
}

func GenerateRandomString(r io.Reader, s int) (string, error) {
	b, err := GenerateRandomBytes(r, s)
	return base64.URLEncoding.EncodeToString(b), err
}
$ go test -bench . -benchmem
goos: darwin
goarch: arm64
pkg: github.com/SimonWaldherr/golang-benchmarks/random
cpu: Apple M2 Max
BenchmarkMathRand-12            	166565755	         7.338 ns/op	       0 B/op	       0 allocs/op
BenchmarkCryptoRand-12          	 9473515	       130.3 ns/op	      48 B/op	       3 allocs/op
BenchmarkMathRandBytes-12       	18462484	        66.25 ns/op	      32 B/op	       1 allocs/op
BenchmarkCryptoRandBytes-12     	 4565014	       274.2 ns/op	      32 B/op	       1 allocs/op
BenchmarkMathRandString-12      	10124570	       126.0 ns/op	     128 B/op	       3 allocs/op
BenchmarkCryptoRandString-12    	 3624658	       313.9 ns/op	     128 B/op	       3 allocs/op
PASS
ok  	github.com/SimonWaldherr/golang-benchmarks/random	9.025s

regexp

// Package regexp benchmarks the performance of a pre-compiled regexp match
// a non-pre-compiled match and JIT-cached-compilation via golibs: https://simonwaldherr.de/go/golibs
package regexp

import (
	"regexp"
	"testing"

	"simonwaldherr.de/go/golibs/regex"
)

var regexpStr string = `^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,9}$`

func BenchmarkMatchString(b *testing.B) {
	for n := 0; n < b.N; n++ {
		_, err := regexp.MatchString(regexpStr, "john.doe@example.tld")
		if err != nil {
			panic(err)
		}
	}
}

func BenchmarkMatchStringCompiled(b *testing.B) {
	r, err := regexp.Compile(regexpStr)
	if err != nil {
		panic(err)
	}

	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		r.MatchString("john.doe@example.tld")
	}
}

func BenchmarkMatchStringGolibs(b *testing.B) {
	for n := 0; n < b.N; n++ {
		_, err := regex.MatchString("john.doe@example.tld", regexpStr)
		if err != nil {
			panic(err)
		}
	}
}
$ go test -bench . -benchmem
goos: darwin
goarch: arm64
pkg: github.com/SimonWaldherr/golang-benchmarks/regexp
cpu: Apple M2 Max
BenchmarkMatchString-12            	  194449	      5309 ns/op	   10223 B/op	      86 allocs/op
BenchmarkMatchStringCompiled-12    	 3743511	       343.5 ns/op	       0 B/op	       0 allocs/op
BenchmarkMatchStringGolibs-12      	 3132643	       336.4 ns/op	       0 B/op	       0 allocs/op
PASS
ok  	github.com/SimonWaldherr/golang-benchmarks/regexp	4.443s

sql

package main

import (
	"database/sql"
	"fmt"
	"strings"
	"testing"

	_ "modernc.org/sqlite"
)

var sqlResultSink int64

func openSQLite(dsn string) (*sql.DB, error) {
	return sql.Open("sqlite", dsn)
}

// prepareSchema creates the users and orders tables used by benchmarks.
func prepareSchema(db *sql.DB) error {
	if _, err := db.Exec(`CREATE TABLE users (id INT PRIMARY KEY, name TEXT, email TEXT, active BOOL)`); err != nil {
		return err
	}
	if _, err := db.Exec(`CREATE TABLE orders (id INT PRIMARY KEY, user_id INT, amount FLOAT, status TEXT, meta JSON)`); err != nil {
		return err
	}
	return nil
}

func benchmarkDB(b *testing.B, open func(string) (*sql.DB, error), dsn string) *sql.DB {
	db, err := open(dsn)
	if err != nil {
		b.Fatalf("open: %v", err)
	}

	if err := prepareSchema(db); err != nil {
		db.Close()
		b.Fatalf("schema: %v", err)
	}
	b.Cleanup(func() {
		if err := db.Close(); err != nil {
			b.Fatalf("close: %v", err)
		}
	})

	return db
}

func prepareBenchmarkStmt(b *testing.B, tx interface {
	Prepare(string) (*sql.Stmt, error)
}, query string) *sql.Stmt {
	stmt, err := tx.Prepare(query)
	if err != nil {
		b.Fatalf("prepare: %v", err)
	}
	b.Cleanup(func() {
		if err := stmt.Close(); err != nil {
			b.Fatalf("close stmt: %v", err)
		}
	})

	return stmt
}

func populateUsersAndOrders(b *testing.B, db *sql.DB, users, ordersPerUser int) {
	tx, err := db.Begin()
	if err != nil {
		b.Fatalf("begin populate: %v", err)
	}

	ustmt, err := tx.Prepare(`INSERT INTO users (id, name, email, active) VALUES (?, ?, ?, ?)`)
	if err != nil {
		tx.Rollback()
		b.Fatalf("prepare users: %v", err)
	}
	ostmt, err := tx.Prepare(`INSERT INTO orders (id, user_id, amount, status, meta) VALUES (?, ?, ?, ?, ?)`)
	if err != nil {
		ustmt.Close()
		tx.Rollback()
		b.Fatalf("prepare orders: %v", err)
	}

	for i := 1; i <= users; i++ {
		if _, err := ustmt.Exec(i, fmt.Sprintf("user%d", i), fmt.Sprintf("u%d@example.com", i), i%3 != 0); err != nil {
			ustmt.Close()
			ostmt.Close()
			tx.Rollback()
			b.Fatalf("populate user: %v", err)
		}
		for j := 0; j < ordersPerUser; j++ {
			id := i*100 + j
			status := "PAID"
			if j%4 == 0 {
				status = "PENDING"
			}
			if _, err := ostmt.Exec(id, i, float64(id)*1.5, status, `{"device":"web"}`); err != nil {
				ustmt.Close()
				ostmt.Close()
				tx.Rollback()
				b.Fatalf("populate order: %v", err)
			}
		}
	}

	if err := ustmt.Close(); err != nil {
		ostmt.Close()
		tx.Rollback()
		b.Fatalf("close users stmt: %v", err)
	}
	if err := ostmt.Close(); err != nil {
		tx.Rollback()
		b.Fatalf("close orders stmt: %v", err)
	}
	if err := tx.Commit(); err != nil {
		b.Fatalf("commit populate: %v", err)
	}
}

func benchInsertTxPerRow(b *testing.B, open func(string) (*sql.DB, error), dsn string) {
	db := benchmarkDB(b, open, dsn)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		tx, err := db.Begin()
		if err != nil {
			b.Fatalf("begin: %v", err)
		}
		stmt, err := tx.Prepare(`INSERT INTO users (id, name, email, active) VALUES (?, ?, ?, ?)`)
		if err != nil {
			b.Fatalf("prepare: %v", err)
		}
		if _, err := stmt.Exec(i, fmt.Sprintf("user%d", i), fmt.Sprintf("u%d@example.com", i), i%2 == 0); err != nil {
			b.Fatalf("exec: %v", err)
		}
		stmt.Close()
		if err := tx.Commit(); err != nil {
			b.Fatalf("commit: %v", err)
		}
	}
}

func benchInsertPrepared(b *testing.B, open func(string) (*sql.DB, error), dsn string) {
	db := benchmarkDB(b, open, dsn)
	tx, err := db.Begin()
	if err != nil {
		b.Fatalf("begin: %v", err)
	}
	stmt, err := tx.Prepare(`INSERT INTO users (id, name, email, active) VALUES (?, ?, ?, ?)`)
	if err != nil {
		tx.Rollback()
		b.Fatalf("prepare: %v", err)
	}

	const valuePoolSize = 1024
	names := make([]string, valuePoolSize)
	emails := make([]string, valuePoolSize)
	for i := range names {
		names[i] = fmt.Sprintf("user%d", i)
		emails[i] = fmt.Sprintf("u%d@example.com", i)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		valueIndex := i % valuePoolSize
		if _, err := stmt.Exec(i, names[valueIndex], emails[valueIndex], i%2 == 0); err != nil {
			b.Fatalf("exec: %v", err)
		}
	}
	b.StopTimer()

	if err := stmt.Close(); err != nil {
		tx.Rollback()
		b.Fatalf("close stmt: %v", err)
	}
	if err := tx.Commit(); err != nil {
		b.Fatalf("commit: %v", err)
	}
}

func benchSelectPoint(b *testing.B, open func(string) (*sql.DB, error), dsn string, users int) {
	db := benchmarkDB(b, open, dsn)
	populateUsersAndOrders(b, db, users, 2)
	stmt := prepareBenchmarkStmt(b, db, `SELECT email, active FROM users WHERE id = ?`)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		var email string
		var active bool
		id := i%users + 1
		if err := stmt.QueryRow(id).Scan(&email, &active); err != nil {
			b.Fatalf("query row: %v", err)
		}
		if active {
			sqlResultSink += int64(len(email))
		}
	}
}

func benchSelectRange(b *testing.B, open func(string) (*sql.DB, error), dsn string, users int) {
	db := benchmarkDB(b, open, dsn)
	populateUsersAndOrders(b, db, users, 2)
	stmt := prepareBenchmarkStmt(b, db, `SELECT id, name FROM users WHERE id BETWEEN ? AND ? ORDER BY id`)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		start := i%users + 1
		end := start + 24
		if end > users {
			end = users
		}
		rows, err := stmt.Query(start, end)
		if err != nil {
			b.Fatalf("query: %v", err)
		}

		var count int64
		for rows.Next() {
			var id int
			var name string
			if err := rows.Scan(&id, &name); err != nil {
				rows.Close()
				b.Fatalf("scan: %v", err)
			}
			count += int64(id + len(name))
		}
		if err := rows.Err(); err != nil {
			rows.Close()
			b.Fatalf("rows: %v", err)
		}
		if err := rows.Close(); err != nil {
			b.Fatalf("close rows: %v", err)
		}
		sqlResultSink += count
	}
}

func benchUpdatePrepared(b *testing.B, open func(string) (*sql.DB, error), dsn string, users int) {
	db := benchmarkDB(b, open, dsn)
	populateUsersAndOrders(b, db, users, 1)
	stmt := prepareBenchmarkStmt(b, db, `UPDATE users SET active = ? WHERE id = ?`)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		id := i%users + 1
		result, err := stmt.Exec(i%2 == 0, id)
		if err != nil {
			b.Fatalf("exec update: %v", err)
		}
		affected, err := result.RowsAffected()
		if err == nil {
			sqlResultSink += affected
		}
	}
}

func benchDeletePrepared(b *testing.B, open func(string) (*sql.DB, error), dsn string, users int) {
	db := benchmarkDB(b, open, dsn)
	populateUsersAndOrders(b, db, users, 3)
	stmt := prepareBenchmarkStmt(b, db, `DELETE FROM orders WHERE id = ?`)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		userID := i%users + 1
		orderID := userID*100 + i%3
		result, err := stmt.Exec(orderID)
		if err != nil {
			b.Fatalf("exec delete: %v", err)
		}
		affected, err := result.RowsAffected()
		if err == nil {
			sqlResultSink += affected
		}
	}
}

func benchAggregateByStatus(b *testing.B, open func(string) (*sql.DB, error), dsn string, users int) {
	db := benchmarkDB(b, open, dsn)
	populateUsersAndOrders(b, db, users, 6)
	stmt := prepareBenchmarkStmt(b, db, `
		SELECT status, COUNT(id), SUM(amount)
		FROM orders
		GROUP BY status
		ORDER BY status
	`)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		rows, err := stmt.Query()
		if err != nil {
			b.Fatalf("query aggregate: %v", err)
		}

		var total int64
		for rows.Next() {
			var status string
			var count int
			var amount sql.NullFloat64
			if err := rows.Scan(&status, &count, &amount); err != nil {
				rows.Close()
				b.Fatalf("scan aggregate: %v", err)
			}
			total += int64(len(status) + count)
			if amount.Valid {
				total += int64(amount.Float64)
			}
		}
		if err := rows.Err(); err != nil {
			rows.Close()
			b.Fatalf("rows aggregate: %v", err)
		}
		if err := rows.Close(); err != nil {
			b.Fatalf("close rows aggregate: %v", err)
		}
		sqlResultSink += total
	}
}

func benchSelectOrderedOrders(b *testing.B, open func(string) (*sql.DB, error), dsn string, users int) {
	db := benchmarkDB(b, open, dsn)
	populateUsersAndOrders(b, db, users, 5)
	stmt := prepareBenchmarkStmt(b, db, `
		SELECT id, user_id, amount, status
		FROM orders
		WHERE status = ?
		ORDER BY amount
	`)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		rows, err := stmt.Query("PAID")
		if err != nil {
			b.Fatalf("query ordered orders: %v", err)
		}

		var total int64
		for rows.Next() {
			var id int
			var userID int
			var amount float64
			var status string
			if err := rows.Scan(&id, &userID, &amount, &status); err != nil {
				rows.Close()
				b.Fatalf("scan ordered orders: %v", err)
			}
			total += int64(id + userID + len(status))
			total += int64(amount)
		}
		if err := rows.Err(); err != nil {
			rows.Close()
			b.Fatalf("rows ordered orders: %v", err)
		}
		if err := rows.Close(); err != nil {
			b.Fatalf("close rows ordered orders: %v", err)
		}
		sqlResultSink += total
	}
}

func benchSelectJoin(b *testing.B, open func(string) (*sql.DB, error), dsn string, rows int) {
	db := benchmarkDB(b, open, dsn)
	populateUsersAndOrders(b, db, rows, 2)

	query := strings.TrimSpace(`
		SELECT u.name AS user, SUM(o.amount) AS total, COUNT(o.id) AS cnt
		FROM users u
		LEFT JOIN orders o ON u.id = o.user_id AND o.status = 'PAID'
		GROUP BY u.name
		ORDER BY u.name
	`)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		rows, err := db.Query(query)
		if err != nil {
			b.Fatalf("query: %v", err)
		}

		var totalCount int64
		for rows.Next() {
			var name string
			var total sql.NullFloat64
			var cnt int
			if err := rows.Scan(&name, &total, &cnt); err != nil {
				rows.Close()
				b.Fatalf("scan: %v", err)
			}
			totalCount += int64(len(name) + cnt)
			if total.Valid {
				totalCount += int64(total.Float64)
			}
		}
		if err := rows.Err(); err != nil {
			rows.Close()
			b.Fatalf("rows: %v", err)
		}
		if err := rows.Close(); err != nil {
			b.Fatalf("close rows: %v", err)
		}
		sqlResultSink += totalCount
	}
}

func benchTransactionBatch(b *testing.B, open func(string) (*sql.DB, error), dsn string, batchSize int) {
	db := benchmarkDB(b, open, dsn)

	names := make([]string, batchSize)
	emails := make([]string, batchSize)
	for i := 0; i < batchSize; i++ {
		names[i] = fmt.Sprintf("user%d", i)
		emails[i] = fmt.Sprintf("u%d@example.com", i)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		tx, err := db.Begin()
		if err != nil {
			b.Fatalf("begin: %v", err)
		}
		stmt, err := tx.Prepare(`INSERT INTO users (id, name, email, active) VALUES (?, ?, ?, ?)`)
		if err != nil {
			tx.Rollback()
			b.Fatalf("prepare: %v", err)
		}
		baseID := i * batchSize
		for j := 0; j < batchSize; j++ {
			if _, err := stmt.Exec(baseID+j, names[j], emails[j], j%2 == 0); err != nil {
				stmt.Close()
				tx.Rollback()
				b.Fatalf("exec: %v", err)
			}
		}
		if err := stmt.Close(); err != nil {
			tx.Rollback()
			b.Fatalf("close stmt: %v", err)
		}
		if err := tx.Commit(); err != nil {
			b.Fatalf("commit: %v", err)
		}
	}
}

func runSQLBenchmarks(b *testing.B, open func(string) (*sql.DB, error), dsn func(string) string) {
	b.Run("InsertTxPerRow", func(b *testing.B) {
		benchInsertTxPerRow(b, open, dsn("insert_tx"))
	})
	b.Run("InsertPrepared", func(b *testing.B) {
		benchInsertPrepared(b, open, dsn("insert_prepared"))
	})
	b.Run("TransactionBatch100", func(b *testing.B) {
		benchTransactionBatch(b, open, dsn("batch_100"), 100)
	})
	b.Run("SelectPoint", func(b *testing.B) {
		benchSelectPoint(b, open, dsn("select_point"), 1000)
	})
	b.Run("SelectRange25", func(b *testing.B) {
		benchSelectRange(b, open, dsn("select_range"), 1000)
	})
	b.Run("UpdatePrepared", func(b *testing.B) {
		benchUpdatePrepared(b, open, dsn("update_prepared"), 1000)
	})
	b.Run("DeletePrepared", func(b *testing.B) {
		benchDeletePrepared(b, open, dsn("delete_prepared"), 1000)
	})
	b.Run("AggregateByStatus", func(b *testing.B) {
		benchAggregateByStatus(b, open, dsn("aggregate_status"), 1000)
	})
	b.Run("SelectOrderedOrders", func(b *testing.B) {
		benchSelectOrderedOrders(b, open, dsn("select_ordered_orders"), 500)
	})
	b.Run("SelectJoin", func(b *testing.B) {
		benchSelectJoin(b, open, dsn("select_join"), 200)
	})
}

func sqliteDSN(name string) string {
	return "file:" + name + "?mode=memory&cache=shared"
}

func Benchmark_Insert_SQLite(b *testing.B) {
	benchInsertTxPerRow(b, openSQLite, sqliteDSN("insert_legacy"))
}

func Benchmark_SelectJoin_SQLite(b *testing.B) {
	benchSelectJoin(b, openSQLite, sqliteDSN("select_join_legacy"), 200)
}
package main

import (
	"database/sql"
	"testing"

	tsqldriver "github.com/SimonWaldherr/tinySQL/driver"
)

func Benchmark_Insert_TinySQL(b *testing.B) {
	benchInsertTxPerRow(b, openTinySQL, "mem://?tenant=bench_insert")
}

func Benchmark_SelectJoin_TinySQL(b *testing.B) {
	benchSelectJoin(b, openTinySQL, "mem://?tenant=bench_select", 200)
}

func Benchmark_SQLCompare(b *testing.B) {
	runComparedSQLBenchmark(b, "InsertTxPerRow", benchInsertTxPerRow)
	runComparedSQLBenchmark(b, "InsertPrepared", benchInsertPrepared)
	runComparedSQLBenchmark(b, "TransactionBatch100", func(b *testing.B, open func(string) (*sql.DB, error), dsn string) {
		benchTransactionBatch(b, open, dsn, 100)
	})
	runComparedSQLBenchmark(b, "SelectPoint", func(b *testing.B, open func(string) (*sql.DB, error), dsn string) {
		benchSelectPoint(b, open, dsn, 1000)
	})
	runComparedSQLBenchmark(b, "SelectRange25", func(b *testing.B, open func(string) (*sql.DB, error), dsn string) {
		benchSelectRange(b, open, dsn, 1000)
	})
	runComparedSQLBenchmark(b, "UpdatePrepared", func(b *testing.B, open func(string) (*sql.DB, error), dsn string) {
		benchUpdatePrepared(b, open, dsn, 1000)
	})
	runComparedSQLBenchmark(b, "DeletePrepared", func(b *testing.B, open func(string) (*sql.DB, error), dsn string) {
		benchDeletePrepared(b, open, dsn, 1000)
	})
	runComparedSQLBenchmark(b, "AggregateByStatus", func(b *testing.B, open func(string) (*sql.DB, error), dsn string) {
		benchAggregateByStatus(b, open, dsn, 1000)
	})
	runComparedSQLBenchmark(b, "SelectOrderedOrders", func(b *testing.B, open func(string) (*sql.DB, error), dsn string) {
		benchSelectOrderedOrders(b, open, dsn, 500)
	})
	runComparedSQLBenchmark(b, "SelectJoin", func(b *testing.B, open func(string) (*sql.DB, error), dsn string) {
		benchSelectJoin(b, open, dsn, 200)
	})
}

func runComparedSQLBenchmark(
	b *testing.B,
	name string,
	bench func(*testing.B, func(string) (*sql.DB, error), string),
) {
	b.Run(name, func(b *testing.B) {
		b.Run("SQLite", func(b *testing.B) {
			bench(b, openSQLite, sqliteDSN("compare_"+name))
		})
		b.Run("TinySQL", func(b *testing.B) {
			bench(b, openTinySQL, "mem://?tenant=compare_"+name)
		})
	})
}

func openTinySQL(dsn string) (*sql.DB, error) {
	return sql.Open(tsqldriver.DriverName, dsn)
}
$ go test -bench . -benchmem
goos: darwin
goarch: arm64
pkg: github.com/SimonWaldherr/golang-benchmarks/sql
cpu: Apple M2 Max
Benchmark_Insert_SQLite-12         	  133728	      8550 ns/op	     993 B/op	      26 allocs/op
Benchmark_SelectJoin_SQLite-12     	    2419	    467467 ns/op	   14928 B/op	    1215 allocs/op
Benchmark_Insert_TinySQL-12        	   10000	    583271 ns/op	 1114113 B/op	    9961 allocs/op
Benchmark_SelectJoin_TinySQL-12    	      20	  51604038 ns/op	99678249 B/op	  324837 allocs/op
Benchmark_SQLCompare/InsertTxPerRow/SQLite-12  	  148186	      8098 ns/op	     991 B/op	      26 allocs/op
Benchmark_SQLCompare/InsertTxPerRow/TinySQL-12 	   10000	    750274 ns/op	 1114655 B/op	    9964 allocs/op
Benchmark_SQLCompare/InsertPrepared/SQLite-12  	  572886	      1957 ns/op	     304 B/op	      10 allocs/op
Benchmark_SQLCompare/InsertPrepared/TinySQL-12 	  179713	      6088 ns/op	    3270 B/op	      72 allocs/op
Benchmark_SQLCompare/TransactionBatch100/SQLite-12         	    5917	    223977 ns/op	   31031 B/op	    1111 allocs/op
Benchmark_SQLCompare/TransactionBatch100/TinySQL-12        	     998	   6661030 ns/op	10945004 B/op	  106927 allocs/op
Benchmark_SQLCompare/SelectPoint/SQLite-12                 	  479940	      2350 ns/op	     635 B/op	      23 allocs/op
Benchmark_SQLCompare/SelectPoint/TinySQL-12                	  145869	      8043 ns/op	    1736 B/op	      24 allocs/op
Benchmark_SQLCompare/SelectRange25/SQLite-12               	   89516	     14198 ns/op	    2113 B/op	     159 allocs/op
Benchmark_SQLCompare/SelectRange25/TinySQL-12              	   46370	     31042 ns/op	   15746 B/op	     191 allocs/op
Benchmark_SQLCompare/UpdatePrepared/SQLite-12              	  677367	      1627 ns/op	     162 B/op	       6 allocs/op
Benchmark_SQLCompare/UpdatePrepared/TinySQL-12             	    5752	    198618 ns/op	   94479 B/op	      67 allocs/op
Benchmark_SQLCompare/DeletePrepared/SQLite-12              	 1406258	       838.8 ns/op	     135 B/op	       6 allocs/op
Benchmark_SQLCompare/DeletePrepared/TinySQL-12             	    5248	    198708 ns/op	  144781 B/op	      64 allocs/op
Benchmark_SQLCompare/AggregateByStatus/SQLite-12           	     414	   2732814 ns/op	     744 B/op	      32 allocs/op
Benchmark_SQLCompare/AggregateByStatus/TinySQL-12          	     357	   3403372 ns/op	 4540057 B/op	   32099 allocs/op
Benchmark_SQLCompare/SelectOrderedOrders/SQLite-12         	     842	   1445735 ns/op	  138520 B/op	   13954 allocs/op
Benchmark_SQLCompare/SelectOrderedOrders/TinySQL-12        	    1353	    858144 ns/op	  881377 B/op	   15458 allocs/op
Benchmark_SQLCompare/SelectJoin/SQLite-12                  	    2631	    438089 ns/op	   14928 B/op	    1215 allocs/op
Benchmark_SQLCompare/SelectJoin/TinySQL-12                 	      15	  77559561 ns/op	99678297 B/op	  324839 allocs/op
PASS
ok  	github.com/SimonWaldherr/golang-benchmarks/sql	583.792s

template

// Package template benchmarks the performance of different templating methods
package template

import (
	"bytes"
	htmltemplate "html/template"
	"regexp"
	"testing"
	texttemplate "text/template"
)

// Define a struct to hold the data for the templates
type Data struct {
	Name    string
	Address string
}

// Prepare the templates and data
var (
	data = Data{Name: "John Doe", Address: "123 Elm St"}

	textTplString = "Name: {{.Name}}, Address: {{.Address}}"
	htmlTplString = "<div>Name: {{.Name}}</div><div>Address: {{.Address}}</div>"
	regExpString  = "Name: {{NAME}}, Address: {{ADDRESS}}"
)

// Benchmark for text/template
func BenchmarkTextTemplate(b *testing.B) {
	tpl, _ := texttemplate.New("text").Parse(textTplString)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		var buf bytes.Buffer
		tpl.Execute(&buf, data)
	}
}

// Benchmark for html/template
func BenchmarkHTMLTemplate(b *testing.B) {
	tpl, _ := htmltemplate.New("html").Parse(htmlTplString)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		var buf bytes.Buffer
		tpl.Execute(&buf, data)
	}
}

// Benchmark for replacing placeholders using regexp
func BenchmarkRegExp(b *testing.B) {
	rName := regexp.MustCompile(`{{NAME}}`)
	rAddress := regexp.MustCompile(`{{ADDRESS}}`)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		result := rName.ReplaceAllString(regExpString, data.Name)
		result = rAddress.ReplaceAllString(result, data.Address)
	}
}
$ go test -bench . -benchmem
goos: darwin
goarch: arm64
pkg: github.com/SimonWaldherr/golang-benchmarks/template
cpu: Apple M2 Max
BenchmarkTextTemplate-12    	 3448192	       318.0 ns/op	     272 B/op	       5 allocs/op
BenchmarkHTMLTemplate-12    	 1236817	       979.0 ns/op	     496 B/op	      15 allocs/op
BenchmarkRegExp-12          	 3219236	       373.6 ns/op	     297 B/op	       9 allocs/op
PASS
ok  	github.com/SimonWaldherr/golang-benchmarks/template	5.579s
S
Description
No description provided
Readme MIT 328 KiB
Languages
Go 90.1%
Shell 5.2%
Batchfile 3.3%
Makefile 1.4%