benchmark_test.go raw

   1  package event
   2  
   3  import (
   4  	"bytes"
   5  	"testing"
   6  	"time"
   7  
   8  	"next.orly.dev/pkg/nostr/encoders/hex"
   9  	"next.orly.dev/pkg/nostr/encoders/kind"
  10  	"next.orly.dev/pkg/nostr/encoders/tag"
  11  	"next.orly.dev/pkg/nostr/interfaces/signer/p8k"
  12  	"lukechampine.com/frand"
  13  )
  14  
  15  // createTestEvent creates a realistic test event with proper signing
  16  func createTestEvent() *E {
  17  	signer := p8k.MustNew()
  18  	if err := signer.Generate(); err != nil {
  19  		panic(err)
  20  	}
  21  
  22  	ev := New()
  23  	ev.Pubkey = signer.Pub()
  24  	ev.CreatedAt = time.Now().Unix()
  25  	ev.Kind = kind.TextNote.K
  26  
  27  	// Create realistic tags
  28  	ev.Tags = tag.NewS(
  29  		tag.NewFromBytesSlice([]byte("t"), []byte("hashtag")),
  30  		tag.NewFromBytesSlice([]byte("e"), hex.EncAppend(nil, frand.Bytes(32))),
  31  		tag.NewFromBytesSlice([]byte("p"), hex.EncAppend(nil, frand.Bytes(32))),
  32  	)
  33  
  34  	// Create realistic content
  35  	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.`)
  36  
  37  	// Sign the event
  38  	if err := ev.Sign(signer); err != nil {
  39  		panic(err)
  40  	}
  41  
  42  	return ev
  43  }
  44  
  45  // createLargeTestEvent creates a larger event with more tags and content
  46  func createLargeTestEvent() *E {
  47  	signer := p8k.MustNew()
  48  	if err := signer.Generate(); err != nil {
  49  		panic(err)
  50  	}
  51  
  52  	ev := New()
  53  	ev.Pubkey = signer.Pub()
  54  	ev.CreatedAt = time.Now().Unix()
  55  	ev.Kind = kind.TextNote.K
  56  
  57  	// Create many tags
  58  	tags := tag.NewS()
  59  	for i := 0; i < 20; i++ {
  60  		tags.Append(
  61  			tag.NewFromBytesSlice(
  62  				[]byte("t"),
  63  				[]byte("hashtag"+string(rune('0'+i))),
  64  			),
  65  		)
  66  		if i%3 == 0 {
  67  			tags.Append(
  68  				tag.NewFromBytesSlice(
  69  					[]byte("e"),
  70  					hex.EncAppend(nil, frand.Bytes(32)),
  71  				),
  72  			)
  73  		}
  74  	}
  75  	ev.Tags = tags
  76  
  77  	// Large content
  78  	content := make([]byte, 0, 4096)
  79  	for i := 0; i < 50; i++ {
  80  		content = append(
  81  			content,
  82  			[]byte("This is a longer piece of content that simulates real-world event content. ")...,
  83  		)
  84  		if i%10 == 0 {
  85  			content = append(
  86  				content, []byte("With special chars: < > & \" ' ")...,
  87  			)
  88  		}
  89  	}
  90  	ev.Content = content
  91  
  92  	// Sign the event
  93  	if err := ev.Sign(signer); err != nil {
  94  		panic(err)
  95  	}
  96  
  97  	return ev
  98  }
  99  
 100  // BenchmarkJSONMarshal benchmarks the JSON marshaling
 101  func BenchmarkJSONMarshal(b *testing.B) {
 102  	ev := createTestEvent()
 103  	defer ev.Free()
 104  
 105  	b.ResetTimer()
 106  	b.ReportAllocs()
 107  
 108  	for i := 0; i < b.N; i++ {
 109  		_ = ev.Marshal(nil)
 110  	}
 111  }
 112  
 113  // BenchmarkJSONMarshalLarge benchmarks JSON marshaling with large events
 114  func BenchmarkJSONMarshalLarge(b *testing.B) {
 115  	ev := createLargeTestEvent()
 116  	defer ev.Free()
 117  
 118  	b.ResetTimer()
 119  	b.ReportAllocs()
 120  
 121  	for i := 0; i < b.N; i++ {
 122  		_ = ev.Marshal(nil)
 123  	}
 124  }
 125  
 126  // BenchmarkJSONUnmarshal benchmarks JSON unmarshaling
 127  func BenchmarkJSONUnmarshal(b *testing.B) {
 128  	ev := createTestEvent()
 129  	jsonData := ev.Marshal(nil)
 130  	defer ev.Free()
 131  
 132  	b.ResetTimer()
 133  	b.ReportAllocs()
 134  
 135  	for i := 0; i < b.N; i++ {
 136  		ev2 := New()
 137  		_, err := ev2.Unmarshal(jsonData)
 138  		if err != nil {
 139  			b.Fatal(err)
 140  		}
 141  		ev2.Free()
 142  	}
 143  }
 144  
 145  // BenchmarkBinaryMarshal benchmarks binary marshaling
 146  func BenchmarkBinaryMarshal(b *testing.B) {
 147  	ev := createTestEvent()
 148  	defer ev.Free()
 149  
 150  	buf := &bytes.Buffer{}
 151  	b.ResetTimer()
 152  	b.ReportAllocs()
 153  
 154  	for i := 0; i < b.N; i++ {
 155  		buf.Reset()
 156  		ev.MarshalBinary(buf)
 157  	}
 158  }
 159  
 160  // BenchmarkBinaryMarshalLarge benchmarks binary marshaling with large events
 161  func BenchmarkBinaryMarshalLarge(b *testing.B) {
 162  	ev := createLargeTestEvent()
 163  	defer ev.Free()
 164  
 165  	buf := &bytes.Buffer{}
 166  	b.ResetTimer()
 167  	b.ReportAllocs()
 168  
 169  	for i := 0; i < b.N; i++ {
 170  		buf.Reset()
 171  		ev.MarshalBinary(buf)
 172  	}
 173  }
 174  
 175  // BenchmarkBinaryUnmarshal benchmarks binary unmarshaling
 176  func BenchmarkBinaryUnmarshal(b *testing.B) {
 177  	ev := createTestEvent()
 178  	buf := &bytes.Buffer{}
 179  	ev.MarshalBinary(buf)
 180  	binaryData := buf.Bytes()
 181  	defer ev.Free()
 182  
 183  	b.ResetTimer()
 184  	b.ReportAllocs()
 185  
 186  	for i := 0; i < b.N; i++ {
 187  		ev2 := New()
 188  		reader := bytes.NewReader(binaryData)
 189  		if err := ev2.UnmarshalBinary(reader); err != nil {
 190  			b.Fatal(err)
 191  		}
 192  		ev2.Free()
 193  	}
 194  }
 195  
 196  // BenchmarkCanonical benchmarks canonical encoding
 197  func BenchmarkCanonical(b *testing.B) {
 198  	ev := createTestEvent()
 199  	defer ev.Free()
 200  
 201  	b.ResetTimer()
 202  	b.ReportAllocs()
 203  
 204  	for i := 0; i < b.N; i++ {
 205  		_ = ev.ToCanonical(nil)
 206  	}
 207  }
 208  
 209  // BenchmarkCanonicalLarge benchmarks canonical encoding with large events
 210  func BenchmarkCanonicalLarge(b *testing.B) {
 211  	ev := createLargeTestEvent()
 212  	defer ev.Free()
 213  
 214  	b.ResetTimer()
 215  	b.ReportAllocs()
 216  
 217  	for i := 0; i < b.N; i++ {
 218  		_ = ev.ToCanonical(nil)
 219  	}
 220  }
 221  
 222  // BenchmarkGetIDBytes benchmarks ID generation (canonical + hash)
 223  func BenchmarkGetIDBytes(b *testing.B) {
 224  	ev := createTestEvent()
 225  	defer ev.Free()
 226  
 227  	b.ResetTimer()
 228  	b.ReportAllocs()
 229  
 230  	for i := 0; i < b.N; i++ {
 231  		_ = ev.GetIDBytes()
 232  	}
 233  }
 234  
 235  // BenchmarkRoundTripJSON benchmarks JSON marshal/unmarshal round trip
 236  func BenchmarkRoundTripJSON(b *testing.B) {
 237  	ev := createTestEvent()
 238  	defer ev.Free()
 239  
 240  	b.ResetTimer()
 241  	b.ReportAllocs()
 242  
 243  	for i := 0; i < b.N; i++ {
 244  		jsonData := ev.Marshal(nil)
 245  		ev2 := New()
 246  		_, err := ev2.Unmarshal(jsonData)
 247  		if err != nil {
 248  			b.Fatal(err)
 249  		}
 250  		ev2.Free()
 251  	}
 252  }
 253  
 254  // BenchmarkRoundTripBinary benchmarks binary marshal/unmarshal round trip
 255  func BenchmarkRoundTripBinary(b *testing.B) {
 256  	ev := createTestEvent()
 257  	defer ev.Free()
 258  
 259  	buf := &bytes.Buffer{}
 260  	b.ResetTimer()
 261  	b.ReportAllocs()
 262  
 263  	for i := 0; i < b.N; i++ {
 264  		buf.Reset()
 265  		ev.MarshalBinary(buf)
 266  
 267  		ev2 := New()
 268  		reader := bytes.NewReader(buf.Bytes())
 269  		if err := ev2.UnmarshalBinary(reader); err != nil {
 270  			b.Fatal(err)
 271  		}
 272  		ev2.Free()
 273  	}
 274  }
 275  
 276  // BenchmarkEstimateSize benchmarks size estimation
 277  func BenchmarkEstimateSize(b *testing.B) {
 278  	ev := createTestEvent()
 279  	defer ev.Free()
 280  
 281  	b.ResetTimer()
 282  	b.ReportAllocs()
 283  
 284  	for i := 0; i < b.N; i++ {
 285  		_ = ev.EstimateSize()
 286  	}
 287  }
 288