This is just a bunch of code experiments in Go, some of which are benchmarked and documented here for educational and self-awareness purposes.
Marshalling a simple struct into JSON using two strategies:
MarshalDirect
: the usual approach of marshalling into a byte slice and then printing that.MarshalWriter
: avoiding the temporary buffer by writing directly to aWriter
.
Files:
Metrics:
goos: linux
goarch: amd64
pkg: github.com/mkock/efficient-go
BenchmarkMarshalDirect-8 300000 4320 ns/op 480 B/op 7 allocs/op
BenchmarkMarshalWriter-8 1000000 1961 ns/op 304 B/op 5 allocs/op
PASS
coverage: 80.0% of statements
ok github.com/mkock/efficient-go 3.331s
Success: Benchmarks passed.
Building a string from a series of random text snippets using two strategies:
ConcatString
: the usual approach of constructing a new string from the concatenation of two others.ConcatStringBuilder
: avoiding the overhead of allocation by leveraging thestrings.Builder
from the stdlib.
Files:
Metrics:
goos: linux
goarch: amd64
pkg: github.com/mkock/efficient-go
BenchmarkConcatString-8 1 23900002999 ns/op 208581365520 B/op 54598 allocs/op
BenchmarkConcatStringBuilder-8 300 5072790 ns/op 45183032 B/op 38 allocs/op
PASS
ok github.com/mkock/efficient-go 25.918s
Success: Benchmarks passed.
There's a really interesting blog post that shows how decorators/middleware could work in a way that respects the single responsibility and open/closed principles. Link: bartfokker.com.
This experiment is mostly a reproduction of the author's examples for my own reference.
Main concepts:
- There must be two interfaces: one for working with the implementation, and one for working with middlewares.
- Middlewares can take custom arguments, but are required to return (a modified version of) a type that satisfies the interface for the implemented solution.
- Middlewares return a function that redefines the implemented solution in order to add additional functionality to it.
Files:
Iterating over a struct and using it for managing two random numbers to generate a result, using two strategies:
unpooledRandomizer
: allocates a struct per iteration.pooledRandomizer
: uses sync.Pool to reduce allocations be recycling them.
Files:
Metrics:
goos: windows
goarch: amd64
pkg: github.com/mkock/efficientgo
BenchmarkUnpooledRandomizer-8 36 36206583 ns/op 2007138 B/op 1 allocs/op
--- BENCH: BenchmarkUnpooledRandomizer-8
pool_test.go:13: 500000
pool_test.go:13: 500000
pool_test.go:13: 500000
BenchmarkPooledRandomizer-8 27 46013848 ns/op 2007675 B/op 3 allocs/op
--- BENCH: BenchmarkPooledRandomizer-8
pool_test.go:22: 500000
pool_test.go:22: 500000
PASS
ok github.com/mkock/efficientgo 3.961s
Three different strategies for printing to stdout of which two escape variables to the heap, and one does not:
printAsValue
simply prints the struct, which escapes to the heap.printAsIndividualFields
prints each field of the struct without reflection, but still escapes to heap.printByteSlice
creates a string manually before printing, which does not escape to the heap.
Files:
Metrics:
>go build -gcflags="-m -l" heap.go
# command-line-arguments
.\heap.go:18:12: printAsValue ... argument does not escape
.\heap.go:18:13: p escapes to heap
.\heap.go:24:12: printAsIndividualFields ... argument does not escape
.\heap.go:24:30: p.name escapes to heap
.\heap.go:24:38: p.profession escapes to heap
.\heap.go:24:52: p.year escapes to heap
.\heap.go:30:22: os.Stdout escapes to heap
.\heap.go:31:65: printByteSlice p.name + ", " + p.profession + ", " + strconv.Itoa(int(p.year)) does not escape
<autogenerated>:1: (*File).close .this does not escape
<autogenerated>:1: (*File).isdir .this does not escape
Performance benchmarks for extracting an ID from a slice of structs:
BenchmarkBubbleSort
creates the sorted slice by bubbling each value through the slice until it finds the correct location.BenchmarkBuiltinSort
starts with a loop over the slice to extract the ID's, and then calls the builtinsort.Slice
.
BenchmarkSorts100/BenchmarkBubbleSort-8 383965 2667 ns/op
BenchmarkSorts100/BenchmarkBuiltinSort-8 393109 2925 ns/op
BenchmarkSorts1000/BenchmarkBubbleSort-8 4995 212136 ns/op
BenchmarkSorts1000/BenchmarkBuiltinSort-8 15807 75124 ns/op
BenchmarkSorts10000/BenchmarkBubbleSort-8 50 21047367 ns/op
BenchmarkSorts10000/BenchmarkBuiltinSort-8 1210 979537 ns/op
Making use of container/heap
to get a pre-sorted data structure that always returns the minimum value.
Tested using timestamps as the value to be sorted.
Files: