mirror of
https://github.com/golang/go
synced 2024-11-17 18:54:42 -07:00
2db4cc38a0
Consider switch x:= x.(type) { case int: // int stmts case error: // error stmts } Prior to this change, we lowered this roughly as: if x, ok := x.(int); ok { // int stmts } else if x, ok := x.(error); ok { // error stmts } x, ok := x.(error) is implemented with a call to runtime.assertE2I2 or runtime.assertI2I2. x, ok := x.(int) generates inline code that checks whether x has type int, and populates x and ok as appropriate. We then immediately branch again on ok. The shortcircuit pass in the SSA backend is designed to recognize situations like this, in which we are immediately branching on a bool value that we just calculated with a branch. However, the shortcircuit pass has limitations when the intermediate state has phis. In this case, the phi value is x (the int). CL 222923 improved the situation, but many cases are still unhandled. I have further improvements in progress, which is how I found this particular problem, but they are expensive, and may or may not see the light of day. In the common case of a lone concrete type in a type switch case, it is easier and cheaper to simply lower a different way, roughly: if _, ok := x.(int); ok { x := x.(int) // int stmts } Instead of using a type assertion, though, we extract the value of x from the interface directly. This removes the need to track x (the int) across the branch on ok, which removes the phi, which lets the shortcircuit pass do its job. Benchmarks for encoding/binary show improvements, as well as some wild swings on the super fast benchmarks (alignment effects?): name old time/op new time/op delta ReadSlice1000Int32s-8 5.25µs ± 2% 4.87µs ± 3% -7.11% (p=0.000 n=44+49) ReadStruct-8 451ns ± 2% 417ns ± 2% -7.39% (p=0.000 n=45+46) WriteStruct-8 412ns ± 2% 405ns ± 3% -1.58% (p=0.000 n=46+48) ReadInts-8 296ns ± 8% 275ns ± 3% -7.23% (p=0.000 n=48+50) WriteInts-8 324ns ± 1% 318ns ± 2% -1.67% (p=0.000 n=44+49) WriteSlice1000Int32s-8 5.21µs ± 2% 4.92µs ± 1% -5.67% (p=0.000 n=46+44) PutUint16-8 0.58ns ± 2% 0.59ns ± 2% +0.63% (p=0.000 n=49+49) PutUint32-8 0.87ns ± 1% 0.58ns ± 1% -33.10% (p=0.000 n=46+44) PutUint64-8 0.66ns ± 2% 0.87ns ± 2% +33.07% (p=0.000 n=47+48) LittleEndianPutUint16-8 0.86ns ± 2% 0.87ns ± 2% +0.55% (p=0.003 n=47+50) LittleEndianPutUint32-8 0.87ns ± 1% 0.87ns ± 1% ~ (p=0.547 n=45+47) LittleEndianPutUint64-8 0.87ns ± 2% 0.87ns ± 1% ~ (p=0.451 n=46+47) ReadFloats-8 79.8ns ± 5% 75.9ns ± 2% -4.83% (p=0.000 n=50+47) WriteFloats-8 89.3ns ± 1% 88.9ns ± 1% -0.48% (p=0.000 n=46+44) ReadSlice1000Float32s-8 5.51µs ± 1% 4.87µs ± 2% -11.74% (p=0.000 n=47+46) WriteSlice1000Float32s-8 5.51µs ± 1% 4.93µs ± 1% -10.60% (p=0.000 n=48+47) PutUvarint32-8 25.9ns ± 2% 24.0ns ± 2% -7.02% (p=0.000 n=48+50) PutUvarint64-8 75.1ns ± 1% 61.5ns ± 2% -18.12% (p=0.000 n=45+47) [Geo mean] 57.3ns 54.3ns -5.33% Despite the rarity of type switches, this generates noticeably smaller binaries. file before after Δ % addr2line 4413296 4409200 -4096 -0.093% api 5982648 5962168 -20480 -0.342% cgo 4854168 4833688 -20480 -0.422% compile 19694784 19682560 -12224 -0.062% cover 5278008 5265720 -12288 -0.233% doc 4694824 4682536 -12288 -0.262% fix 3411336 3394952 -16384 -0.480% link 6721496 6717400 -4096 -0.061% nm 4371152 4358864 -12288 -0.281% objdump 4760960 4752768 -8192 -0.172% pprof 14810820 14790340 -20480 -0.138% trace 11681076 11668788 -12288 -0.105% vet 8285464 8244504 -40960 -0.494% total 115824120 115627576 -196544 -0.170% Compiler performance is marginally improved (note that go/types has many type switches): name old alloc/op new alloc/op delta Template 35.0MB ± 0% 35.0MB ± 0% +0.09% (p=0.008 n=5+5) Unicode 28.5MB ± 0% 28.5MB ± 0% ~ (p=0.548 n=5+5) GoTypes 114MB ± 0% 114MB ± 0% -0.76% (p=0.008 n=5+5) Compiler 541MB ± 0% 541MB ± 0% -0.03% (p=0.008 n=5+5) SSA 1.17GB ± 0% 1.17GB ± 0% ~ (p=0.841 n=5+5) Flate 21.9MB ± 0% 21.9MB ± 0% ~ (p=0.421 n=5+5) GoParser 26.9MB ± 0% 26.9MB ± 0% ~ (p=0.222 n=5+5) Reflect 74.6MB ± 0% 74.6MB ± 0% ~ (p=1.000 n=5+5) Tar 32.9MB ± 0% 32.8MB ± 0% ~ (p=0.056 n=5+5) XML 42.4MB ± 0% 42.1MB ± 0% -0.77% (p=0.008 n=5+5) [Geo mean] 73.2MB 73.1MB -0.15% name old allocs/op new allocs/op delta Template 377k ± 0% 377k ± 0% +0.06% (p=0.008 n=5+5) Unicode 354k ± 0% 354k ± 0% ~ (p=0.095 n=5+5) GoTypes 1.31M ± 0% 1.30M ± 0% -0.73% (p=0.008 n=5+5) Compiler 5.44M ± 0% 5.44M ± 0% -0.04% (p=0.008 n=5+5) SSA 11.7M ± 0% 11.7M ± 0% ~ (p=1.000 n=5+5) Flate 239k ± 0% 239k ± 0% ~ (p=1.000 n=5+5) GoParser 302k ± 0% 302k ± 0% -0.04% (p=0.008 n=5+5) Reflect 977k ± 0% 977k ± 0% ~ (p=0.690 n=5+5) Tar 346k ± 0% 346k ± 0% ~ (p=0.889 n=5+5) XML 431k ± 0% 430k ± 0% -0.25% (p=0.008 n=5+5) [Geo mean] 806k 806k -0.10% For packages with many type switches, this considerably shrinks function text size. Some examples: file before after Δ % encoding/binary.s 30726 29504 -1222 -3.977% go/printer.s 77597 76005 -1592 -2.052% cmd/vendor/golang.org/x/tools/go/ast/astutil.s 65704 63318 -2386 -3.631% cmd/vendor/golang.org/x/tools/go/analysis/passes/unreachable.s 8047 7714 -333 -4.138% Text size regressions are rare. Change-Id: Ic10982bbb04876250eaa5bfee97990141ae5fc28 Reviewed-on: https://go-review.googlesource.com/c/go/+/228106 Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com> Reviewed-by: Keith Randall <khr@golang.org> |
||
---|---|---|
.. | ||
alias3.dir | ||
bench | ||
chan | ||
closure3.dir | ||
codegen | ||
ddd2.dir | ||
dwarf | ||
fixedbugs | ||
import2.dir | ||
import4.dir | ||
interface | ||
intrinsic.dir | ||
ken | ||
linkname.dir | ||
method4.dir | ||
oldescape_linkname.dir | ||
retjmp.dir | ||
runtime | ||
stress | ||
syntax | ||
uintptrescapes.dir | ||
64bit.go | ||
235.go | ||
alg.go | ||
alias1.go | ||
alias2.go | ||
alias3.go | ||
alias.go | ||
align.go | ||
append1.go | ||
append.go | ||
args.go | ||
armimm.go | ||
assign1.go | ||
assign.go | ||
atomicload.go | ||
bigalg.go | ||
bigmap.go | ||
blank1.go | ||
blank.go | ||
bom.go | ||
bombad.go | ||
bounds.go | ||
chancap.go | ||
chanlinear.go | ||
char_lit1.go | ||
char_lit.go | ||
checkbce.go | ||
clearfat.go | ||
closedchan.go | ||
closure1.go | ||
closure2.go | ||
closure3.go | ||
closure4.go | ||
closure.go | ||
cmp6.go | ||
cmp.go | ||
cmplx.go | ||
cmplxdivide1.go | ||
cmplxdivide.c | ||
cmplxdivide.go | ||
complit1.go | ||
complit.go | ||
compos.go | ||
const1.go | ||
const2.go | ||
const3.go | ||
const4.go | ||
const5.go | ||
const6.go | ||
const.go | ||
convert1.go | ||
convert2.go | ||
convert3.go | ||
convert.go | ||
convlit1.go | ||
convlit.go | ||
convT2X.go | ||
copy1.go | ||
copy.go | ||
crlf.go | ||
ddd1.go | ||
ddd2.go | ||
ddd.go | ||
decl.go | ||
declbad.go | ||
defer.go | ||
defererrcheck.go | ||
deferfin.go | ||
defernil.go | ||
deferprint.go | ||
deferprint.out | ||
devirt.go | ||
divide.go | ||
divmod.go | ||
empty.go | ||
env.go | ||
eof1.go | ||
eof.go | ||
escape2.go | ||
escape2n.go | ||
escape3.go | ||
escape4.go | ||
escape5.go | ||
escape_array.go | ||
escape_calls.go | ||
escape_closure.go | ||
escape_field.go | ||
escape_goto.go | ||
escape_hash_maphash.go | ||
escape_iface.go | ||
escape_indir.go | ||
escape_level.go | ||
escape_map.go | ||
escape_param.go | ||
escape_runtime_atomic.go | ||
escape_selfassign.go | ||
escape_slice.go | ||
escape_struct_param1.go | ||
escape_struct_param2.go | ||
escape_struct_return.go | ||
escape_sync_atomic.go | ||
escape_unsafe.go | ||
escape.go | ||
fibo.go | ||
finprofiled.go | ||
float_lit2.go | ||
float_lit3.go | ||
float_lit.go | ||
floatcmp.go | ||
for.go | ||
func1.go | ||
func2.go | ||
func3.go | ||
func4.go | ||
func5.go | ||
func6.go | ||
func7.go | ||
func8.go | ||
func.go | ||
funcdup2.go | ||
funcdup.go | ||
gc1.go | ||
gc2.go | ||
gc.go | ||
gcgort.go | ||
gcstring.go | ||
goprint.go | ||
goprint.out | ||
goto.go | ||
heapsampling.go | ||
helloworld.go | ||
helloworld.out | ||
if.go | ||
import1.go | ||
import2.go | ||
import4.go | ||
import5.go | ||
import6.go | ||
import.go | ||
index0.go | ||
index1.go | ||
index2.go | ||
index.go | ||
indirect1.go | ||
indirect.go | ||
init1.go | ||
init.go | ||
initcomma.go | ||
initempty.go | ||
initialize.go | ||
initializerr.go | ||
initloop.go | ||
inline_big.go | ||
inline_caller.go | ||
inline_callers.go | ||
inline_literal.go | ||
inline_math_bits_rotate.go | ||
inline_sync.go | ||
inline_variadic.go | ||
inline.go | ||
int_lit.go | ||
intcvt.go | ||
intrinsic_atomic.go | ||
intrinsic.go | ||
iota.go | ||
label1.go | ||
label.go | ||
linkmain_run.go | ||
linkmain.go | ||
linkname.go | ||
linkobj.go | ||
linkx_run.go | ||
linkx.go | ||
literal2.go | ||
literal.go | ||
live1.go | ||
live2.go | ||
live_syscall.go | ||
live.go | ||
locklinear.go | ||
loopbce.go | ||
makechan.go | ||
makemap.go | ||
makenew.go | ||
mallocfin.go | ||
map1.go | ||
map.go | ||
mapclear.go | ||
maplinear.go | ||
mergemul.go | ||
method1.go | ||
method2.go | ||
method3.go | ||
method4.go | ||
method5.go | ||
method6.go | ||
method7.go | ||
method.go | ||
named1.go | ||
named.go | ||
nil.go | ||
nilcheck.go | ||
nilptr2.go | ||
nilptr3.go | ||
nilptr4.go | ||
nilptr5_aix.go | ||
nilptr5_wasm.go | ||
nilptr5.go | ||
nilptr_aix.go | ||
nilptr.go | ||
nosplit.go | ||
notinheap2.go | ||
notinheap3.go | ||
notinheap.go | ||
nowritebarrier.go | ||
nul1.go | ||
opt_branchlikely.go | ||
parentype.go | ||
peano.go | ||
phiopt.go | ||
print.go | ||
print.out | ||
printbig.go | ||
printbig.out | ||
prove.go | ||
range.go | ||
README.md | ||
recover1.go | ||
recover2.go | ||
recover3.go | ||
recover4.go | ||
recover5.go | ||
recover.go | ||
reflectmethod1.go | ||
reflectmethod2.go | ||
reflectmethod3.go | ||
reflectmethod4.go | ||
rename1.go | ||
rename.go | ||
reorder2.go | ||
reorder.go | ||
retjmp.go | ||
return.go | ||
rotate0.go | ||
rotate1.go | ||
rotate2.go | ||
rotate3.go | ||
rotate.go | ||
run.go | ||
rune.go | ||
runtime.go | ||
shift1.go | ||
shift2.go | ||
sieve.go | ||
sigchld.go | ||
sigchld.out | ||
simassign.go | ||
sinit_run.go | ||
sinit.go | ||
sizeof.go | ||
slice3.go | ||
slice3err.go | ||
slicecap.go | ||
sliceopt.go | ||
solitaire.go | ||
stack.go | ||
stackobj2.go | ||
stackobj3.go | ||
stackobj.go | ||
strcopy.go | ||
strength.go | ||
string_lit.go | ||
stringrange.go | ||
struct0.go | ||
switch2.go | ||
switch3.go | ||
switch4.go | ||
switch5.go | ||
switch6.go | ||
switch7.go | ||
switch.go | ||
tinyfin.go | ||
torture.go | ||
turing.go | ||
typecheck.go | ||
typecheckloop.go | ||
typeswitch1.go | ||
typeswitch2.go | ||
typeswitch2b.go | ||
typeswitch3.go | ||
typeswitch.go | ||
uintptrescapes2.go | ||
uintptrescapes3.go | ||
uintptrescapes.go | ||
undef.go | ||
utf.go | ||
varerr.go | ||
varinit.go | ||
winbatch.go | ||
writebarrier.go | ||
zerodivide.go |
The test directory contains tests of the Go tool chain and runtime. It includes black box tests, regression tests, and error output tests. They are run as part of all.bash.
To run just these tests, execute:
../bin/go run run.go
Standard library tests should be written as regular Go tests in the appropriate package.
The tool chain and runtime also have regular Go tests in their packages. The main reasons to add a new test to this directory are:
- it is most naturally expressed using the test runner; or
- it is also applicable to
gccgo
and other Go tool chains.