1
0
mirror of https://github.com/golang/go synced 2024-09-29 14:14:29 -06:00

bytes, internal/bytealg: simplify Equal

The compiler has advanced enough that it is cheaper
to convert to strings than to go through the assembly
trampolines to call runtime.memequal.

Simplify Equal accordingly, and cull dead code from bytealg.

While we're here, simplify Equal's documentation.

Fixes #31587

Change-Id: Ie721d33f9a6cbd86b1d873398b20e7882c2c63e9
Reviewed-on: https://go-review.googlesource.com/c/go/+/173323
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Dave Cheney <dave@cheney.net>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
Josh Bleecher Snyder 2019-04-22 14:39:55 -07:00
parent de050717f1
commit ca0c449a6b
16 changed files with 31 additions and 233 deletions

View File

@ -12,23 +12,12 @@ import (
"unicode/utf8"
)
// Equal returns a boolean reporting whether a and b
// Equal reports whether a and b
// are the same length and contain the same bytes.
// A nil argument is equivalent to an empty slice.
func Equal(a, b []byte) bool {
return bytealg.Equal(a, b)
}
func equalPortable(a, b []byte) bool {
if len(a) != len(b) {
return false
}
for i, c := range a {
if c != b[i] {
return false
}
}
return true
// Neither cmd/compile nor gccgo allocates for these string conversions.
return string(a) == string(b)
}
// Compare returns an integer comparing two byte slices lexicographically.

View File

@ -51,15 +51,17 @@ type BinOpTest struct {
}
func TestEqual(t *testing.T) {
for _, tt := range compareTests {
eql := Equal(tt.a, tt.b)
if eql != (tt.i == 0) {
t.Errorf(`Equal(%q, %q) = %v`, tt.a, tt.b, eql)
}
eql = EqualPortable(tt.a, tt.b)
if eql != (tt.i == 0) {
t.Errorf(`EqualPortable(%q, %q) = %v`, tt.a, tt.b, eql)
// Run the tests and check for allocation at the same time.
allocs := testing.AllocsPerRun(10, func() {
for _, tt := range compareTests {
eql := Equal(tt.a, tt.b)
if eql != (tt.i == 0) {
t.Errorf(`Equal(%q, %q) = %v`, tt.a, tt.b, eql)
}
}
})
if allocs > 0 {
t.Errorf("Equal allocated %v times", allocs)
}
}
@ -572,11 +574,6 @@ func BenchmarkEqual(b *testing.B) {
benchBytes(b, sizes, bmEqual(Equal))
}
func BenchmarkEqualPort(b *testing.B) {
sizes := []int{1, 6, 32, 4 << 10, 4 << 20, 64 << 20}
benchBytes(b, sizes, bmEqual(EqualPortable))
}
func bmEqual(equal func([]byte, []byte) bool) func(b *testing.B, n int) {
return func(b *testing.B, n int) {
if len(bmbuf) < 2*n {

View File

@ -6,4 +6,3 @@ package bytes
// Export func for testing
var IndexBytePortable = indexBytePortable
var EqualPortable = equalPortable

View File

@ -12,4 +12,3 @@ runtime/tls_arm.s: [arm] load_g: function load_g missing Go declaration
runtime/tls_arm.s: [arm] _initcgo: function _initcgo missing Go declaration
runtime/internal/atomic/asm_arm.s: [arm] cas: function cas missing Go declaration
internal/bytealg/equal_arm.s: [arm] Equal: invalid MOVW of ret+24(FP); bool is 1-byte value

View File

@ -5,24 +5,6 @@
#include "go_asm.h"
#include "textflag.h"
TEXT ·Equal(SB),NOSPLIT,$0-25
MOVL a_len+4(FP), BX
MOVL b_len+16(FP), CX
CMPL BX, CX
JNE neq
MOVL a_base+0(FP), SI
MOVL b_base+12(FP), DI
CMPL SI, DI
JEQ eq
LEAL ret+24(FP), AX
JMP memeqbody<>(SB)
neq:
MOVB $0, ret+24(FP)
RET
eq:
MOVB $1, ret+24(FP)
RET
// memequal(a, b unsafe.Pointer, size uintptr) bool
TEXT runtime·memequal(SB),NOSPLIT,$0-13
MOVL a+0(FP), SI

View File

@ -5,24 +5,6 @@
#include "go_asm.h"
#include "textflag.h"
TEXT ·Equal(SB),NOSPLIT,$0-49
MOVQ a_len+8(FP), BX
MOVQ b_len+32(FP), CX
CMPQ BX, CX
JNE neq
MOVQ a_base+0(FP), SI
MOVQ b_base+24(FP), DI
CMPQ SI, DI
JEQ eq
LEAQ ret+48(FP), AX
JMP memeqbody<>(SB)
neq:
MOVB $0, ret+48(FP)
RET
eq:
MOVB $1, ret+48(FP)
RET
// memequal(a, b unsafe.Pointer, size uintptr) bool
TEXT runtime·memequal(SB),NOSPLIT,$0-25
MOVQ a+0(FP), SI

View File

@ -5,25 +5,6 @@
#include "go_asm.h"
#include "textflag.h"
TEXT ·Equal(SB),NOSPLIT,$0-25
MOVL a_len+4(FP), BX
MOVL b_len+16(FP), CX
CMPL BX, CX
JNE neq
MOVL a_base+0(FP), SI
MOVL b_base+12(FP), DI
CMPL SI, DI
JEQ eq
CALL memeqbody<>(SB)
MOVB AX, ret+24(FP)
RET
neq:
MOVB $0, ret+24(FP)
RET
eq:
MOVB $1, ret+24(FP)
RET
// memequal(a, b unsafe.Pointer, size uintptr) bool
TEXT runtime·memequal(SB),NOSPLIT,$0-17
MOVL a+0(FP), SI

View File

@ -5,27 +5,6 @@
#include "go_asm.h"
#include "textflag.h"
TEXT ·Equal(SB),NOSPLIT,$0-25
MOVW a_len+4(FP), R1
MOVW b_len+16(FP), R3
CMP R1, R3 // unequal lengths are not equal
B.NE notequal
CMP $0, R1 // short path to handle 0-byte case
B.EQ equal
MOVW a_base+0(FP), R0
MOVW b_base+12(FP), R2
MOVW $ret+24(FP), R7
B memeqbody<>(SB)
equal:
MOVW $1, R0
MOVB R0, ret+24(FP)
RET
notequal:
MOVW $0, R0
MOVBU R0, ret+24(FP)
RET
// memequal(a, b unsafe.Pointer, size uintptr) bool
TEXT runtime·memequal(SB),NOSPLIT|NOFRAME,$0-13
MOVW a+0(FP), R0

View File

@ -5,26 +5,6 @@
#include "go_asm.h"
#include "textflag.h"
TEXT ·Equal(SB),NOSPLIT,$0-49
MOVD a_len+8(FP), R1
MOVD b_len+32(FP), R3
CMP R1, R3
// unequal lengths are not equal
BNE not_equal
// short path to handle 0-byte case
CBZ R1, equal
MOVD a_base+0(FP), R0
MOVD b_base+24(FP), R2
MOVD $ret+48(FP), R8
B memeqbody<>(SB)
equal:
MOVD $1, R0
MOVB R0, ret+48(FP)
RET
not_equal:
MOVB ZR, ret+48(FP)
RET
// memequal(a, b unsafe.Pointer, size uintptr) bool
TEXT runtime·memequal(SB),NOSPLIT|NOFRAME,$0-25
MOVD size+16(FP), R1

View File

@ -0,0 +1,18 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package bytealg
// Equal reports whether a and b
// are the same length and contain the same bytes.
// A nil argument is equivalent to an empty slice.
//
// Equal is equivalent to bytes.Equal.
// It is provided here for convenience,
// because some packages cannot depend on bytes.
func Equal(a, b []byte) bool {
// Neither cmd/compile nor gccgo allocates for these string conversions.
// There is a test for this in package bytes.
return string(a) == string(b)
}

View File

@ -9,32 +9,6 @@
#define REGCTXT R22
TEXT ·Equal(SB),NOSPLIT,$0-49
MOVV a_len+8(FP), R3
MOVV b_len+32(FP), R4
BNE R3, R4, noteq // unequal lengths are not equal
MOVV a_base+0(FP), R1
MOVV b_base+24(FP), R2
ADDV R1, R3 // end
loop:
BEQ R1, R3, equal // reached the end
MOVBU (R1), R6
ADDV $1, R1
MOVBU (R2), R7
ADDV $1, R2
BEQ R6, R7, loop
noteq:
MOVB R0, ret+48(FP)
RET
equal:
MOVV $1, R1
MOVB R1, ret+48(FP)
RET
// memequal(a, b unsafe.Pointer, size uintptr) bool
TEXT runtime·memequal(SB),NOSPLIT|NOFRAME,$0-25
MOVV a+0(FP), R1

View File

@ -9,32 +9,6 @@
#define REGCTXT R22
TEXT ·Equal(SB),NOSPLIT,$0-25
MOVW a_len+4(FP), R3
MOVW b_len+16(FP), R4
BNE R3, R4, noteq // unequal lengths are not equal
MOVW a_base+0(FP), R1
MOVW b_base+12(FP), R2
ADDU R1, R3 // end
loop:
BEQ R1, R3, equal // reached the end
MOVBU (R1), R6
ADDU $1, R1
MOVBU (R2), R7
ADDU $1, R2
BEQ R6, R7, loop
noteq:
MOVB R0, ret+24(FP)
RET
equal:
MOVW $1, R1
MOVB R1, ret+24(FP)
RET
// memequal(a, b unsafe.Pointer, size uintptr) bool
TEXT runtime·memequal(SB),NOSPLIT,$0-13
MOVW a+0(FP), R1

View File

@ -6,11 +6,6 @@ package bytealg
import "unsafe"
// Note: there's no equal_generic.go because every platform must implement at least memequal_varlen in assembly.
//go:noescape
func Equal(a, b []byte) bool
// The declarations below generate ABI wrappers for functions
// implemented in assembly in this package but declared in another
// package.

View File

@ -7,25 +7,6 @@
#include "go_asm.h"
#include "textflag.h"
TEXT ·Equal(SB),NOSPLIT|NOFRAME,$0-49
MOVD a_len+8(FP), R4
MOVD b_len+32(FP), R5
CMP R5, R4 // unequal lengths are not equal
BNE noteq
MOVD a_base+0(FP), R3
MOVD b_base+24(FP), R4
MOVD $ret+48(FP), R10
BR memeqbody<>(SB)
noteq:
MOVBZ $0,ret+48(FP)
RET
equal:
MOVD $1,R3
MOVBZ R3,ret+48(FP)
RET
// memequal(a, b unsafe.Pointer, size uintptr) bool
TEXT runtime·memequal(SB),NOSPLIT|NOFRAME,$0-25
MOVD a+0(FP), R3

View File

@ -5,18 +5,6 @@
#include "go_asm.h"
#include "textflag.h"
TEXT ·Equal(SB),NOSPLIT|NOFRAME,$0-49
MOVD a_len+8(FP), R2
MOVD b_len+32(FP), R6
MOVD a_base+0(FP), R3
MOVD b_base+24(FP), R5
LA ret+48(FP), R7
CMPBNE R2, R6, notequal
BR memeqbody<>(SB)
notequal:
MOVB $0, ret+48(FP)
RET
// memequal(a, b unsafe.Pointer, size uintptr) bool
TEXT runtime·memequal(SB),NOSPLIT|NOFRAME,$0-25
MOVD a+0(FP), R3

View File

@ -5,26 +5,6 @@
#include "go_asm.h"
#include "textflag.h"
TEXT ·Equal(SB), NOSPLIT, $0-49
MOVD a_len+8(FP), R0
MOVD b_len+32(FP), R1
Get R0
Get R1
I64Eq
If
Get SP
I64Load a+0(FP)
I64Load b+24(FP)
Get R0
Call memeqbody<>(SB)
I64Store8 ret+48(FP)
Else
Get SP
I64Const $0
I64Store8 ret+48(FP)
End
RET
// memequal(p, q unsafe.Pointer, size uintptr) bool
TEXT runtime·memequal(SB), NOSPLIT, $0-25
Get SP