Skip to content

Instantly share code, notes, and snippets.

@xorcare
Last active January 31, 2022 18:26
Show Gist options
  • Save xorcare/66e269d33926663c2b4e1350e1558b3b to your computer and use it in GitHub Desktop.
Save xorcare/66e269d33926663c2b4e1350e1558b3b to your computer and use it in GitHub Desktop.
Modified source code for article Pointers Might Not be Ideal as Arguments
// gen.go
// This code is derived from the article
// https://golang.design/research/pointer-params
// and modified to fit my needs.
package main
import (
"bytes"
"fmt"
"go/format"
"io/ioutil"
"log"
"strings"
"text/template"
)
var (
head = `// Code generated by go run gen.go; DO NOT EDIT.
package fields_test
import "testing"
`
structTmpl = template.Must(template.New("ss").Parse(`
type {{.Name}} struct {
{{.Properties}}
}
func (s {{.Name}}) addv(ss {{.Name}}) {{.Name}} {
return {{.Name}}{
{{.Addv}}
}
}
func (s {{.Name}}) addp(ss *{{.Name}}) {{.Name}} {
return {{.Name}}{
{{.Addv}}
}
}
//go:noinline
func (s {{.Name}}) addvni(ss {{.Name}}) {{.Name}} {
return {{.Name}}{
{{.Addv}}
}
}
//go:noinline
func (s {{.Name}}) addpni(ss *{{.Name}}) {{.Name}} {
return {{.Name}}{
{{.Addv}}
}
}
var _{{.Name}} {{.Name}}
`))
benchHead = `func BenchmarkVec(b *testing.B) {`
benchTail = `}`
benchBody = template.Must(template.New("bench").Parse(`
b.Run("add-by-value-{{.Name}}", func(b *testing.B) {
{{.InitV}}
b.ResetTimer()
for i := 0; i < b.N; i++ {
if i%2 == 0 {
v1 = v1.addv(v2)
} else {
v2 = v2.addv(v1)
}
}
_{{.Name}} = v1
_{{.Name}} = v2
})
b.Run("add-by-pointer-{{.Name}}", func(b *testing.B) {
{{.InitP}}
b.ResetTimer()
for i := 0; i < b.N; i++ {
if i%2 == 0 {
v1 = v1.addp(&v2)
} else {
v2 = v2.addp(&v1)
}
}
_{{.Name}} = v1
_{{.Name}} = v2
})
b.Run("add-by-value-noinline{{.Name}}", func(b *testing.B) {
{{.InitV}}
b.ResetTimer()
for i := 0; i < b.N; i++ {
if i%2 == 0 {
v1 = v1.addvni(v2)
} else {
v2 = v2.addvni(v1)
}
}
_{{.Name}} = v1
_{{.Name}} = v2
})
b.Run("add-by-pointer-noinline{{.Name}}", func(b *testing.B) {
{{.InitP}}
b.ResetTimer()
for i := 0; i < b.N; i++ {
if i%2 == 0 {
v1 = v1.addpni(&v2)
} else {
v2 = v2.addpni(&v1)
}
}
_{{.Name}} = v1
_{{.Name}} = v2
})
`))
)
type structFields struct {
Name string
Properties string
Addv string
Addp string
NoInline bool
}
type benchFields struct {
Name string
InitV string
InitP string
}
func main() {
buff := bytes.NewBuffer(make([]byte, 0, 4096))
buff.WriteString(head)
const maxNumberOfFields = 16
const strTypeOfField = "float64"
const noInline = true
const startCountOfFields = 1
const nameFormat = "s%04d"
for i := startCountOfFields; i <= maxNumberOfFields; i++ {
var ps, adv, adpl, adpr []string
for j := 1; j <= i; j++ {
ps = append(ps, fmt.Sprintf("x%d\t%s", j, strTypeOfField))
adv = append(adv, fmt.Sprintf("s.x%d + ss.x%d,", j, j))
adpl = append(adpl, fmt.Sprintf("s.x%d", j))
adpr = append(adpr, fmt.Sprintf("s.x%d + ss.x%d", j, j))
}
err := structTmpl.Execute(buff, structFields{
Name: fmt.Sprintf(nameFormat, i),
Properties: strings.Join(ps, "\n"),
Addv: strings.Join(adv, "\n"),
Addp: strings.Join(adpl, ",") + " = " + strings.Join(adpr, ","),
NoInline: noInline,
})
if err != nil {
log.Fatal(err)
}
}
buff.WriteString(benchHead)
for countOfFields := startCountOfFields; countOfFields <= maxNumberOfFields; countOfFields++ {
var firstSetValues, secondSetValues []string
for value := 1; value <= countOfFields; value++ {
firstSetValues = append(firstSetValues, fmt.Sprintf("%d", value%127))
secondSetValues = append(secondSetValues, fmt.Sprintf("%d", (value+countOfFields)%127))
}
firstVectorValues := strings.Join(firstSetValues, ", ")
secondVectorValues := strings.Join(secondSetValues, ", ")
err := benchBody.Execute(buff, benchFields{
Name: fmt.Sprintf(nameFormat, countOfFields),
InitV: fmt.Sprintf("v1 := s%04d{%s}\nv2 := s%04d{%s}", countOfFields, firstVectorValues, countOfFields, secondVectorValues),
InitP: fmt.Sprintf("v1 := s%04d{%s}\nv2 := s%04d{%s}", countOfFields, firstVectorValues, countOfFields, secondVectorValues),
})
if err != nil {
log.Fatal(err)
}
}
buff.WriteString(benchTail)
source, err := format.Source(buff.Bytes())
if err != nil {
log.Fatal(err)
}
if err := ioutil.WriteFile(makeFilename(strTypeOfField, noInline, maxNumberOfFields), source, 0660); err != nil {
log.Fatal(err)
}
}
func makeFilename(strTypeOfField string, noinline bool, maxNumberOfFields int) string {
str := "inline"
if noinline {
str = "noinline"
}
return fmt.Sprintf("benchmark_with_%s_fileds_type_%s_max_fields_%d_test.go", str, strTypeOfField, maxNumberOfFields)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment