Skip to content

mkock/efficientgo

Repository files navigation

Efficient Go

This is just a bunch of code experiments in Go, some of which are benchmarked and documented here for educational and self-awareness purposes.

JSON Marshal

Marshalling a simple struct into JSON using two strategies:

  1. MarshalDirect: the usual approach of marshalling into a byte slice and then printing that.
  2. MarshalWriter: avoiding the temporary buffer by writing directly to a Writer.

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.

String Concatenation

Building a string from a series of random text snippets using two strategies:

  1. ConcatString: the usual approach of constructing a new string from the concatenation of two others.
  2. ConcatStringBuilder: avoiding the overhead of allocation by leveraging the strings.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.

Decorators

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:

sync.Pool

Iterating over a struct and using it for managing two random numbers to generate a result, using two strategies:

  1. unpooledRandomizer: allocates a struct per iteration.
  2. 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

Heap

Three different strategies for printing to stdout of which two escape variables to the heap, and one does not:

  1. printAsValue simply prints the struct, which escapes to the heap.
  2. printAsIndividualFields prints each field of the struct without reflection, but still escapes to heap.
  3. 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

Extract and Sort ID's From Slice

Performance benchmarks for extracting an ID from a slice of structs:

  1. BenchmarkBubbleSort creates the sorted slice by bubbling each value through the slice until it finds the correct location.
  2. BenchmarkBuiltinSort starts with a loop over the slice to extract the ID's, and then calls the builtin sort.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

Min-Heap

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:

About

Code experiments in Go

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages