Skip to content

Commit 8a6e51a

Browse files
committed
cmd/compile: generate makechan calls with int arguments
Where possible generate calls to runtime makechan with int arguments during compile time instead of makechan with int64 arguments. This eliminates converting arguments for calls to makechan with int64 arguments for platforms where int64 values do not fit into arguments of type int. A similar optimization for makeslice was introduced in CL golang.org/cl/27851. 386: name old time/op new time/op delta MakeChan/Byte 52.4ns ± 6% 45.0ns ± 1% -14.14% (p=0.000 n=10+10) MakeChan/Int 54.5ns ± 1% 49.1ns ± 1% -9.87% (p=0.000 n=10+10) MakeChan/Ptr 150ns ± 1% 143ns ± 0% -4.38% (p=0.000 n=9+7) MakeChan/Struct/0 49.2ns ± 2% 43.2ns ± 2% -12.27% (p=0.000 n=10+10) MakeChan/Struct/32 81.7ns ± 2% 76.2ns ± 1% -6.71% (p=0.000 n=10+10) MakeChan/Struct/40 88.4ns ± 2% 82.5ns ± 2% -6.60% (p=0.000 n=10+10) AMD64: name old time/op new time/op delta MakeChan/Byte 83.4ns ± 8% 80.8ns ± 3% ~ (p=0.171 n=10+10) MakeChan/Int 101ns ± 3% 101ns ± 2% ~ (p=0.412 n=10+10) MakeChan/Ptr 128ns ± 1% 128ns ± 1% ~ (p=0.191 n=10+10) MakeChan/Struct/0 67.6ns ± 3% 68.7ns ± 4% ~ (p=0.224 n=10+10) MakeChan/Struct/32 138ns ± 1% 139ns ± 1% ~ (p=0.185 n=10+9) MakeChan/Struct/40 154ns ± 1% 154ns ± 1% -0.55% (p=0.027 n=10+9) Change-Id: Ie854cb066007232c5e9f71ea7d6fe27e81a9c050 Reviewed-on: https://go-review.googlesource.com/55140 Run-TryBot: Martin Möhrmann <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Keith Randall <[email protected]>
1 parent 4c55774 commit 8a6e51a

8 files changed

Lines changed: 246 additions & 95 deletions

File tree

src/cmd/compile/internal/gc/builtin.go

Lines changed: 88 additions & 86 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cmd/compile/internal/gc/builtin/runtime.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ func mapdelete_faststr(mapType *byte, hmap map[any]any, key any)
116116
func mapiternext(hiter *any)
117117

118118
// *byte is really *runtime.Type
119-
func makechan(chanType *byte, hint int64) (hchan chan any)
119+
func makechan64(chanType *byte, size int64) (hchan chan any)
120+
func makechan(chanType *byte, size int) (hchan chan any)
120121
func chanrecv1(hchan <-chan any, elem *any)
121122
func chanrecv2(hchan <-chan any, elem *any) bool
122123
func chansend1(hchan chan<- any, elem *any)

src/cmd/compile/internal/gc/walk.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1417,7 +1417,21 @@ opswitch:
14171417
n = mkcall1(fn, nil, init, n.Left)
14181418

14191419
case OMAKECHAN:
1420-
n = mkcall1(chanfn("makechan", 1, n.Type), n.Type, init, typename(n.Type), conv(n.Left, types.Types[TINT64]))
1420+
// When size fits into int, use makechan instead of
1421+
// makechan64, which is faster and shorter on 32 bit platforms.
1422+
size := n.Left
1423+
fnname := "makechan64"
1424+
argtype := types.Types[TINT64]
1425+
1426+
// Type checking guarantees that TIDEAL size is positive and fits in an int.
1427+
// The case of size overflow when converting TUINT or TUINTPTR to TINT
1428+
// will be handled by the negative range checks in makechan during runtime.
1429+
if size.Type.IsKind(TIDEAL) || maxintval[size.Type.Etype].Cmp(maxintval[TUINT]) <= 0 {
1430+
fnname = "makechan"
1431+
argtype = types.Types[TINT]
1432+
}
1433+
1434+
n = mkcall1(chanfn(fnname, 1, n.Type), n.Type, init, typename(n.Type), conv(size, argtype))
14211435

14221436
case OMAKEMAP:
14231437
t := n.Type

src/reflect/value.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2072,7 +2072,7 @@ func MakeChan(typ Type, buffer int) Value {
20722072
if typ.ChanDir() != BothDir {
20732073
panic("reflect.MakeChan: unidirectional channel type")
20742074
}
2075-
ch := makechan(typ.(*rtype), uint64(buffer))
2075+
ch := makechan(typ.(*rtype), buffer)
20762076
return Value{typ.common(), ch, flag(Chan)}
20772077
}
20782078

@@ -2480,7 +2480,7 @@ func chanrecv(ch unsafe.Pointer, nb bool, val unsafe.Pointer) (selected, receive
24802480
//go:noescape
24812481
func chansend(ch unsafe.Pointer, val unsafe.Pointer, nb bool) bool
24822482

2483-
func makechan(typ *rtype, size uint64) (ch unsafe.Pointer)
2483+
func makechan(typ *rtype, size int) (ch unsafe.Pointer)
24842484
func makemap(t *rtype, cap int) (m unsafe.Pointer)
24852485

24862486
//go:noescape

src/runtime/chan.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,19 @@ type waitq struct {
5555
}
5656

5757
//go:linkname reflect_makechan reflect.makechan
58-
func reflect_makechan(t *chantype, size int64) *hchan {
58+
func reflect_makechan(t *chantype, size int) *hchan {
5959
return makechan(t, size)
6060
}
6161

62-
func makechan(t *chantype, size int64) *hchan {
62+
func makechan64(t *chantype, size int64) *hchan {
63+
if int64(int(size)) != size {
64+
panic(plainError("makechan: size out of range"))
65+
}
66+
67+
return makechan(t, int(size))
68+
}
69+
70+
func makechan(t *chantype, size int) *hchan {
6371
elem := t.elem
6472

6573
// compiler checks this but be safe.
@@ -69,7 +77,7 @@ func makechan(t *chantype, size int64) *hchan {
6977
if hchanSize%maxAlign != 0 || elem.align > maxAlign {
7078
throw("makechan: bad alignment")
7179
}
72-
if size < 0 || int64(uintptr(size)) != size || (elem.size > 0 && uintptr(size) > (_MaxMem-hchanSize)/elem.size) {
80+
if size < 0 || (elem.size > 0 && uintptr(size) > (_MaxMem-hchanSize)/elem.size) {
7381
panic(plainError("makechan: size out of range"))
7482
}
7583

src/runtime/chan_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,59 @@ done:
669669
<-ready2
670670
}
671671

672+
type (
673+
struct0 struct{}
674+
struct32 struct{ a, b, c, d int64 }
675+
struct40 struct{ a, b, c, d, e int64 }
676+
)
677+
678+
func BenchmarkMakeChan(b *testing.B) {
679+
b.Run("Byte", func(b *testing.B) {
680+
var x chan byte
681+
for i := 0; i < b.N; i++ {
682+
x = make(chan byte, 8)
683+
}
684+
close(x)
685+
})
686+
b.Run("Int", func(b *testing.B) {
687+
var x chan int
688+
for i := 0; i < b.N; i++ {
689+
x = make(chan int, 8)
690+
}
691+
close(x)
692+
})
693+
b.Run("Ptr", func(b *testing.B) {
694+
var x chan *byte
695+
for i := 0; i < b.N; i++ {
696+
x = make(chan *byte, 8)
697+
}
698+
close(x)
699+
})
700+
b.Run("Struct", func(b *testing.B) {
701+
b.Run("0", func(b *testing.B) {
702+
var x chan struct0
703+
for i := 0; i < b.N; i++ {
704+
x = make(chan struct0, 8)
705+
}
706+
close(x)
707+
})
708+
b.Run("32", func(b *testing.B) {
709+
var x chan struct32
710+
for i := 0; i < b.N; i++ {
711+
x = make(chan struct32, 8)
712+
}
713+
close(x)
714+
})
715+
b.Run("40", func(b *testing.B) {
716+
var x chan struct40
717+
for i := 0; i < b.N; i++ {
718+
x = make(chan struct40, 8)
719+
}
720+
close(x)
721+
})
722+
})
723+
}
724+
672725
func BenchmarkChanNonblocking(b *testing.B) {
673726
myc := make(chan int)
674727
b.RunParallel(func(pb *testing.PB) {

test/chancap.go

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,17 @@
88

99
package main
1010

11+
import (
12+
"strings"
13+
"unsafe"
14+
)
15+
16+
type T chan int
17+
18+
const ptrSize = unsafe.Sizeof((*byte)(nil))
19+
1120
func main() {
12-
c := make(chan int, 10)
21+
c := make(T, 10)
1322
if len(c) != 0 || cap(c) != 10 {
1423
println("chan len/cap ", len(c), cap(c), " want 0 10")
1524
panic("fail")
@@ -23,9 +32,39 @@ func main() {
2332
panic("fail")
2433
}
2534

26-
c = make(chan int)
35+
c = make(T)
2736
if len(c) != 0 || cap(c) != 0 {
2837
println("chan len/cap ", len(c), cap(c), " want 0 0")
2938
panic("fail")
3039
}
40+
41+
n := -1
42+
shouldPanic("makechan: size out of range", func() { _ = make(T, n) })
43+
shouldPanic("makechan: size out of range", func() { _ = make(T, int64(n)) })
44+
if ptrSize == 8 {
45+
n = 1 << 20
46+
n <<= 20
47+
shouldPanic("makechan: size out of range", func() { _ = make(T, n) })
48+
n <<= 20
49+
shouldPanic("makechan: size out of range", func() { _ = make(T, n) })
50+
} else {
51+
n = 1<<31 - 1
52+
shouldPanic("makechan: size out of range", func() { _ = make(T, n) })
53+
shouldPanic("makechan: size out of range", func() { _ = make(T, int64(n)) })
54+
}
55+
}
56+
57+
func shouldPanic(str string, f func()) {
58+
defer func() {
59+
err := recover()
60+
if err == nil {
61+
panic("did not panic")
62+
}
63+
s := err.(error).Error()
64+
if !strings.Contains(s, str) {
65+
panic("got panic " + s + ", want " + str)
66+
}
67+
}()
68+
69+
f()
3170
}

0 commit comments

Comments
 (0)