Skip to content

Commit

Permalink
Add SSE and AVX code path to decoder.skipWhitespaces()
Browse files Browse the repository at this point in the history
  • Loading branch information
kaiburjack committed Aug 16, 2022
1 parent d1c9b03 commit 4affcda
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 35 deletions.
20 changes: 6 additions & 14 deletions combined_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -420,8 +420,8 @@ func TestPreserveWhitespace(t *testing.T) {
func TestInsignificantWhitespace(t *testing.T) {
// given
input := `
<?xml version="1.0" encoding="utf-8" ?>
<a xml:space = "preserve" >
<?xml version = "1.0" encoding = "utf-8" ?>
<a xml:space = "preserve" >
</a >`
dec := gosaxml.NewDecoder(strings.NewReader(input))
w := &bytes.Buffer{}
Expand All @@ -432,7 +432,7 @@ func TestInsignificantWhitespace(t *testing.T) {
decodeEncode(t, dec, enc, &tk)

// then
assert.Equal(t, "<?xml version=\"1.0\" encoding=\"utf-8\"?>"+
assert.Equal(t, "<?xml version = \"1.0\" encoding = \"utf-8\"?>"+
"<a xml:space=\"preserve\">\n</a>", w.String())
}

Expand Down Expand Up @@ -462,18 +462,10 @@ func BenchmarkLotsOfText(b *testing.B) {
}
}

func BenchmarkWithSkippedWhitespace(b *testing.B) {
func BenchmarkWithWhitespaceInAttributes(b *testing.B) {
r := strings.NewReader(
`
<soap:Envelope
xmlns:soap="http://www.w3.org/2003/05/soap-envelope/"
soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
<soap:Body>
<m:GetPrice xmlns:m="https://www.w3schools.com/prices">
<!-- we want to add a <m:Item>Apples</m:Item> here -->
</m:GetPrice>
</soap:Body>
</soap:Envelope>`)
`<a a = "test" >
</a >`)
dec := gosaxml.NewDecoder(r)
enc := gosaxml.NewEncoder(io.Discard, gosaxml.NewNamespaceModifier())
var tk gosaxml.Token
Expand Down
70 changes: 68 additions & 2 deletions decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,72 @@ func (thiz *decoder) Reset(r io.Reader) {
}

func (thiz *decoder) skipWhitespaces(b byte) (byte, error) {
if canUseAVX2 {
return thiz.skipWhitespacesAVX2(b)
} else if canUseSSE {
return thiz.skipWhitespacesSSE(b)
}
return thiz.skipWhitespacesGeneric(b)
}

func (thiz *decoder) skipWhitespacesAVX2(b byte) (byte, error) {
if !isWhitespace(b) {
return b, nil
}
for {
for thiz.w > thiz.r {
sidx := int(onlySpaces32(thiz.rb[thiz.r:thiz.w]))
_, err := thiz.discard(sidx)
if err != nil {
return 0, err
}
if sidx != 32 {
newB, err := thiz.readByte()
if err != nil {
return 0, err
}
return newB, nil
}
}
thiz.discardBuffer()
err := thiz.read0()
if err != nil {
return 0, err
}
}
}

func (thiz *decoder) skipWhitespacesSSE(b byte) (byte, error) {
if !isWhitespace(b) {
return b, nil
}
for {
j := thiz.r
c := 0
for thiz.w > thiz.r+c {
sidx := onlySpaces32(thiz.rb[j+c : thiz.w])
c += int(sidx)
if sidx != 16 {
_, err := thiz.discard(c)
if err != nil {
return 0, err
}
newB, err := thiz.readByte()
if err != nil {
return 0, err
}
return newB, nil
}
}
thiz.discardBuffer()
err := thiz.read0()
if err != nil {
return 0, err
}
}
}

func (thiz *decoder) skipWhitespacesGeneric(b byte) (byte, error) {
for {
if !isWhitespace(b) {
return b, nil
Expand Down Expand Up @@ -355,7 +421,7 @@ func (thiz *decoder) decodeTextSSE(t *Token) (bool, error) {
for thiz.w > thiz.r+c {
sidx := findFirstOpenAngleBracket16(thiz.rb[j+c : thiz.w])
onlyWhitespaces = onlyWhitespaces && onlySpacesUntil16(thiz.rb[j+c:thiz.w], sidx)
c += sidx
c += int(sidx)
if sidx != 16 {
_, err := thiz.discard(c)
if err != nil {
Expand Down Expand Up @@ -388,7 +454,7 @@ func (thiz *decoder) decodeTextAVX2(t *Token) (bool, error) {
for thiz.w > thiz.r+c {
sidx := findFirstOpenAngleBracket32(thiz.rb[j+c : thiz.w])
onlyWhitespaces = onlyWhitespaces && onlySpacesUntil32(thiz.rb[j+c:thiz.w], sidx)
c += sidx
c += int(sidx)
if sidx != 32 {
_, err := thiz.discard(c)
if err != nil {
Expand Down
22 changes: 11 additions & 11 deletions sse_amd64.go
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
package gosaxml

//go:noescape
func openAngleBracket16([]uint8) uint16
func openAngleBracket16([]uint8) byte

func findFirstOpenAngleBracket16(slice []uint8) int {
return int(openAngleBracket16(slice))
func findFirstOpenAngleBracket16(slice []uint8) byte {
return openAngleBracket16(slice)
}

//go:noescape
func onlySpaces16([]uint8) uint16
func onlySpaces16([]uint8) byte

func onlySpacesUntil16(slice []uint8, n int) bool {
return onlySpaces16(slice)<<(16-n) == 0
func onlySpacesUntil16(slice []uint8, n byte) bool {
return onlySpaces16(slice) >= n
}

//go:noescape
func openAngleBracket32([]uint8) int
func openAngleBracket32([]uint8) byte

func findFirstOpenAngleBracket32(slice []uint8) int {
func findFirstOpenAngleBracket32(slice []uint8) byte {
return openAngleBracket32(slice)
}

//go:noescape
func onlySpaces32([]uint8) uint32
func onlySpaces32([]uint8) byte

func onlySpacesUntil32(slice []uint8, n int) bool {
return onlySpaces32(slice)<<(32-n) == 0
func onlySpacesUntil32(slice []uint8, n byte) bool {
return onlySpaces32(slice) >= n
}
10 changes: 6 additions & 4 deletions sse_amd64.s
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ TEXT ·openAngleBracket16(SB),NOSPLIT, $0
PCMPEQB ·oab<>(SB), X0
PMOVMSKB X0, AX
TZCNTW AX, AX
MOVW AX, ret+24(FP)
MOVB AX, ret+24(FP)
RET

TEXT ·openAngleBracket32(SB),NOSPLIT, $0
Expand All @@ -27,7 +27,7 @@ TEXT ·openAngleBracket32(SB),NOSPLIT, $0
VPCMPEQB ·oab<>(SB), Y0, Y0
VPMOVMSKB Y0, AX
TZCNTL AX, AX
MOVQ AX, ret+24(FP)
MOVB AX, ret+24(FP)
VZEROUPPER // <- https://i.stack.imgur.com/dGpbi.png
RET

Expand All @@ -40,7 +40,8 @@ TEXT ·onlySpaces16(SB),NOSPLIT, $0
PCMPGTB X1, X2
POR X2, X0
PMOVMSKB X0, AX
MOVW AX, ret+24(FP)
TZCNTW AX, AX
MOVB AX, ret+24(FP)
RET

TEXT ·onlySpaces32(SB),NOSPLIT, $0
Expand All @@ -52,6 +53,7 @@ TEXT ·onlySpaces32(SB),NOSPLIT, $0
VPCMPGTB Y1, Y2, Y2
VPOR Y2, Y0, Y0
VPMOVMSKB Y0, AX
MOVL AX, ret+24(FP)
TZCNTL AX, AX
MOVB AX, ret+24(FP)
VZEROUPPER // <- https://i.stack.imgur.com/dGpbi.png
RET
10 changes: 6 additions & 4 deletions sse_amd64_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@ import (
)

func TestFindFirstOpenAngleBracket16(t *testing.T) {
assert.Equal(t, 3, findFirstOpenAngleBracket16([]uint8{0x20, 0x20, 0x20, 0x3C, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}))
assert.Equal(t, 16, findFirstOpenAngleBracket16([]uint8{0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}))
assert.Equal(t, byte(3), findFirstOpenAngleBracket16([]uint8{0x20, 0x20, 0x20, 0x3C, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}))
assert.Equal(t, byte(16), findFirstOpenAngleBracket16([]uint8{0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}))
}

func TestOnlySpaces16(t *testing.T) {
assert.Equal(t, uint16(0x18), onlySpaces16([]uint8{0x20, 0x20, 0x20, 0xC2, 0xA7, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}))
assert.Equal(t, byte(3), onlySpaces16([]uint8{0x20, 0x20, 0x20, 0xC2, 0xA7, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}))
assert.Equal(t, byte(16), onlySpaces16([]uint8{0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}))
}

func TestOnlySpaces32(t *testing.T) {
assert.Equal(t, uint32(0x18), onlySpaces32([]uint8{0x20, 0x20, 0x20, 0xC2, 0xA7, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}))
assert.Equal(t, byte(3), onlySpaces32([]uint8{0x20, 0x20, 0x20, 0xC2, 0xA7, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}))
assert.Equal(t, byte(32), onlySpaces32([]uint8{0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}))
}

func TestOnlySpacesUntil16(t *testing.T) {
Expand Down

0 comments on commit 4affcda

Please sign in to comment.