package main

import (
	"fmt"
	"testing"
)

var sink *int
var boxed any
var number int
var fn func() int

func makePointer(n int) *int { return &n }

//go:noinline
func makePointerNoInline(n int) *int { return &n }

//go:noinline
func consume(p *int) int { return *p }

//go:noinline
func localAddress(n int) int { return consume(&n) }

//go:noinline
func keepClosure() func() int { n := 0; return func() int { n++; return n } }

//go:noinline
func dynamic(n int) int {
	b := make([]byte, n)
	for i := range b {
		b[i] = byte(i)
	}
	return int(b[n-1])
}

//go:noinline
func keepBox(n int) { boxed = n }

func measure(name string, f func()) {
	fmt.Printf("%s: %.0f allocs/run\n", name, testing.AllocsPerRun(1000, f))
}
func main() {
	fmt.Println("values:", localAddress(42), *makePointer(42), *makePointerNoInline(42))
	f := keepClosure()
	fmt.Println("closure:", f(), f())
	measure("local address", func() { number = localAddress(42) })
	measure("inlined pointer consumed", func() { number = *makePointer(42) })
	measure("noinline pointer consumed", func() { number = *makePointerNoInline(42) })
	measure("pointer retained", func() { sink = makePointer(42) })
	measure("closure retained", func() { fn = keepClosure() })
	measure("interface retains 1000", func() { keepBox(1000) })
	measure("dynamic 16", func() { number = dynamic(16) })
	measure("dynamic 64", func() { number = dynamic(64) })
	measure("dynamic 4096", func() { number = dynamic(4096) })
}
