Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(frame): reduce bound checks #143

Merged
merged 2 commits into from
Aug 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
CHANGELOG
=========

v3.2.0 (-.08.2021)
-------------------

## 🚀 New:

- ✏️ 50% reduce bound checks in the frame's operations. [PR](https://github.com/spiral/goridge/pull/143)

## 🔨 BC:

- 💔 Frame now for the internal (but public) operations receive additional `[]byte` slice to skip bound checks.

---

v3.1.4 (14.06.2021)
-------------------

Expand Down
2 changes: 1 addition & 1 deletion internal/receive.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func ReceiveFrame(relay io.Reader, fr *frame.Frame) error {
}

// verify header CRC
if !header.VerifyCRC() {
if !header.VerifyCRC(header.Header()) {
return errors.E(op, errors.Str("CRC verification failed"))
}

Expand Down
63 changes: 31 additions & 32 deletions pkg/frame/frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func ReadHeader(data []byte) *Frame {
// first 12 bytes will be a header
// the rest - payload
func ReadFrame(data []byte) *Frame {
_ = data[0]
_ = data[11]
opt := data[0] & 0x0F
// if more than 3, that we have options
if opt > 3 {
Expand All @@ -45,8 +45,8 @@ func ReadFrame(data []byte) *Frame {
header: data[:12],
payload: data[12:],
}
f.header[10] = 0
f.header[11] = 0

f.header[10], f.header[11] = 0, 0

return f
}
Expand Down Expand Up @@ -98,7 +98,6 @@ func (f *Frame) WriteVersion(version Version) {
// To erase, we applying bitwise AND to the upper 4 bits and returning result
//go:inline
func (f *Frame) ReadHL() byte {
_ = f.header[0]
// 0101_1111 0000_1111
return f.header[0] & 0x0F
}
Expand All @@ -107,13 +106,11 @@ func (f *Frame) ReadHL() byte {
// we can easily apply bitwise OR and set lower 4 bits to needed hl value
//go:inline
func (f *Frame) writeHl(hl byte) {
_ = f.header[0]
f.header[0] |= hl
}

//go:inline
func (f *Frame) incrementHL() {
_ = f.header[0]
hl := f.ReadHL()
if hl > 15 {
panic("header len should be less than 15")
Expand All @@ -123,20 +120,17 @@ func (f *Frame) incrementHL() {

//go:inline
func (f *Frame) defaultHL() {
_ = f.header[0]
f.writeHl(3)
}

// ReadFlags ..
// Flags is full 1st byte
//go:inline
func (f *Frame) ReadFlags() byte {
_ = f.header[1]
return f.header[1]
}

func (f *Frame) WriteFlags(flags ...byte) {
_ = f.header[1]
for i := 0; i < len(flags); i++ {
f.header[1] |= flags[i]
}
Expand Down Expand Up @@ -176,34 +170,39 @@ func (f *Frame) AppendOptions(opts []byte) {
f.header = append(f.header, opts...)
}

// last byte after main header and first options byte
const lb = 12

// ReadOptions ...
// f.readHL() - 2 needed to know actual options size
// we know, that 2 WORDS is minimal header len
// extra WORDS will add extra 32bits to the options (4 bytes)
func (f *Frame) ReadOptions() []uint32 {
func (f *Frame) ReadOptions(header []byte) []uint32 {
// we can read options, if there are no options
if f.ReadHL() <= 3 {
return nil
}

// last byte after main header and first options byte
const lb = 12

// Get the options len
optionLen := f.ReadHL() - 3 // 3 is the default
// slice in place
options := make([]uint32, 0, optionLen)
options := make([]uint32, optionLen)

// Options starting from 8-th byte
// we should scan with 4 byte window (32bit, WORD)
for i := byte(0); i < optionLen*WORD; i += WORD {
for i, j := byte(0), 0; i < optionLen*WORD; i, j = i+WORD, j+1 {
// for example
// 8 12 16
// 9 13 17
// 10 14 18
// 11 15 19
// For this data, HL will be 3, optionLen will be 12 (3*4) bytes
options = append(options, uint32(f.header[lb+i])|uint32(f.header[lb+i+1])<<8|uint32(f.header[lb+i+2])<<16|uint32(f.header[lb+i+3])<<24)
options[j] |= uint32(header[lb+i])
options[j] |= uint32(header[lb+i+1]) << 8
options[j] |= uint32(header[lb+i+2]) << 16
options[j] |= uint32(header[lb+i+3]) << 24
}

return options
}

Expand All @@ -221,35 +220,35 @@ func (f *Frame) ReadPayloadLen() uint32 {
// LE format used to write Payload
// Using 4 bytes (2,3,4,5 bytes in the header)
//go:inline
func (f *Frame) WritePayloadLen(len uint32) {
_ = f.header[5]
f.header[2] = byte(len)
f.header[3] = byte(len >> 8)
f.header[4] = byte(len >> 16)
f.header[5] = byte(len >> 24)
func (f *Frame) WritePayloadLen(data []byte, len uint32) {
_ = data[5]
data[2] = byte(len)
data[3] = byte(len >> 8)
data[4] = byte(len >> 16)
data[5] = byte(len >> 24)
}

// WriteCRC ..
// Calculating CRC and writing it to the 6th byte (7th reserved)
func (f *Frame) WriteCRC() {
func (f *Frame) WriteCRC(header []byte) {
// 6 7 8 9 bytes
// 10, 11 reserved
_ = f.header[9]
_ = header[9]

crc := crc32.ChecksumIEEE(f.header[:6])
f.header[6] = byte(crc)
f.header[7] = byte(crc >> 8)
f.header[8] = byte(crc >> 16)
f.header[9] = byte(crc >> 24)
crc := crc32.ChecksumIEEE(header[:6])
header[6] = byte(crc)
header[7] = byte(crc >> 8)
header[8] = byte(crc >> 16)
header[9] = byte(crc >> 24)
}

// VerifyCRC ..
// Reading info from 6th byte and verifying it with calculated in-place. Should be equal.
// If not - drop the frame as incorrect.
func (f *Frame) VerifyCRC() bool {
_ = f.header[9]
func (f *Frame) VerifyCRC(header []byte) bool {
_ = header[9]

return crc32.ChecksumIEEE(f.header[:6]) == uint32(f.header[6])|uint32(f.header[7])<<8|uint32(f.header[8])<<16|uint32(f.header[9])<<24
return crc32.ChecksumIEEE(header[:6]) == uint32(header[6])|uint32(header[7])<<8|uint32(header[8])<<16|uint32(header[9])<<24
}

// Bytes returns header with payload
Expand Down
61 changes: 40 additions & 21 deletions pkg/frame/frame_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ func TestNewFrame(t *testing.T) {
nf := NewFrame()
nf.WriteVersion(VERSION_1)
nf.WriteFlags(CONTROL)
nf.WritePayloadLen(uint32(len([]byte(TestPayload))))
nf.WriteCRC()
nf.WritePayloadLen(nf.Header(), uint32(len([]byte(TestPayload))))
nf.WriteCRC(nf.header)

nf.WritePayload([]byte(TestPayload))

Expand All @@ -25,16 +25,17 @@ func TestNewFrame(t *testing.T) {
assert.Equal(t, rf.ReadVersion(), nf.ReadVersion())
assert.Equal(t, rf.ReadFlags(), nf.ReadFlags())
assert.Equal(t, rf.ReadPayloadLen(), nf.ReadPayloadLen())
assert.Equal(t, true, rf.VerifyCRC())
assert.Equal(t, true, rf.VerifyCRC(rf.Header()))
assert.Equal(t, []uint32(nil), rf.ReadOptions(rf.Header()))
}

func TestFrame_VerifyCRC_Fail(t *testing.T) {
nf := NewFrame()
// this is the wrong position
nf.WriteCRC()
nf.WriteCRC(nf.Header())
nf.WriteVersion(VERSION_1)
nf.WriteFlags(CONTROL)
nf.WritePayloadLen(uint32(len([]byte(TestPayload))))
nf.WritePayloadLen(nf.Header(), uint32(len([]byte(TestPayload))))

nf.WritePayload([]byte(TestPayload))

Expand All @@ -45,53 +46,53 @@ func TestFrame_VerifyCRC_Fail(t *testing.T) {
assert.Equal(t, rf.ReadVersion(), nf.ReadVersion())
assert.Equal(t, rf.ReadFlags(), nf.ReadFlags())
assert.Equal(t, rf.ReadPayloadLen(), nf.ReadPayloadLen())
assert.Equal(t, false, rf.VerifyCRC())
assert.Equal(t, false, rf.VerifyCRC(rf.Header()))
}

func TestFrame_Options(t *testing.T) {
nf := NewFrame()
nf.WriteVersion(1)
nf.WriteFlags(CONTROL, CODEC_GOB)
nf.WritePayloadLen(uint32(len([]byte(TestPayload))))
nf.WriteOptions(323423432)
nf.WritePayloadLen(nf.Header(), uint32(len([]byte(TestPayload))))
nf.WriteOptions(323423432, 1213231)

// test options
options := nf.ReadOptions()
assert.Equal(t, []uint32{323423432}, options)
options := nf.ReadOptions(nf.Header())
assert.Equal(t, []uint32{323423432, 1213231}, options)
// write payload
nf.WritePayload([]byte(TestPayload))
nf.WriteCRC()
nf.WriteCRC(nf.Header())
data := nf.Bytes()

rf := ReadFrame(data)

assert.Equal(t, rf.ReadVersion(), nf.ReadVersion())
assert.Equal(t, rf.ReadFlags(), nf.ReadFlags())
assert.Equal(t, rf.ReadPayloadLen(), nf.ReadPayloadLen())
assert.Equal(t, rf.VerifyCRC(), true)
assert.Equal(t, rf.VerifyCRC(rf.Header()), true)
}

func TestFrame_Bytes(t *testing.T) {
nf := NewFrame()
nf.WriteVersion(1)
nf.WriteFlags(CONTROL, CODEC_GOB)
nf.WritePayloadLen(uint32(len([]byte(TestPayload))))
nf.WritePayloadLen(nf.Header(), uint32(len([]byte(TestPayload))))

nf.WriteOptions(323423432)
assert.Equal(t, []uint32{323423432}, nf.ReadOptions())
assert.Equal(t, []uint32{323423432}, nf.ReadOptions(nf.Header()))
nf.WritePayload([]byte(TestPayload))

nf.WriteCRC()
assert.Equal(t, true, nf.VerifyCRC())
nf.WriteCRC(nf.Header())
assert.Equal(t, true, nf.VerifyCRC(nf.Header()))
data := nf.Bytes()

rf := ReadFrame(data)

assert.Equal(t, rf.ReadVersion(), nf.ReadVersion())
assert.Equal(t, rf.ReadFlags(), nf.ReadFlags())
assert.Equal(t, rf.ReadPayloadLen(), nf.ReadPayloadLen())
assert.Equal(t, true, rf.VerifyCRC())
assert.Equal(t, []uint32{323423432}, rf.ReadOptions())
assert.Equal(t, true, rf.VerifyCRC(rf.Header()))
assert.Equal(t, []uint32{323423432}, rf.ReadOptions(rf.Header()))
}

func BenchmarkCRC32(b *testing.B) {
Expand All @@ -107,15 +108,33 @@ func BenchmarkFrame_CRC(b *testing.B) {
nf := NewFrame()
nf.WriteVersion(VERSION_1)
nf.WriteFlags(CONTROL, CODEC_GOB)
nf.WritePayloadLen(uint32(len([]byte(TestPayload))))
nf.WritePayloadLen(nf.Header(), uint32(len([]byte(TestPayload))))
nf.WriteOptions(1000, 1000, 1000, 1000, 1000, 1000)

b.ResetTimer()
b.ReportAllocs()

for i := 0; i < b.N; i++ {
nf.WriteCRC()
if !nf.VerifyCRC() {
nf.WriteCRC(nf.Header())
if !nf.VerifyCRC(nf.Header()) {
panic("CRC")
}
}
}

func BenchmarkFrame(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()

for i := 0; i < b.N; i++ {
nf := NewFrame()
nf.WriteVersion(VERSION_1)
nf.WriteFlags(CONTROL, CODEC_GOB)
nf.WritePayloadLen(nf.Header(), uint32(len([]byte(TestPayload))))
nf.WriteOptions(1000, 1000, 1000, 1000, 1000, 1000)
nf.WriteCRC(nf.Header())

if !nf.VerifyCRC(nf.Header()) {
panic("CRC")
}
}
Expand Down
22 changes: 11 additions & 11 deletions pkg/pipe/pipe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ func TestPipeReceive(t *testing.T) {
nf := frame.NewFrame()
nf.WriteVersion(frame.VERSION_1)
nf.WriteFlags(frame.CONTROL, frame.CODEC_GOB, frame.CODEC_JSON)
nf.WritePayloadLen(uint32(len([]byte(TestPayload))))
nf.WritePayloadLen(nf.Header(), uint32(len([]byte(TestPayload))))
nf.WritePayload([]byte(TestPayload))
nf.WriteCRC()
assert.Equal(t, true, nf.VerifyCRC())
nf.WriteCRC(nf.Header())
assert.Equal(t, true, nf.VerifyCRC(nf.Header()))

go func(frame *frame.Frame) {
defer func() {
Expand All @@ -41,7 +41,7 @@ func TestPipeReceive(t *testing.T) {
assert.Equal(t, fr.ReadVersion(), nf.ReadVersion())
assert.Equal(t, fr.ReadFlags(), nf.ReadFlags())
assert.Equal(t, fr.ReadPayloadLen(), nf.ReadPayloadLen())
assert.Equal(t, true, fr.VerifyCRC())
assert.Equal(t, true, fr.VerifyCRC(nf.Header()))
assert.Equal(t, []byte(TestPayload), fr.Payload())
}

Expand All @@ -53,11 +53,11 @@ func TestPipeReceiveWithOptions(t *testing.T) {
nf := frame.NewFrame()
nf.WriteVersion(frame.VERSION_1)
nf.WriteFlags(frame.CONTROL, frame.CODEC_GOB, frame.CODEC_JSON)
nf.WritePayloadLen(uint32(len([]byte(TestPayload))))
nf.WritePayloadLen(nf.Header(), uint32(len([]byte(TestPayload))))
nf.WritePayload([]byte(TestPayload))
nf.WriteOptions(100, 10000, 100000)
nf.WriteCRC()
assert.Equal(t, true, nf.VerifyCRC())
nf.WriteCRC(nf.Header())
assert.Equal(t, true, nf.VerifyCRC(nf.Header()))

go func(frame *frame.Frame) {
defer func() {
Expand All @@ -77,9 +77,9 @@ func TestPipeReceiveWithOptions(t *testing.T) {
assert.Equal(t, fr.ReadVersion(), nf.ReadVersion())
assert.Equal(t, fr.ReadFlags(), nf.ReadFlags())
assert.Equal(t, fr.ReadPayloadLen(), nf.ReadPayloadLen())
assert.Equal(t, true, fr.VerifyCRC())
assert.Equal(t, true, fr.VerifyCRC(fr.Header()))
assert.Equal(t, []byte(TestPayload), fr.Payload())
assert.Equal(t, []uint32{100, 10000, 100000}, fr.ReadOptions())
assert.Equal(t, []uint32{100, 10000, 100000}, fr.ReadOptions(fr.Header()))
}

func TestPipeCRC_Failed(t *testing.T) {
Expand All @@ -90,9 +90,9 @@ func TestPipeCRC_Failed(t *testing.T) {
nf := frame.NewFrame()
nf.WriteVersion(frame.VERSION_1)
nf.WriteFlags(frame.CONTROL)
nf.WritePayloadLen(uint32(len([]byte(TestPayload))))
nf.WritePayloadLen(nf.Header(), uint32(len([]byte(TestPayload))))

assert.Equal(t, false, nf.VerifyCRC())
assert.Equal(t, false, nf.VerifyCRC(nf.Header()))

nf.WritePayload([]byte(TestPayload))

Expand Down
Loading