Go version
go version devel go1.24-fc968d49c1 Fri Aug 30 11:42:55 2024 -0400 darwin/arm64 (with CL 609095)
Output of go env in your module/workspace:
GO111MODULE=''
GOARCH='arm64'
GOBIN=''
GOCACHE='/Users/a/Library/Caches/go-build'
GOENV='/Users/a/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/Users/a/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/a/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/Users/a/proj/goroot'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/Users/a/proj/goroot/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='devel go1.24-fc968d49c1 Fri Aug 30 11:42:55 2024 -0400'
GODEBUG=''
GOTELEMETRY='local'
GOTELEMETRYDIR='/Users/a/Library/Application Support/go/telemetry'
GCCGO='gccgo'
GOARM64='v8.0'
AR='ar'
CC='clang'
CXX='clang++'
CGO_ENABLED='1'
GOMOD='/Users/a/Downloads/itergarbage/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/9c/t7b8z6m93sxgr4m4m4kbvllw0000gn/T/go-build4265899566=/tmp/go-build -gno-record-gcc-switches -fno-common'
What did you do?
Benchmarked https://go.dev/play/p/bNomKLr7kQX that compared the performance, in particular the allocations, of 3 ways to implement a nested iterator. The benchmarks are synthetic, because they're extracted from a larger project with more complex iterators.
$ go test -bench . -benchmem
goos: darwin
goarch: arm64
pkg: example.com
cpu: Apple M1 Pro
BenchmarkFormatIdiomatic-8 39902122 29.64 ns/op 24 B/op 2 allocs/op
BenchmarkFormatterIdiomatic-8 38252599 29.33 ns/op 24 B/op 2 allocs/op
BenchmarkFormatterNonIdiomatic-8 381599230 3.155 ns/op 0 B/op 0 allocs/op
PASS
ok example.com 4.857s
What did you see happen?
The idiomatic way to create an iterator is to return it from a function or method. In my example, the iterator I want is
func FormatIdiomatic() iter.Seq[rune]
however, that and its method variant,
func (f *Formatter) RunesIdiomatic() iter.Seq[rune]
both result in allocations.
Notably, the unidiomatic
func (f *Formatter) RunesNonIdiomatic(yield func(rune) bool)
doesn't allocate and in general these "direct" iterators runs significantly faster than their idiomatic siblings.
What did you expect to see?
No allocations from idiomatic iterators, even if nested.
Note that I ran the tests with https://go-review.googlesource.com/c/go/+/609095 applied, which didn't seem to make a difference.
Related: #66469 and #69015
Go version
go version devel go1.24-fc968d49c1 Fri Aug 30 11:42:55 2024 -0400 darwin/arm64 (with CL 609095)
Output of
go envin your module/workspace:What did you do?
Benchmarked https://go.dev/play/p/bNomKLr7kQX that compared the performance, in particular the allocations, of 3 ways to implement a nested iterator. The benchmarks are synthetic, because they're extracted from a larger project with more complex iterators.
What did you see happen?
The idiomatic way to create an iterator is to return it from a function or method. In my example, the iterator I want is
func FormatIdiomatic() iter.Seq[rune]however, that and its method variant,
func (f *Formatter) RunesIdiomatic() iter.Seq[rune]both result in allocations.
Notably, the unidiomatic
func (f *Formatter) RunesNonIdiomatic(yield func(rune) bool)doesn't allocate and in general these "direct" iterators runs significantly faster than their idiomatic siblings.
What did you expect to see?
No allocations from idiomatic iterators, even if nested.
Note that I ran the tests with https://go-review.googlesource.com/c/go/+/609095 applied, which didn't seem to make a difference.
Related: #66469 and #69015