Skip to content
Open
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
2 changes: 1 addition & 1 deletion bindings/cproto/cproto_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func BenchmarkGetConn(b *testing.B) {
b.Run("getConn", func(b *testing.B) {
var conn connection
ctx := context.Background()
for i := 0; i < b.N; i++ {
for b.Loop() {
conn, err = binding.getConnection(ctx)
if err != nil {
panic(err)
Expand Down
29 changes: 13 additions & 16 deletions bindings/cproto/encdec_bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ func BenchmarkRPCEncoderInt32ArrArg(b *testing.B) {
}

b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
enc := newRPCEncoder(cmdSelect, uint32(i), false, false)
var seq uint32
for b.Loop() {
enc := newRPCEncoder(cmdSelect, seq, false, false)
enc.int32ArrArg(values)
_ = enc.bytes()
enc.ser.Close()
seq++
}
}

Expand All @@ -34,12 +35,13 @@ func BenchmarkRPCEncoderSnappyBytes(b *testing.B) {

b.ReportAllocs()
b.SetBytes(int64(len(payload)))
b.ResetTimer()
for i := 0; i < b.N; i++ {
enc := newRPCEncoder(cmdSelect, uint32(i), true, false)
var seq uint32
for b.Loop() {
enc := newRPCEncoder(cmdSelect, seq, true, false)
enc.bytesArg(payload)
_ = enc.bytes()
enc.ser.Close()
seq++
}
}

Expand All @@ -48,8 +50,7 @@ func BenchmarkRPCEncoderStartArgsChunck(b *testing.B) {
defer enc.ser.Close()

b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
enc.ser.Truncate(cprotoHdrLen)
enc.startArgsChunck()
}
Expand All @@ -65,8 +66,7 @@ func BenchmarkRPCEncoderUpdate(b *testing.B) {
enc.ser.PutVarInt(1)

b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
enc.update()
}
}
Expand Down Expand Up @@ -98,8 +98,7 @@ func BenchmarkRPCDecoderIntfArgs(b *testing.B) {

b.ReportAllocs()
b.SetBytes(int64(len(reply)))
b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
dec := newRPCDecoder(reply)
err := dec.errCode()
require.NoError(b, err)
Expand Down Expand Up @@ -141,8 +140,7 @@ func BenchmarkNetBufferParseArgs(b *testing.B) {

b.ReportAllocs()
b.SetBytes(int64(len(reply)))
b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
err := nb.parseArgs()
require.NoError(b, err)
if len(nb.args) != 5 {
Expand All @@ -159,8 +157,7 @@ func BenchmarkNetBufferParseArgsTimeout(b *testing.B) {

b.ReportAllocs()
b.SetBytes(int64(len(reply)))
b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
err := nb.parseArgs()
if err != context.DeadlineExceeded {
b.Fatalf("expected context.DeadlineExceeded, got %v", err)
Expand Down
193 changes: 193 additions & 0 deletions cjson/base64_bench_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
package cjson

import (
"fmt"
"sort"
"testing"
)

type benchBase64BytesDoc struct {
ID int `json:"id"`
Data []byte `json:"data"`
}

var cjsonBase64PayloadSizes = []int{0, 1, 2, 3, 8, 16, 24, 32, 64, 128, 192, 512, 1024, 4096}

func makeCJSONBase64Payload(size int) []byte {
payload := make([]byte, size)
for i := range payload {
payload[i] = byte(i*31 + 17)
}
return payload
}

func makeCJSONBase64RealShapePayloads() [][]byte {
words := []string{
"able_ant", "balanced_bear", "direct_dane", "enabled_emu",
"faithful_fish", "great_goat", "humane_hare", "logical_lark",
"modern_mole", "optimal_orca", "rapid_ray", "welcome_wren",
}
payloads := make([][]byte, len(words))
for i, word := range words {
payloads[i] = []byte(word)
}
return payloads
}

func BenchmarkCJSONBase64Encode(b *testing.B) {
state := NewState()
enc := state.NewEncoder()

for _, size := range cjsonBase64PayloadSizes {
payload := makeCJSONBase64Payload(size)
doc := benchBase64BytesDoc{ID: size, Data: payload}

ser := NewPoolSerializer()
if err := enc.EncodeRaw(doc, ser); err != nil {
b.Fatalf("warm encode size %d: %v", size, err)
}
ser.Close()

b.Run(fmt.Sprintf("size=%d", size), func(b *testing.B) {
b.ReportAllocs()
b.SetBytes(int64(size))
for b.Loop() {
ser := NewPoolSerializer()
if err := enc.EncodeRaw(doc, ser); err != nil {
b.Fatalf("encode size %d: %v", size, err)
}
benchBytesSink = ser.Bytes()
ser.Close()
}
})
}
}

func BenchmarkCJSONBase64Decode(b *testing.B) {
state := NewState()
enc := state.NewEncoder()
dec := state.NewDecoder(benchBase64BytesDoc{}, nil)
defer dec.Finalize()

for _, size := range cjsonBase64PayloadSizes {
payload := makeCJSONBase64Payload(size)
doc := benchBase64BytesDoc{ID: size, Data: payload}
ser := NewPoolSerializer()
if err := enc.EncodeRaw(doc, ser); err != nil {
b.Fatalf("prepare decode size %d: %v", size, err)
}
wire := append([]byte(nil), ser.Bytes()...)
ser.Close()

b.Run(fmt.Sprintf("size=%d", size), func(b *testing.B) {
b.ReportAllocs()
b.SetBytes(int64(size))
for b.Loop() {
var out benchBase64BytesDoc
if err := dec.Decode(wire, &out); err != nil {
b.Fatalf("decode size %d: %v", size, err)
}
benchBytesSink = out.Data
}
})
}
}

func BenchmarkCJSONBase64RealShape(b *testing.B) {
payloads := makeCJSONBase64RealShapePayloads()
state := NewState()
enc := state.NewEncoder()
dec := state.NewDecoder(benchBase64BytesDoc{}, nil)
defer dec.Finalize()

wires := make([][]byte, len(payloads))
for i, payload := range payloads {
ser := NewPoolSerializer()
if err := enc.EncodeRaw(benchBase64BytesDoc{ID: i, Data: payload}, ser); err != nil {
b.Fatalf("prepare real-shape %d: %v", i, err)
}
wires[i] = append([]byte(nil), ser.Bytes()...)
ser.Close()
}

b.Run("encode", func(b *testing.B) {
b.ReportAllocs()
for b.Loop() {
for i, payload := range payloads {
ser := NewPoolSerializer()
if err := enc.EncodeRaw(benchBase64BytesDoc{ID: i, Data: payload}, ser); err != nil {
b.Fatalf("encode real-shape %d: %v", i, err)
}
benchBytesSink = ser.Bytes()
ser.Close()
}
}
})

b.Run("decode", func(b *testing.B) {
b.ReportAllocs()
for b.Loop() {
for i, wire := range wires {
var out benchBase64BytesDoc
if err := dec.Decode(wire, &out); err != nil {
b.Fatalf("decode real-shape %d: %v", i, err)
}
benchBytesSink = out.Data
}
}
})
}

func BenchmarkCJSONBase64PayloadReport(b *testing.B) {
payloads := append([][]byte(nil), makeCJSONBase64RealShapePayloads()...)
for _, size := range cjsonBase64PayloadSizes {
payloads = append(payloads, makeCJSONBase64Payload(size))
}

lengths := make([]int, len(payloads))
encodedLengths := make([]int, len(payloads))
modCounts := [3]int{}
buckets := [4]int{}
for i, payload := range payloads {
l := len(payload)
lengths[i] = l
encodedLengths[i] = ((l + 2) / 3) * 4
modCounts[l%3]++
switch {
case l < 16:
buckets[0]++
case l <= 128:
buckets[1]++
case l <= 191:
buckets[2]++
default:
buckets[3]++
}
}
sort.Ints(lengths)
sort.Ints(encodedLengths)

count := float64(len(payloads))
b.ReportAllocs()
for b.Loop() {
for _, payload := range payloads {
benchBytesSink = payload
}
}

b.ReportMetric(count, "payloads")
b.ReportMetric(float64(lengths[0]), "raw_min")
b.ReportMetric(float64(lengths[len(lengths)/2]), "raw_p50")
b.ReportMetric(float64(lengths[(len(lengths)*90)/100]), "raw_p90")
b.ReportMetric(float64(lengths[(len(lengths)*99)/100]), "raw_p99")
b.ReportMetric(float64(lengths[len(lengths)-1]), "raw_max")
b.ReportMetric(float64(encodedLengths[len(encodedLengths)/2]), "enc_p50")
b.ReportMetric(float64(encodedLengths[len(encodedLengths)-1]), "enc_max")
b.ReportMetric(float64(modCounts[0])*100/count, "mod0_pct")
b.ReportMetric(float64(modCounts[1])*100/count, "mod1_pct")
b.ReportMetric(float64(modCounts[2])*100/count, "mod2_pct")
b.ReportMetric(float64(buckets[0])*100/count, "lt16_pct")
b.ReportMetric(float64(buckets[1])*100/count, "16_128_pct")
b.ReportMetric(float64(buckets[2])*100/count, "129_191_pct")
b.ReportMetric(float64(buckets[3])*100/count, "ge192_pct")
}
2 changes: 0 additions & 2 deletions cjson/encoder_bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ func benchmarkCJSONEncodeRaw(b *testing.B, doc any) {
ser.Close()

b.ReportAllocs()
b.ResetTimer()
for b.Loop() {
ser := NewPoolSerializer()
if err := enc.EncodeRaw(doc, ser); err != nil {
Expand All @@ -103,7 +102,6 @@ func BenchmarkGobEncoderTypicalDocument(b *testing.B) {
}

b.ReportAllocs()
b.ResetTimer()
for b.Loop() {
buf.Reset()
if err := enc.Encode(doc); err != nil {
Expand Down
53 changes: 49 additions & 4 deletions cjson/serializer_bench_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package cjson

import (
"reflect"
"testing"

"github.com/stretchr/testify/require"
)

var benchSerializerLen int

func BenchmarkSerializerEncodeTypicalDocument(b *testing.B) {
tags := []string{"books", "science", "math", "history"}
scores := []int{10, 42, 17, 5, 99, 100, 77, 54}
Expand All @@ -15,7 +18,7 @@ func BenchmarkSerializerEncodeTypicalDocument(b *testing.B) {
}

b.ReportAllocs()

for b.Loop() {
ser := NewPoolSerializer()
ser.WriteString("doc-2026-04-12")
Expand Down Expand Up @@ -44,7 +47,7 @@ func BenchmarkSerializerWriteIntsBatch(b *testing.B) {

b.ReportAllocs()
b.SetBytes(int64(len(values) * 8))

for b.Loop() {
ser := NewPoolSerializer()
_, err := ser.WriteInts(values)
Expand All @@ -61,7 +64,7 @@ func BenchmarkSerializerWriteInts16Batch(b *testing.B) {

b.ReportAllocs()
b.SetBytes(int64(len(values) * 2))

for b.Loop() {
ser := NewPoolSerializer()
_, err := ser.WriteInts16(values)
Expand All @@ -78,10 +81,52 @@ func BenchmarkSerializerPutFloatVectorBatch(b *testing.B) {

b.ReportAllocs()
b.SetBytes(int64(len(vec) * 4))

for b.Loop() {
ser := NewPoolSerializer()
ser.PutFloatVector(vec)
ser.Close()
}
}

func BenchmarkSerializerPutValueInt(b *testing.B) {
v := reflect.ValueOf(123)
var buf [32]byte

b.ReportAllocs()
for b.Loop() {
ser := NewSerializer(buf[:0])
if err := ser.PutValue(v); err != nil {
b.Fatal(err)
}
benchSerializerLen = len(ser.Bytes())
}
}

func BenchmarkSerializerPutValueString(b *testing.B) {
v := reflect.ValueOf("benchmark")
var buf [64]byte

b.ReportAllocs()
for b.Loop() {
ser := NewSerializer(buf[:0])
if err := ser.PutValue(v); err != nil {
b.Fatal(err)
}
benchSerializerLen = len(ser.Bytes())
}
}

func BenchmarkSerializerPutValueSlice(b *testing.B) {
v := reflect.ValueOf([]int{1, 2, 3, 4})
var buf [128]byte

b.ReportAllocs()
for b.Loop() {
ser := NewSerializer(buf[:0])
if err := ser.PutValue(v); err != nil {
b.Fatal(err)
}
benchSerializerLen = len(ser.Bytes())
}
}
Loading