go cmd/compile: 嵌套迭代器中的优化机会

mmvthczy  于 10个月前  发布在  Go
关注(0)|答案(2)|浏览(93)

Go版本
go版本 devel go1.23-5e1e3a0025 Thu Mar 21 17:25:54 2024 +0000 linux/amd64

在你的模块/工作区中运行go env的输出结果:

  1. GO111MODULE=''
  2. GOARCH='amd64'
  3. GOBIN=''
  4. GOCACHE='/home/rogpeppe/.cache/go-build'
  5. GOENV='/home/rogpeppe/.config/go/env'
  6. GOEXE=''
  7. GOEXPERIMENT=''
  8. GOFLAGS=''
  9. GOHOSTARCH='amd64'
  10. GOHOSTOS='linux'
  11. GOINSECURE=''
  12. GOMODCACHE='/home/rogpeppe/src/go/pkg/mod'
  13. GOOS='linux'
  14. GOPATH='/home/rogpeppe/src/go'
  15. GOPROXY='https://proxy.golang.org,direct'
  16. GOROOT='/home/rogpeppe/go'
  17. GOSUMDB='sum.golang.org'
  18. GOTMPDIR=''
  19. GOTOOLCHAIN='auto'
  20. GOTOOLDIR='/home/rogpeppe/go/pkg/tool/linux_amd64'
  21. GOVCS=''
  22. GOVERSION='devel go1.23-5e1e3a0025 Thu Mar 21 17:25:54 2024 +0000'
  23. GODEBUG=''
  24. GCCGO='gccgo'
  25. GOAMD64='v1'
  26. AR='ar'
  27. CC='gcc'
  28. CXX='g++'
  29. CGO_ENABLED='1'
  30. GOMOD='/home/rogpeppe/go/src/go.mod'
  31. GOWORK=''
  32. CGO_CFLAGS='-O2 -g'
  33. CGO_CPPFLAGS=''
  34. CGO_CXXFLAGS='-O2 -g'
  35. CGO_FFLAGS='-O2 -g'
  36. CGO_LDFLAGS='-O2 -g'
  37. PKG_CONFIG='pkg-config'
  38. GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build2835753179=/tmp/go-build -gno-record-gcc-switches'

你做了什么?

我运行了这个测试脚本:

  1. exec go run main1.go
  2. cp stdout result1
  3. exec go run main2.go
  4. cp stdout result2
  5. grep '1 allocs/op' result1
  6. grep '1 allocs/op' result2
  7. cmp stdout want-stdout
  8. -- go.mod --
  9. module m
  10. -- main1.go --
  11. // This version uses an extra layer of iteration.
  12. package main
  13. import (
  14. "fmt"
  15. "testing"
  16. )
  17. func main() {
  18. r := testing.Benchmark(func(b *testing.B) {
  19. err := errorT{"a"}
  20. for range b.N {
  21. if !AsType[errorT](err) {
  22. panic("not ok")
  23. }
  24. }
  25. })
  26. fmt.Printf("BenchmarkAsType1: %v %v\n", r, r.MemString())
  27. }
  28. func AsType[T any](err error) bool {
  29. for _ = range AllAs[T](err) {
  30. return true
  31. }
  32. return false
  33. }
  34. func AllAs[T any](err error) func(func(T) bool) {
  35. return func(yield func(T) bool) {
  36. for err := range All(err) {
  37. if x, ok := err.(T); ok {
  38. if !yield(x) {
  39. return
  40. }
  41. }
  42. }
  43. }
  44. }
  45. func All(err error) func(func(error) bool) {
  46. return func(yield func(error) bool) {
  47. yield(err)
  48. }
  49. }
  50. type errorT struct{ s string }
  51. func (e errorT) Error() string { return fmt.Sprintf("errorT(%s)", e.s) }
  52. -- main2.go --
  53. // This version avoids the intermediate iterator.
  54. package main
  55. import (
  56. "fmt"
  57. "testing"
  58. )
  59. func main() {
  60. r := testing.Benchmark(func(b *testing.B) {
  61. err := errorT{"a"}
  62. for range b.N {
  63. if !AsType[errorT](err) {
  64. panic("not ok")
  65. }
  66. }
  67. })
  68. fmt.Printf("BenchmarkAsType2: %v %v\n", r, r.MemString())
  69. }
  70. func AsType[T any](err error) bool {
  71. for err := range All(err) {
  72. if _, ok := err.(T); ok {
  73. return true
  74. }
  75. }
  76. return false
  77. }
  78. func All(err error) func(func(error) bool) {
  79. return func(yield func(error) bool) {
  80. yield(err)
  81. }
  82. }
  83. type errorT struct{ s string }
  84. func (e errorT) Error() string { return fmt.Sprintf("errorT(%s)", e.s) }

你看到了什么?

  1. > exec go run main1.go
  2. [stdout]
  3. BenchmarkAsType1: 5617491 199.9 ns/op 168 B/op 9 allocs/op
  4. > cp stdout result1
  5. > exec go run main2.go
  6. [stdout]
  7. BenchmarkAsType2: 31012682 32.98 ns/op 16 B/op 1 allocs/op
  8. > cp stdout result2
  9. > grep '1 allocs/op' result1
  10. [result1]
  11. BenchmarkAsType1: 5617491 199.9 ns/op 168 B/op 9 allocs/op
  12. FAIL: /tmp/testscript4012864748/x.txtar/script.txtar:5: no match for `1 allocs/op` found in result1
  13. error running /tmp/x.txtar in /tmp/testscript4012864748/x.txtar

你期望看到什么?

我期望这两个版本具有大致相似的性能特性。
它们都在做完全相同的基本工作,但其中一个比另一个快大约6倍。
也许在足够的编译器智能的情况下可以减少额外的分配。
顺便说一下,这个问题是在探索#66455实现时遇到的。

irtuqstp

irtuqstp1#

@dr2chase @mdempsky

js5cn81o

js5cn81o2#

为了记录,这似乎与通用类型在这里发挥作用的事实无关。我将AsType更改为非通用,在两个示例中结果相同。

相关问题