Add benchmark tests for event encoders and optimize performance
- Introduced benchmark tests for JSON and binary marshaling/unmarshaling, canonical encoding, and ID generation to assess performance. - Implemented optimizations to reduce memory allocations and CPU processing time across various encoding methods. - Enhanced `Marshal`, `ToCanonical`, and `MarshalBinary` methods with pre-allocation strategies to minimize reallocations. - Added handling for nil tags to avoid unnecessary allocations during binary encoding. - Documented performance improvements in the new PERFORMANCE_REPORT.md file, highlighting significant reductions in execution time and memory usage.
This commit is contained in:
279
pkg/encoders/event/benchmark_test.go
Normal file
279
pkg/encoders/event/benchmark_test.go
Normal file
@@ -0,0 +1,279 @@
|
||||
package event
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"next.orly.dev/pkg/crypto/p256k"
|
||||
"next.orly.dev/pkg/encoders/hex"
|
||||
"next.orly.dev/pkg/encoders/kind"
|
||||
"next.orly.dev/pkg/encoders/tag"
|
||||
"lukechampine.com/frand"
|
||||
)
|
||||
|
||||
// createTestEvent creates a realistic test event with proper signing
|
||||
func createTestEvent() *E {
|
||||
signer := &p256k.Signer{}
|
||||
if err := signer.Generate(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ev := New()
|
||||
ev.Pubkey = signer.Pub()
|
||||
ev.CreatedAt = time.Now().Unix()
|
||||
ev.Kind = kind.TextNote.K
|
||||
|
||||
// Create realistic tags
|
||||
ev.Tags = tag.NewS(
|
||||
tag.NewFromBytesSlice([]byte("t"), []byte("hashtag")),
|
||||
tag.NewFromBytesSlice([]byte("e"), hex.EncAppend(nil, frand.Bytes(32))),
|
||||
tag.NewFromBytesSlice([]byte("p"), hex.EncAppend(nil, frand.Bytes(32))),
|
||||
)
|
||||
|
||||
// Create realistic content
|
||||
ev.Content = []byte(`This is a test event with some content that includes special characters like < > & and "quotes" and various other things that might need escaping.`)
|
||||
|
||||
// Sign the event
|
||||
if err := ev.Sign(signer); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return ev
|
||||
}
|
||||
|
||||
// createLargeTestEvent creates a larger event with more tags and content
|
||||
func createLargeTestEvent() *E {
|
||||
signer := &p256k.Signer{}
|
||||
if err := signer.Generate(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ev := New()
|
||||
ev.Pubkey = signer.Pub()
|
||||
ev.CreatedAt = time.Now().Unix()
|
||||
ev.Kind = kind.TextNote.K
|
||||
|
||||
// Create many tags
|
||||
tags := tag.NewS()
|
||||
for i := 0; i < 20; i++ {
|
||||
tags.Append(tag.NewFromBytesSlice(
|
||||
[]byte("t"),
|
||||
[]byte("hashtag" + string(rune('0'+i))),
|
||||
))
|
||||
if i%3 == 0 {
|
||||
tags.Append(tag.NewFromBytesSlice(
|
||||
[]byte("e"),
|
||||
hex.EncAppend(nil, frand.Bytes(32)),
|
||||
))
|
||||
}
|
||||
}
|
||||
ev.Tags = tags
|
||||
|
||||
// Large content
|
||||
content := make([]byte, 0, 4096)
|
||||
for i := 0; i < 50; i++ {
|
||||
content = append(content, []byte("This is a longer piece of content that simulates real-world event content. ")...)
|
||||
if i%10 == 0 {
|
||||
content = append(content, []byte("With special chars: < > & \" ' ")...)
|
||||
}
|
||||
}
|
||||
ev.Content = content
|
||||
|
||||
// Sign the event
|
||||
if err := ev.Sign(signer); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return ev
|
||||
}
|
||||
|
||||
// BenchmarkJSONMarshal benchmarks the JSON marshaling
|
||||
func BenchmarkJSONMarshal(b *testing.B) {
|
||||
ev := createTestEvent()
|
||||
defer ev.Free()
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = ev.Marshal(nil)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkJSONMarshalLarge benchmarks JSON marshaling with large events
|
||||
func BenchmarkJSONMarshalLarge(b *testing.B) {
|
||||
ev := createLargeTestEvent()
|
||||
defer ev.Free()
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = ev.Marshal(nil)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkJSONUnmarshal benchmarks JSON unmarshaling
|
||||
func BenchmarkJSONUnmarshal(b *testing.B) {
|
||||
ev := createTestEvent()
|
||||
jsonData := ev.Marshal(nil)
|
||||
defer ev.Free()
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
ev2 := New()
|
||||
_, err := ev2.Unmarshal(jsonData)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
ev2.Free()
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkBinaryMarshal benchmarks binary marshaling
|
||||
func BenchmarkBinaryMarshal(b *testing.B) {
|
||||
ev := createTestEvent()
|
||||
defer ev.Free()
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
buf.Reset()
|
||||
ev.MarshalBinary(buf)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkBinaryMarshalLarge benchmarks binary marshaling with large events
|
||||
func BenchmarkBinaryMarshalLarge(b *testing.B) {
|
||||
ev := createLargeTestEvent()
|
||||
defer ev.Free()
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
buf.Reset()
|
||||
ev.MarshalBinary(buf)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkBinaryUnmarshal benchmarks binary unmarshaling
|
||||
func BenchmarkBinaryUnmarshal(b *testing.B) {
|
||||
ev := createTestEvent()
|
||||
buf := &bytes.Buffer{}
|
||||
ev.MarshalBinary(buf)
|
||||
binaryData := buf.Bytes()
|
||||
defer ev.Free()
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
ev2 := New()
|
||||
reader := bytes.NewReader(binaryData)
|
||||
if err := ev2.UnmarshalBinary(reader); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
ev2.Free()
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkCanonical benchmarks canonical encoding
|
||||
func BenchmarkCanonical(b *testing.B) {
|
||||
ev := createTestEvent()
|
||||
defer ev.Free()
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = ev.ToCanonical(nil)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkCanonicalLarge benchmarks canonical encoding with large events
|
||||
func BenchmarkCanonicalLarge(b *testing.B) {
|
||||
ev := createLargeTestEvent()
|
||||
defer ev.Free()
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = ev.ToCanonical(nil)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkGetIDBytes benchmarks ID generation (canonical + hash)
|
||||
func BenchmarkGetIDBytes(b *testing.B) {
|
||||
ev := createTestEvent()
|
||||
defer ev.Free()
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = ev.GetIDBytes()
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkRoundTripJSON benchmarks JSON marshal/unmarshal round trip
|
||||
func BenchmarkRoundTripJSON(b *testing.B) {
|
||||
ev := createTestEvent()
|
||||
defer ev.Free()
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
jsonData := ev.Marshal(nil)
|
||||
ev2 := New()
|
||||
_, err := ev2.Unmarshal(jsonData)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
ev2.Free()
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkRoundTripBinary benchmarks binary marshal/unmarshal round trip
|
||||
func BenchmarkRoundTripBinary(b *testing.B) {
|
||||
ev := createTestEvent()
|
||||
defer ev.Free()
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
buf.Reset()
|
||||
ev.MarshalBinary(buf)
|
||||
|
||||
ev2 := New()
|
||||
reader := bytes.NewReader(buf.Bytes())
|
||||
if err := ev2.UnmarshalBinary(reader); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
ev2.Free()
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkEstimateSize benchmarks size estimation
|
||||
func BenchmarkEstimateSize(b *testing.B) {
|
||||
ev := createTestEvent()
|
||||
defer ev.Free()
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = ev.EstimateSize()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user