Skip to content

Commit

Permalink
#143 perf(frame): reduce bound checks
Browse files Browse the repository at this point in the history
#143 perf(frame): reduce bound checks
  • Loading branch information
rustatian authored Aug 4, 2021
2 parents 0b1e40f + e7fe0fc commit d5cf35b
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 94 deletions.
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

0 comments on commit d5cf35b

Please sign in to comment.