mirror of https://github.com/gorilla/websocket.git
Improve mask performance
This commit is contained in:
parent
5df680c89f
commit
77f110791c
|
@ -1,19 +0,0 @@
|
||||||
// Copyright 2014 The Gorilla WebSocket 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 websocket
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func BenchmarkMaskBytes(b *testing.B) {
|
|
||||||
var key [4]byte
|
|
||||||
data := make([]byte, 1024)
|
|
||||||
pos := 0
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
pos = maskBytes(key, pos, data)
|
|
||||||
}
|
|
||||||
b.SetBytes(int64(len(data)))
|
|
||||||
}
|
|
15
conn.go
15
conn.go
|
@ -10,7 +10,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/rand"
|
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
@ -218,20 +217,6 @@ func isValidReceivedCloseCode(code int) bool {
|
||||||
return validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999)
|
return validReceivedCloseCodes[code] || (code >= 3000 && code <= 4999)
|
||||||
}
|
}
|
||||||
|
|
||||||
func maskBytes(key [4]byte, pos int, b []byte) int {
|
|
||||||
for i := range b {
|
|
||||||
b[i] ^= key[pos&3]
|
|
||||||
pos++
|
|
||||||
}
|
|
||||||
return pos & 3
|
|
||||||
}
|
|
||||||
|
|
||||||
func newMaskKey() [4]byte {
|
|
||||||
n := rand.Uint32()
|
|
||||||
return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Conn represents a WebSocket connection.
|
|
||||||
type Conn struct {
|
type Conn struct {
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
isServer bool
|
isServer bool
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
// Copyright 2016 The Gorilla WebSocket 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 websocket
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const wordSize = int(unsafe.Sizeof(uintptr(0)))
|
||||||
|
|
||||||
|
func newMaskKey() [4]byte {
|
||||||
|
n := rand.Uint32()
|
||||||
|
return [4]byte{byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func maskBytes(key [4]byte, pos int, b []byte) int {
|
||||||
|
|
||||||
|
// Mask one byte at a time for small buffers.
|
||||||
|
if len(b) < 2*wordSize {
|
||||||
|
for i := range b {
|
||||||
|
b[i] ^= key[pos&3]
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
return pos & 3
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mask one byte at a time to word boundary.
|
||||||
|
if n := int(uintptr(unsafe.Pointer(&b))) % wordSize; n != 0 {
|
||||||
|
n = wordSize - n
|
||||||
|
for i := range b[:n] {
|
||||||
|
b[i] ^= key[pos&3]
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
b = b[n:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create aligned word size key.
|
||||||
|
var k [wordSize]byte
|
||||||
|
for i := range k {
|
||||||
|
k[i] = key[(pos+i)&3]
|
||||||
|
}
|
||||||
|
kw := *(*uintptr)(unsafe.Pointer(&k))
|
||||||
|
|
||||||
|
// Mask one word at a time.
|
||||||
|
n := (len(b) / wordSize) * wordSize
|
||||||
|
for i := 0; i < n; i += wordSize {
|
||||||
|
*(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mask one byte at a time for remaining bytes.
|
||||||
|
b = b[n:]
|
||||||
|
for i := range b {
|
||||||
|
b[i] ^= key[pos&3]
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos & 3
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of
|
||||||
|
// this source code is governed by a BSD-style license that can be found in the
|
||||||
|
// LICENSE file.
|
||||||
|
|
||||||
|
// Require 1.7 for sub-bencmarks
|
||||||
|
// +build go1.7
|
||||||
|
|
||||||
|
package websocket
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func maskBytesByByte(key [4]byte, pos int, b []byte) int {
|
||||||
|
for i := range b {
|
||||||
|
b[i] ^= key[pos&3]
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
return pos & 3
|
||||||
|
}
|
||||||
|
|
||||||
|
func notzero(b []byte) int {
|
||||||
|
for i := range b {
|
||||||
|
if b[i] != 0 {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMaskBytes(t *testing.T) {
|
||||||
|
key := [4]byte{1, 2, 3, 4}
|
||||||
|
for size := 1; size <= 1024; size++ {
|
||||||
|
for align := 0; align < wordSize; align++ {
|
||||||
|
for pos := 0; pos < 4; pos++ {
|
||||||
|
b := make([]byte, size+align)[align:]
|
||||||
|
maskBytes(key, pos, b)
|
||||||
|
maskBytesByByte(key, pos, b)
|
||||||
|
if i := notzero(b); i >= 0 {
|
||||||
|
t.Errorf("size:%d, align:%d, pos:%d, offset:%d", size, align, pos, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkMaskBytes(b *testing.B) {
|
||||||
|
for _, size := range []int{2, 4, 8, 16, 32, 512, 1024} {
|
||||||
|
b.Run(fmt.Sprintf("size-%d", size), func(b *testing.B) {
|
||||||
|
for _, align := range []int{wordSize / 2} {
|
||||||
|
b.Run(fmt.Sprintf("align-%d", align), func(b *testing.B) {
|
||||||
|
for _, fn := range []struct {
|
||||||
|
name string
|
||||||
|
fn func(key [4]byte, pos int, b []byte) int
|
||||||
|
}{
|
||||||
|
{"byte", maskBytesByByte},
|
||||||
|
{"word", maskBytes},
|
||||||
|
} {
|
||||||
|
b.Run(fn.name, func(b *testing.B) {
|
||||||
|
key := newMaskKey()
|
||||||
|
data := make([]byte, size+align)[align:]
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
fn.fn(key, 0, data)
|
||||||
|
}
|
||||||
|
b.SetBytes(int64(len(data)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue