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

Bounds checks elimination #348

Merged
merged 2 commits into from
Jun 16, 2024
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
59 changes: 42 additions & 17 deletions msgp/integers.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package msgp

import "encoding/binary"

/* ----------------------------------
integer encoding utilities
(inline-able)
Expand All @@ -11,6 +13,8 @@ package msgp
---------------------------------- */

func putMint64(b []byte, i int64) {
_ = b[8] // bounds check elimination

b[0] = mint64
b[1] = byte(i >> 56)
b[2] = byte(i >> 48)
Expand All @@ -23,13 +27,17 @@ func putMint64(b []byte, i int64) {
}

func getMint64(b []byte) int64 {
_ = b[8] // bounds check elimination

return (int64(b[1]) << 56) | (int64(b[2]) << 48) |
(int64(b[3]) << 40) | (int64(b[4]) << 32) |
(int64(b[5]) << 24) | (int64(b[6]) << 16) |
(int64(b[7]) << 8) | (int64(b[8]))
}

func putMint32(b []byte, i int32) {
_ = b[4] // bounds check elimination

b[0] = mint32
b[1] = byte(i >> 24)
b[2] = byte(i >> 16)
Expand All @@ -38,20 +46,28 @@ func putMint32(b []byte, i int32) {
}

func getMint32(b []byte) int32 {
_ = b[4] // bounds check elimination

return (int32(b[1]) << 24) | (int32(b[2]) << 16) | (int32(b[3]) << 8) | (int32(b[4]))
}

func putMint16(b []byte, i int16) {
_ = b[2] // bounds check elimination

b[0] = mint16
b[1] = byte(i >> 8)
b[2] = byte(i)
}

func getMint16(b []byte) (i int16) {
_ = b[2] // bounds check elimination

return (int16(b[1]) << 8) | int16(b[2])
}

func putMint8(b []byte, i int8) {
_ = b[1] // bounds check elimination

b[0] = mint8
b[1] = byte(i)
}
Expand All @@ -61,6 +77,8 @@ func getMint8(b []byte) (i int8) {
}

func putMuint64(b []byte, u uint64) {
_ = b[8] // bounds check elimination

b[0] = muint64
b[1] = byte(u >> 56)
b[2] = byte(u >> 48)
Expand All @@ -73,13 +91,17 @@ func putMuint64(b []byte, u uint64) {
}

func getMuint64(b []byte) uint64 {
_ = b[8] // bounds check elimination

return (uint64(b[1]) << 56) | (uint64(b[2]) << 48) |
(uint64(b[3]) << 40) | (uint64(b[4]) << 32) |
(uint64(b[5]) << 24) | (uint64(b[6]) << 16) |
(uint64(b[7]) << 8) | (uint64(b[8]))
}

func putMuint32(b []byte, u uint32) {
_ = b[4] // bounds check elimination

b[0] = muint32
b[1] = byte(u >> 24)
b[2] = byte(u >> 16)
Expand All @@ -88,20 +110,28 @@ func putMuint32(b []byte, u uint32) {
}

func getMuint32(b []byte) uint32 {
_ = b[4] // bounds check elimination

return (uint32(b[1]) << 24) | (uint32(b[2]) << 16) | (uint32(b[3]) << 8) | (uint32(b[4]))
}

func putMuint16(b []byte, u uint16) {
_ = b[2] // bounds check elimination

b[0] = muint16
b[1] = byte(u >> 8)
b[2] = byte(u)
}

func getMuint16(b []byte) uint16 {
_ = b[2] // bounds check elimination

return (uint16(b[1]) << 8) | uint16(b[2])
}

func putMuint8(b []byte, u uint8) {
_ = b[1] // bounds check elimination

b[0] = muint8
b[1] = byte(u)
}
Expand All @@ -111,28 +141,15 @@ func getMuint8(b []byte) uint8 {
}

func getUnix(b []byte) (sec int64, nsec int32) {
sec = (int64(b[0]) << 56) | (int64(b[1]) << 48) |
(int64(b[2]) << 40) | (int64(b[3]) << 32) |
(int64(b[4]) << 24) | (int64(b[5]) << 16) |
(int64(b[6]) << 8) | (int64(b[7]))
sec = int64(binary.BigEndian.Uint64(b))
nsec = int32(binary.BigEndian.Uint32(b[8:]))

nsec = (int32(b[8]) << 24) | (int32(b[9]) << 16) | (int32(b[10]) << 8) | (int32(b[11]))
return
}

func putUnix(b []byte, sec int64, nsec int32) {
b[0] = byte(sec >> 56)
b[1] = byte(sec >> 48)
b[2] = byte(sec >> 40)
b[3] = byte(sec >> 32)
b[4] = byte(sec >> 24)
b[5] = byte(sec >> 16)
b[6] = byte(sec >> 8)
b[7] = byte(sec)
b[8] = byte(nsec >> 24)
b[9] = byte(nsec >> 16)
b[10] = byte(nsec >> 8)
b[11] = byte(nsec)
binary.BigEndian.PutUint64(b, uint64(sec))
binary.BigEndian.PutUint32(b[8:], uint32(nsec))
}

/* -----------------------------
Expand All @@ -141,19 +158,25 @@ func putUnix(b []byte, sec int64, nsec int32) {

// write prefix and uint8
func prefixu8(b []byte, pre byte, sz uint8) {
_ = b[1] // bounds check elimination

b[0] = pre
b[1] = byte(sz)
}

// write prefix and big-endian uint16
func prefixu16(b []byte, pre byte, sz uint16) {
_ = b[2] // bounds check elimination

b[0] = pre
b[1] = byte(sz >> 8)
b[2] = byte(sz)
}

// write prefix and big-endian uint32
func prefixu32(b []byte, pre byte, sz uint32) {
_ = b[4] // bounds check elimination

b[0] = pre
b[1] = byte(sz >> 24)
b[2] = byte(sz >> 16)
Expand All @@ -162,6 +185,8 @@ func prefixu32(b []byte, pre byte, sz uint32) {
}

func prefixu64(b []byte, pre byte, sz uint64) {
_ = b[8] // bounds check elimination

b[0] = pre
b[1] = byte(sz >> 56)
b[2] = byte(sz >> 48)
Expand Down
140 changes: 140 additions & 0 deletions msgp/integers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package msgp

import (
"encoding/binary"
"testing"
)

func BenchmarkIntegers(b *testing.B) {
bytes64 := make([]byte, 9)
bytes32 := make([]byte, 5)
bytes16 := make([]byte, 3)

b.Run("Int64", func(b *testing.B) {
b.Run("Put", func(b *testing.B) {
for i := 0; i < b.N; i++ {
putMint64(bytes64, -1234567890123456789)
}
})
b.Run("Get", func(b *testing.B) {
putMint64(bytes64, -1234567890123456789)
for i := 0; i < b.N; i++ {
getMint64(bytes64)
}
})
})
b.Run("Int32", func(b *testing.B) {
b.Run("Put", func(b *testing.B) {
for i := 0; i < b.N; i++ {
putMint32(bytes32, -123456789)
}
})
b.Run("Get", func(b *testing.B) {
putMint32(bytes32, -123456789)
for i := 0; i < b.N; i++ {
getMint32(bytes32)
}
})
})
b.Run("Int16", func(b *testing.B) {
b.Run("Put", func(b *testing.B) {
for i := 0; i < b.N; i++ {
putMint16(bytes16, -12345)
}
})
b.Run("Get", func(b *testing.B) {
putMint16(bytes16, -12345)
for i := 0; i < b.N; i++ {
getMint16(bytes16)
}
})
})

b.Run("Uint64", func(b *testing.B) {
b.Run("Put", func(b *testing.B) {
for i := 0; i < b.N; i++ {
putMuint64(bytes64, 1234567890123456789)
}
})
b.Run("Get", func(b *testing.B) {
putMuint64(bytes64, 1234567890123456789)
for i := 0; i < b.N; i++ {
getMuint64(bytes64)
}
})
})
b.Run("Uint32", func(b *testing.B) {
b.Run("Put", func(b *testing.B) {
for i := 0; i < b.N; i++ {
putMuint32(bytes32, 123456789)
}
})
b.Run("Get", func(b *testing.B) {
putMuint32(bytes32, 123456789)
for i := 0; i < b.N; i++ {
getMuint32(bytes32)
}
})
})
b.Run("Uint16", func(b *testing.B) {
b.Run("Put", func(b *testing.B) {
for i := 0; i < b.N; i++ {
putMuint16(bytes16, 12345)
}
})
b.Run("Get", func(b *testing.B) {
putMuint16(bytes16, 12345)
for i := 0; i < b.N; i++ {
getMuint16(bytes16)
}
})
})
}

func BenchmarkIntegersUnix(b *testing.B) {
bytes := make([]byte, 12)
var sec int64 = 1609459200
var nsec int32 = 123456789

b.Run("Get", func(b *testing.B) {
binary.BigEndian.PutUint64(bytes, uint64(sec))
binary.BigEndian.PutUint32(bytes[8:], uint32(nsec))
for i := 0; i < b.N; i++ {
getUnix(bytes)
}
})

b.Run("Put", func(b *testing.B) {
for i := 0; i < b.N; i++ {
putUnix(bytes, sec, nsec)
}
})
}

func BenchmarkIntegersPrefix(b *testing.B) {
bytesU16 := make([]byte, 3)
bytesU32 := make([]byte, 5)
bytesU64 := make([]byte, 9)

b.Run("u16", func(b *testing.B) {
var pre byte = 0x01
var sz uint16 = 12345
for i := 0; i < b.N; i++ {
prefixu16(bytesU16, pre, sz)
}
})
b.Run("u32", func(b *testing.B) {
var pre byte = 0x02
var sz uint32 = 123456789
for i := 0; i < b.N; i++ {
prefixu32(bytesU32, pre, sz)
}
})
b.Run("u64", func(b *testing.B) {
var pre byte = 0x03
var sz uint64 = 1234567890123456789
for i := 0; i < b.N; i++ {
prefixu64(bytesU64, pre, sz)
}
})
}
Loading