mirror of
https://github.com/Crocmagnon/fat-contexts-article-companion.git
synced 2024-12-21 13:51:47 +01:00
initial commit
This commit is contained in:
commit
4230659178
7 changed files with 226 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
.idea
|
||||
data
|
||||
plot.html
|
25
README.md
Normal file
25
README.md
Normal file
|
@ -0,0 +1,25 @@
|
|||
# fat contexts
|
||||
|
||||
This repo holds code for [On context-induced performance bottleneck in Go](https://gabnotes.org/fat-contexts/).
|
||||
|
||||
Reproduce my results:
|
||||
|
||||
```console
|
||||
$ go test -bench=.
|
||||
goos: darwin
|
||||
goarch: arm64
|
||||
pkg: trash
|
||||
BenchmarkContext/shadow_1000-8 19352 61271 ns/op
|
||||
BenchmarkContext/fat_1000-8 532 2187010 ns/op
|
||||
BenchmarkContext/shadow_10000-8 1903 639371 ns/op
|
||||
BenchmarkContext/fat_10000-8 5 219400100 ns/op
|
||||
BenchmarkContext/shadow_100000-8 194 6374344 ns/op
|
||||
BenchmarkContext/fat_100000-8 1 21851940167 ns/op
|
||||
PASS
|
||||
ok trash 30.801s
|
||||
$ go run ./generate/main.go > data
|
||||
$ go run ./plot/main.go
|
||||
$ open plot.html
|
||||
```
|
||||
|
||||
Sorry there are a lot of hardcoded file names in here, but since I don't intend on reusing this code I won't fix it.
|
51
bench_test.go
Normal file
51
bench_test.go
Normal file
|
@ -0,0 +1,51 @@
|
|||
package main_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand/v2"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const key = "key"
|
||||
|
||||
func BenchmarkContext(b *testing.B) {
|
||||
benchmarks := []struct {
|
||||
times uint64
|
||||
}{
|
||||
{1_000},
|
||||
{10_000},
|
||||
{100_000},
|
||||
}
|
||||
for _, bm := range benchmarks {
|
||||
ctx := context.WithValue(context.Background(), key, "some value")
|
||||
b.Run(fmt.Sprintf("shadow %v", bm.times), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
shadow(ctx, bm.times)
|
||||
}
|
||||
})
|
||||
b.Run(fmt.Sprintf("fat %v", bm.times), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
fat(ctx, bm.times)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func shadow(ctx context.Context, times uint64) {
|
||||
for range times {
|
||||
ctx := contextWithRandom(ctx)
|
||||
_ = ctx.Value(key)
|
||||
}
|
||||
}
|
||||
|
||||
func fat(ctx context.Context, times uint64) {
|
||||
for range times {
|
||||
ctx = contextWithRandom(ctx)
|
||||
_ = ctx.Value(key)
|
||||
}
|
||||
}
|
||||
|
||||
func contextWithRandom(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, "other_key", rand.Int64())
|
||||
}
|
50
generate/main.go
Normal file
50
generate/main.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
)
|
||||
|
||||
const key = "key"
|
||||
|
||||
func main() {
|
||||
const times = 10_000
|
||||
|
||||
// Setup the value we want to retrieve in each iteration
|
||||
ctx := context.WithValue(context.Background(), key, "some-val")
|
||||
|
||||
fat(ctx, times)
|
||||
shadow(ctx, times)
|
||||
}
|
||||
|
||||
func fat(ctx context.Context, times uint64) {
|
||||
for range times {
|
||||
// wrap the context, each iteration makes it bigger
|
||||
ctx = contextWithRandom(ctx)
|
||||
|
||||
start := time.Now()
|
||||
// simulate the logging lib which retrieves context values
|
||||
_ = ctx.Value(key)
|
||||
|
||||
fmt.Printf("fat,%v\n", time.Since(start).Nanoseconds())
|
||||
}
|
||||
}
|
||||
|
||||
func shadow(ctx context.Context, times uint64) {
|
||||
for range times {
|
||||
// shadow the context, each iteration creates a new one and it doesn't grow
|
||||
ctx := contextWithRandom(ctx)
|
||||
|
||||
start := time.Now()
|
||||
_ = ctx.Value(key)
|
||||
|
||||
fmt.Printf("shadow,%v\n", time.Since(start).Nanoseconds())
|
||||
}
|
||||
}
|
||||
|
||||
func contextWithRandom(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, "other_key", uuid.Must(uuid.NewV4()))
|
||||
}
|
8
go.mod
Normal file
8
go.mod
Normal file
|
@ -0,0 +1,8 @@
|
|||
module trash
|
||||
|
||||
go 1.22.0
|
||||
|
||||
require (
|
||||
github.com/go-echarts/go-echarts/v2 v2.3.3
|
||||
github.com/gofrs/uuid v4.4.0+incompatible
|
||||
)
|
12
go.sum
Normal file
12
go.sum
Normal file
|
@ -0,0 +1,12 @@
|
|||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-echarts/go-echarts/v2 v2.3.3 h1:uImZAk6qLkC6F9ju6mZ5SPBqTyK8xjZKwSmwnCg4bxg=
|
||||
github.com/go-echarts/go-echarts/v2 v2.3.3/go.mod h1:56YlvzhW/a+du15f3S2qUGNDfKnFOeJSThBIrVFHDtI=
|
||||
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
|
||||
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.6.0 h1:jlIyCplCJFULU/01vCkhKuTyc3OorI3bJFuw6obfgho=
|
||||
github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
77
plot/main.go
Normal file
77
plot/main.go
Normal file
|
@ -0,0 +1,77 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"github.com/go-echarts/go-echarts/v2/charts"
|
||||
"github.com/go-echarts/go-echarts/v2/opts"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
shadow, fat := getData()
|
||||
plot := charts.NewLine()
|
||||
xAxis := make([]int, len(shadow))
|
||||
for i := range xAxis {
|
||||
xAxis[i] = i
|
||||
}
|
||||
|
||||
plot.SetGlobalOptions(
|
||||
charts.WithXAxisOpts(opts.XAxis{Name: "loop count"}),
|
||||
charts.WithYAxisOpts(opts.YAxis{Name: "Nanosecond"}),
|
||||
)
|
||||
|
||||
plot.
|
||||
SetXAxis(xAxis).
|
||||
AddSeries("Shadow", shadow).
|
||||
AddSeries("Fat", fat)
|
||||
|
||||
f, err := os.Create("plot.html")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = plot.Render(f)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func getData() ([]opts.LineData, []opts.LineData) {
|
||||
var shadow []opts.LineData
|
||||
var fat []opts.LineData
|
||||
|
||||
f, err := os.Open("data")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
r := csv.NewReader(f)
|
||||
for {
|
||||
record, err := r.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
series := record[0]
|
||||
val, err := strconv.ParseUint(record[1], 10, 64)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
point := opts.LineData{Value: val, Name: "ns"}
|
||||
|
||||
switch series {
|
||||
case "shadow":
|
||||
shadow = append(shadow, point)
|
||||
case "fat":
|
||||
fat = append(fat, point)
|
||||
}
|
||||
}
|
||||
|
||||
return shadow, fat
|
||||
}
|
Loading…
Reference in a new issue