mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
reflectdata: unroll a loop in array equal function generation
As josharian mentioned, a compare function could benefit from unrolling a loop for arrays. This commit introduces such functionality. name old time/op new time/op delta EqArrayOfStrings5-12 12.5ns ± 1% 8.4ns ± 1% -33.05% (p=0.008 n=5+5) EqArrayOfStrings64-12 71.7ns ± 1% 64.1ns ± 1% -10.57% (p=0.008 n=5+5) EqArrayOfStrings1024-12 1.12µs ± 1% 1.01µs ± 0% -9.77% (p=0.008 n=5+5) [Geo mean] 100ns 81ns -18.56% name old time/op new time/op delta EqArrayOfFloats5-12 4.50ns ± 2% 3.32ns ± 1% -26.09% (p=0.008 n=5+5) EqArrayOfFloats64-12 41.3ns ± 1% 35.7ns ± 0% -13.63% (p=0.016 n=5+4) EqArrayOfFloats1024-12 619ns ± 1% 557ns ± 1% -9.95% (p=0.008 n=5+5) [Geo mean] 48.6ns 40.4ns -16.85% Change-Id: If1b69c5cf3fb246bb0275a292118b0b93ad9c9a8 Reviewed-on: https://go-review.googlesource.com/c/go/+/368614 Reviewed-by: Keith Randall <khr@golang.org> Trust: Emmanuel Odeke <emmanuel@orijtech.com> Run-TryBot: Emmanuel Odeke <emmanuel@orijtech.com> TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
parent
5210a71285
commit
1a955bcdb7
2 changed files with 136 additions and 34 deletions
|
|
@ -412,22 +412,25 @@ func geneq(t *types.Type) *obj.LSym {
|
|||
//
|
||||
// if eq(p[0], q[0]) && eq(p[1], q[1]) && ... {
|
||||
// } else {
|
||||
// return
|
||||
// goto neq
|
||||
// }
|
||||
//
|
||||
// And so on.
|
||||
//
|
||||
// Otherwise it generates:
|
||||
//
|
||||
// for i := 0; i < nelem; i++ {
|
||||
// if eq(p[i], q[i]) {
|
||||
// iterateTo := nelem/unroll*unroll
|
||||
// for i := 0; i < iterateTo; i += unroll {
|
||||
// if eq(p[i+0], q[i+0]) && eq(p[i+1], q[i+1]) && ... && eq(p[i+unroll-1], q[i+unroll-1]) {
|
||||
// } else {
|
||||
// goto neq
|
||||
// }
|
||||
// }
|
||||
// if eq(p[iterateTo+0], q[iterateTo+0]) && eq(p[iterateTo+1], q[iterateTo+1]) && ... {
|
||||
// } else {
|
||||
// goto neq
|
||||
// }
|
||||
//
|
||||
// TODO(josharian): consider doing some loop unrolling
|
||||
// for larger nelem as well, processing a few elements at a time in a loop.
|
||||
checkAll := func(unroll int64, last bool, eq func(pi, qi ir.Node) ir.Node) {
|
||||
// checkIdx generates a node to check for equality at index i.
|
||||
checkIdx := func(i ir.Node) ir.Node {
|
||||
|
|
@ -442,38 +445,62 @@ func geneq(t *types.Type) *obj.LSym {
|
|||
return eq(pi, qi)
|
||||
}
|
||||
|
||||
if nelem <= unroll {
|
||||
if last {
|
||||
// Do last comparison in a different manner.
|
||||
nelem--
|
||||
}
|
||||
// Generate a series of checks.
|
||||
for i := int64(0); i < nelem; i++ {
|
||||
// if check {} else { goto neq }
|
||||
nif := ir.NewIfStmt(base.Pos, checkIdx(ir.NewInt(i)), nil, nil)
|
||||
nif.Else.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, neq))
|
||||
fn.Body.Append(nif)
|
||||
}
|
||||
if last {
|
||||
fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, checkIdx(ir.NewInt(nelem))))
|
||||
}
|
||||
} else {
|
||||
// Generate a for loop.
|
||||
// for i := 0; i < nelem; i++
|
||||
iterations := nelem / unroll
|
||||
iterateTo := iterations * unroll
|
||||
// If a loop is iterated only once, there shouldn't be any loop at all.
|
||||
if iterations == 1 {
|
||||
iterateTo = 0
|
||||
}
|
||||
|
||||
if iterateTo > 0 {
|
||||
// Generate an unrolled for loop.
|
||||
// for i := 0; i < nelem/unroll*unroll; i += unroll
|
||||
i := typecheck.Temp(types.Types[types.TINT])
|
||||
init := ir.NewAssignStmt(base.Pos, i, ir.NewInt(0))
|
||||
cond := ir.NewBinaryExpr(base.Pos, ir.OLT, i, ir.NewInt(nelem))
|
||||
post := ir.NewAssignStmt(base.Pos, i, ir.NewBinaryExpr(base.Pos, ir.OADD, i, ir.NewInt(1)))
|
||||
loop := ir.NewForStmt(base.Pos, nil, cond, post, nil)
|
||||
cond := ir.NewBinaryExpr(base.Pos, ir.OLT, i, ir.NewInt(iterateTo))
|
||||
loop := ir.NewForStmt(base.Pos, nil, cond, nil, nil)
|
||||
loop.PtrInit().Append(init)
|
||||
// if eq(pi, qi) {} else { goto neq }
|
||||
nif := ir.NewIfStmt(base.Pos, checkIdx(i), nil, nil)
|
||||
nif.Else.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, neq))
|
||||
loop.Body.Append(nif)
|
||||
fn.Body.Append(loop)
|
||||
if last {
|
||||
fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, ir.NewBool(true)))
|
||||
|
||||
// if eq(p[i+0], q[i+0]) && eq(p[i+1], q[i+1]) && ... && eq(p[i+unroll-1], q[i+unroll-1]) {
|
||||
// } else {
|
||||
// goto neq
|
||||
// }
|
||||
for j := int64(0); j < unroll; j++ {
|
||||
// if check {} else { goto neq }
|
||||
nif := ir.NewIfStmt(base.Pos, checkIdx(i), nil, nil)
|
||||
nif.Else.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, neq))
|
||||
loop.Body.Append(nif)
|
||||
post := ir.NewAssignStmt(base.Pos, i, ir.NewBinaryExpr(base.Pos, ir.OADD, i, ir.NewInt(1)))
|
||||
loop.Body.Append(post)
|
||||
}
|
||||
|
||||
fn.Body.Append(loop)
|
||||
|
||||
if nelem == iterateTo {
|
||||
if last {
|
||||
fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, ir.NewBool(true)))
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Generate remaining checks, if nelem is not a multiple of unroll.
|
||||
if last {
|
||||
// Do last comparison in a different manner.
|
||||
nelem--
|
||||
}
|
||||
// if eq(p[iterateTo+0], q[iterateTo+0]) && eq(p[iterateTo+1], q[iterateTo+1]) && ... {
|
||||
// } else {
|
||||
// goto neq
|
||||
// }
|
||||
for j := iterateTo; j < nelem; j++ {
|
||||
// if check {} else { goto neq }
|
||||
nif := ir.NewIfStmt(base.Pos, checkIdx(ir.NewInt(j)), nil, nil)
|
||||
nif.Else.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, neq))
|
||||
fn.Body.Append(nif)
|
||||
}
|
||||
if last {
|
||||
fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, checkIdx(ir.NewInt(nelem))))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -481,7 +508,6 @@ func geneq(t *types.Type) *obj.LSym {
|
|||
case types.TSTRING:
|
||||
// Do two loops. First, check that all the lengths match (cheap).
|
||||
// Second, check that all the contents match (expensive).
|
||||
// TODO: when the array size is small, unroll the length match checks.
|
||||
checkAll(3, false, func(pi, qi ir.Node) ir.Node {
|
||||
// Compare lengths.
|
||||
eqlen, _ := EqString(pi, qi)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue