From 2432ec9a23bdd9cb7b8062cadd3c6b8a17997dc3 Mon Sep 17 00:00:00 2001 From: Michal Hruby Date: Sun, 16 Jun 2024 00:39:35 +0100 Subject: [PATCH 1/2] speed up integer processing --- msgp/integers.go | 59 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/msgp/integers.go b/msgp/integers.go index f817d775..d07a5fba 100644 --- a/msgp/integers.go +++ b/msgp/integers.go @@ -1,5 +1,7 @@ package msgp +import "encoding/binary" + /* ---------------------------------- integer encoding utilities (inline-able) @@ -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) @@ -23,6 +27,8 @@ 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) | @@ -30,6 +36,8 @@ func getMint64(b []byte) int64 { } func putMint32(b []byte, i int32) { + _ = b[4] // bounds check elimination + b[0] = mint32 b[1] = byte(i >> 24) b[2] = byte(i >> 16) @@ -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) } @@ -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) @@ -73,6 +91,8 @@ 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) | @@ -80,6 +100,8 @@ func getMuint64(b []byte) uint64 { } func putMuint32(b []byte, u uint32) { + _ = b[4] // bounds check elimination + b[0] = muint32 b[1] = byte(u >> 24) b[2] = byte(u >> 16) @@ -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) } @@ -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)) } /* ----------------------------- @@ -141,12 +158,16 @@ 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) @@ -154,6 +175,8 @@ func prefixu16(b []byte, pre byte, sz uint16) { // 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) @@ -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) From ca808f163dc519cbf4568bbd4b11e426ecb08631 Mon Sep 17 00:00:00 2001 From: Michal Hruby Date: Sun, 16 Jun 2024 00:39:43 +0100 Subject: [PATCH 2/2] added benchmarks --- msgp/integers_test.go | 140 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 msgp/integers_test.go diff --git a/msgp/integers_test.go b/msgp/integers_test.go new file mode 100644 index 00000000..f67c44d8 --- /dev/null +++ b/msgp/integers_test.go @@ -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) + } + }) +}