mirror of
https://github.com/nbd-wtf/go-nostr.git
synced 2025-06-25 16:21:00 +02:00
fix these simdjson benchmarks so others can run them.
This commit is contained in:
parent
be2fe4a019
commit
a82780e82e
@ -1,88 +1,18 @@
|
|||||||
package nostr
|
package nostr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"fmt"
|
||||||
"os"
|
"math/rand/v2"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/minio/simdjson-go"
|
"github.com/minio/simdjson-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
// benchmarkParseMessage benchmarks the ParseMessage function
|
|
||||||
func BenchmarkParseMessage(b *testing.B) {
|
func BenchmarkParseMessage(b *testing.B) {
|
||||||
messages := getTestMessages()
|
messages := generateTestMessages(2000)
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
for _, msg := range messages {
|
|
||||||
_ = ParseMessage(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// benchmarkParseMessageSIMD benchmarks the ParseMessageSIMD function
|
b.Run("golang", func(b *testing.B) {
|
||||||
func BenchmarkParseMessageSIMD(b *testing.B) {
|
|
||||||
messages := getTestMessages()
|
|
||||||
var pj *simdjson.ParsedJson
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
for _, msg := range messages {
|
|
||||||
_, _ = ParseMessageSIMD(msg, pj)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// benchmarkParseMessageSIMDWithReuse benchmarks the ParseMessageSIMD function with reusing the ParsedJson object
|
|
||||||
func BenchmarkParseMessageSIMDWithReuse(b *testing.B) {
|
|
||||||
messages := getTestMessages()
|
|
||||||
pj, _ := simdjson.Parse(make([]byte, 1024), nil) // initialize with some capacity
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
for _, msg := range messages {
|
|
||||||
_, _ = ParseMessageSIMD(msg, pj)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// getTestMessages returns a slice of test messages for benchmarking
|
|
||||||
func getTestMessages() [][]byte {
|
|
||||||
// these are sample messages from the test cases
|
|
||||||
return [][]byte{
|
|
||||||
[]byte(`["EVENT","_",{"kind":1,"id":"dc90c95f09947507c1044e8f48bcf6350aa6bff1507dd4acfc755b9239b5c962","pubkey":"3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d","created_at":1644271588,"tags":[],"content":"now that https://blueskyweb.org/blog/2-7-2022-overview was announced we can stop working on nostr?","sig":"230e9d8f0ddaf7eb70b5f7741ccfa37e87a455c9a469282e3464e2052d3192cd63a167e196e381ef9d7e69e9ea43af2443b839974dc85d8aaab9efe1d9296524"}]`),
|
|
||||||
[]byte(`["EVENT",{"kind":1,"id":"dc90c95f09947507c1044e8f48bcf6350aa6bff1507dd4acfc755b9239b5c962","pubkey":"3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d","created_at":1644271588,"tags":[],"content":"now that https://blueskyweb.org/blog/2-7-2022-overview was announced we can stop working on nostr?","sig":"230e9d8f0ddaf7eb70b5f7741ccfa37e87a455c9a469282e3464e2052d3192cd63a167e196e381ef9d7e69e9ea43af2443b839974dc85d8aaab9efe1d9296524"}]`),
|
|
||||||
[]byte(`["AUTH","challenge-string"]`),
|
|
||||||
[]byte(`["AUTH",{"kind":22242,"id":"9b86ca5d2a9b4aa09870e710438c2fd2fcdeca12a18b6f17ab9ebcdbc43f1d4a","pubkey":"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798","created_at":1740505646,"tags":[["relay","ws://localhost:7777","2"],["challenge","3027526784722639360"]],"content":"","sig":"eceb827c4bba1de0ab8ee43f3e98df71194f5bdde0af27b5cda38e5c4338b5f63d31961acb5e3c119fd00ecef8b469867d060b697dbaa6ecee1906b483bc307d"}]`),
|
|
||||||
[]byte(`["NOTICE","test notice message"]`),
|
|
||||||
[]byte(`["EOSE","subscription123"]`),
|
|
||||||
[]byte(`["CLOSE","subscription123"]`),
|
|
||||||
[]byte(`["CLOSED","subscription123","reason: test closed"]`),
|
|
||||||
[]byte(`["OK","3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefaaaaa",true,""]`),
|
|
||||||
[]byte(`["COUNT","sub1",{"count":42}]`),
|
|
||||||
[]byte(`["REQ","sub1",{"until":999999,"kinds":[1]}]`),
|
|
||||||
[]byte(`["REQ","sub1z\\\"zzz",{"authors":["9b86ca5d2a9b4aa09870e710438c2fd2fcdeca12a18b6f17ab9ebcdbc43f1d4a","8eee10b2ce1162b040fdcfdadb4f888c64aaf87531dab28cc0c09fbdea1b663e","0deadebefb3c1a760f036952abf675076343dd8424efdeaa0f1d9803a359ed46"],"since":1740425099,"limit":2,"#x":["<","as"]},{"kinds":[2345,112],"#plic":["a"],"#ploc":["blblb","wuwuw"]}]`),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// benchmarkParseMessagesFromFile benchmarks parsing messages from a file
|
|
||||||
// this function can be used if you have a file with messages
|
|
||||||
func BenchmarkParseMessagesFromFile(b *testing.B) {
|
|
||||||
// read all messages into memory
|
|
||||||
file, err := os.Open("testdata/messages.json")
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
var messages [][]byte
|
|
||||||
scanner := bufio.NewScanner(file)
|
|
||||||
for scanner.Scan() {
|
|
||||||
messages = append(messages, []byte(scanner.Text()))
|
|
||||||
}
|
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
file.Close()
|
|
||||||
|
|
||||||
// benchmark ParseMessage
|
|
||||||
b.Run("ParseMessage", func(b *testing.B) {
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
for _, msg := range messages {
|
for _, msg := range messages {
|
||||||
_ = ParseMessage(msg)
|
_ = ParseMessage(msg)
|
||||||
@ -90,21 +20,8 @@ func BenchmarkParseMessagesFromFile(b *testing.B) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// benchmark ParseMessageSIMD
|
b.Run("simdjson", func(b *testing.B) {
|
||||||
b.Run("ParseMessageSIMD", func(b *testing.B) {
|
pj := &simdjson.ParsedJson{}
|
||||||
var pj *simdjson.ParsedJson
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
for _, msg := range messages {
|
|
||||||
_, _ = ParseMessageSIMD(msg, pj)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// benchmark ParseMessageSIMD with reuse
|
|
||||||
b.Run("ParseMessageSIMDWithReuse", func(b *testing.B) {
|
|
||||||
pj, _ := simdjson.Parse(make([]byte, 1024), nil) // initialize with some capacity
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
for _, msg := range messages {
|
for _, msg := range messages {
|
||||||
_, _ = ParseMessageSIMD(msg, pj)
|
_, _ = ParseMessageSIMD(msg, pj)
|
||||||
@ -112,3 +29,225 @@ func BenchmarkParseMessagesFromFile(b *testing.B) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func generateTestMessages(count int) [][]byte {
|
||||||
|
messages := make([][]byte, 0, count)
|
||||||
|
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
var msg []byte
|
||||||
|
|
||||||
|
switch rand.IntN(12) {
|
||||||
|
case 1:
|
||||||
|
msg = generateAuthMessage()
|
||||||
|
case 2:
|
||||||
|
msg = generateNoticeMessage()
|
||||||
|
case 3:
|
||||||
|
msg = generateEOSEMessage()
|
||||||
|
case 4:
|
||||||
|
msg = generateOKMessage()
|
||||||
|
case 5:
|
||||||
|
msg = generateCountMessage()
|
||||||
|
case 6:
|
||||||
|
msg = generateReqMessage()
|
||||||
|
default:
|
||||||
|
msg = generateEventMessage()
|
||||||
|
}
|
||||||
|
|
||||||
|
messages = append(messages, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return messages
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateEventMessage() []byte {
|
||||||
|
event := generateRandomEvent()
|
||||||
|
eventJSON, _ := json.Marshal(event)
|
||||||
|
|
||||||
|
if rand.IntN(2) == 0 {
|
||||||
|
subID := fmt.Sprintf("sub_%d", rand.IntN(1000))
|
||||||
|
return []byte(fmt.Sprintf(`["EVENT","%s",%s]`, subID, string(eventJSON)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(fmt.Sprintf(`["EVENT",%s]`, string(eventJSON)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateRandomEvent() Event {
|
||||||
|
tagCount := rand.IntN(10)
|
||||||
|
tags := make(Tags, tagCount)
|
||||||
|
for i := 0; i < tagCount; i++ {
|
||||||
|
tagType := string([]byte{byte('a' + rand.IntN(26))})
|
||||||
|
tagValues := make([]string, rand.IntN(5)+1)
|
||||||
|
for j := range tagValues {
|
||||||
|
tagValues[j] = fmt.Sprintf("value_%d_%d", i, j)
|
||||||
|
}
|
||||||
|
tags[i] = append([]string{tagType}, tagValues...)
|
||||||
|
}
|
||||||
|
|
||||||
|
contentLength := rand.IntN(200) + 10
|
||||||
|
content := make([]byte, contentLength)
|
||||||
|
for i := range content {
|
||||||
|
content[i] = byte('a' + rand.IntN(26))
|
||||||
|
}
|
||||||
|
|
||||||
|
event := Event{
|
||||||
|
ID: generateRandomHex(64),
|
||||||
|
PubKey: generateRandomHex(64),
|
||||||
|
CreatedAt: Timestamp(time.Now().Unix() - int64(rand.IntN(10000000))),
|
||||||
|
Kind: rand.IntN(10000),
|
||||||
|
Tags: tags,
|
||||||
|
Content: string(content),
|
||||||
|
Sig: generateRandomHex(128),
|
||||||
|
}
|
||||||
|
|
||||||
|
return event
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateAuthMessage() []byte {
|
||||||
|
if rand.IntN(2) == 0 {
|
||||||
|
challenge := fmt.Sprintf("challenge_%d", rand.IntN(1000000))
|
||||||
|
return []byte(fmt.Sprintf(`["AUTH","%s"]`, challenge))
|
||||||
|
} else {
|
||||||
|
event := generateRandomEvent()
|
||||||
|
eventJSON, _ := json.Marshal(event)
|
||||||
|
return []byte(fmt.Sprintf(`["AUTH",%s]`, string(eventJSON)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateNoticeMessage() []byte {
|
||||||
|
noticeLength := rand.IntN(100) + 5
|
||||||
|
notice := make([]byte, noticeLength)
|
||||||
|
for i := range notice {
|
||||||
|
notice[i] = byte('a' + rand.IntN(26))
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(fmt.Sprintf(`["NOTICE","%s"]`, string(notice)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateEOSEMessage() []byte {
|
||||||
|
subID := fmt.Sprintf("sub_%d", rand.IntN(1000))
|
||||||
|
return []byte(fmt.Sprintf(`["EOSE","%s"]`, subID))
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateOKMessage() []byte {
|
||||||
|
eventID := generateRandomHex(64)
|
||||||
|
success := rand.IntN(2) == 0
|
||||||
|
|
||||||
|
var reason string
|
||||||
|
if !success {
|
||||||
|
reasons := []string{
|
||||||
|
"blocked",
|
||||||
|
"rate-limited",
|
||||||
|
"invalid: signature verification failed",
|
||||||
|
"error: could not connect to the database",
|
||||||
|
"pow: difficulty too low",
|
||||||
|
}
|
||||||
|
reason = reasons[rand.IntN(len(reasons))]
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(fmt.Sprintf(`["OK","%s",%t,"%s"]`, eventID, success, reason))
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateCountMessage() []byte {
|
||||||
|
subID := fmt.Sprintf("sub_%d", rand.IntN(1000))
|
||||||
|
count := rand.IntN(10000)
|
||||||
|
|
||||||
|
if rand.IntN(5) == 0 {
|
||||||
|
hll := generateRandomHex(512)
|
||||||
|
return []byte(fmt.Sprintf(`["COUNT","%s",{"count":%d,"hll":"%s"}]`, subID, count, hll))
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(fmt.Sprintf(`["COUNT","%s",{"count":%d}]`, subID, count))
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateReqMessage() []byte {
|
||||||
|
subID := fmt.Sprintf("sub_%d", rand.IntN(1000))
|
||||||
|
|
||||||
|
filterCount := rand.IntN(3) + 1
|
||||||
|
filters := make([]string, filterCount)
|
||||||
|
|
||||||
|
for i := range filters {
|
||||||
|
filter := generateRandomFilter()
|
||||||
|
filterJSON, _ := json.Marshal(filter)
|
||||||
|
filters[i] = string(filterJSON)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := fmt.Sprintf(`["REQ","%s"`, subID)
|
||||||
|
for _, f := range filters {
|
||||||
|
result += "," + f
|
||||||
|
}
|
||||||
|
result += "]"
|
||||||
|
|
||||||
|
return []byte(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateRandomFilter() Filter {
|
||||||
|
filter := Filter{}
|
||||||
|
|
||||||
|
if rand.IntN(2) == 0 {
|
||||||
|
count := rand.IntN(5) + 1
|
||||||
|
filter.IDs = make([]string, count)
|
||||||
|
for i := range filter.IDs {
|
||||||
|
filter.IDs[i] = generateRandomHex(64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rand.IntN(2) == 0 {
|
||||||
|
count := rand.IntN(5) + 1
|
||||||
|
filter.Kinds = make([]int, count)
|
||||||
|
for i := range filter.Kinds {
|
||||||
|
filter.Kinds[i] = rand.IntN(10000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rand.IntN(2) == 0 {
|
||||||
|
count := rand.IntN(5) + 1
|
||||||
|
filter.Authors = make([]string, count)
|
||||||
|
for i := range filter.Authors {
|
||||||
|
filter.Authors[i] = generateRandomHex(64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rand.IntN(2) == 0 {
|
||||||
|
tagCount := rand.IntN(3) + 1
|
||||||
|
filter.Tags = make(TagMap)
|
||||||
|
|
||||||
|
for i := 0; i < tagCount; i++ {
|
||||||
|
tagName := string([]byte{byte('a' + rand.IntN(26))})
|
||||||
|
valueCount := rand.IntN(3) + 1
|
||||||
|
values := make([]string, valueCount)
|
||||||
|
|
||||||
|
for j := range values {
|
||||||
|
values[j] = fmt.Sprintf("tag_value_%d", rand.IntN(100))
|
||||||
|
}
|
||||||
|
|
||||||
|
filter.Tags[tagName] = values
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rand.IntN(2) == 0 {
|
||||||
|
ts := Timestamp(time.Now().Unix() - int64(rand.IntN(10000000)))
|
||||||
|
filter.Since = &ts
|
||||||
|
}
|
||||||
|
|
||||||
|
if rand.IntN(2) == 0 {
|
||||||
|
ts := Timestamp(time.Now().Unix() - int64(rand.IntN(1000000)))
|
||||||
|
filter.Until = &ts
|
||||||
|
}
|
||||||
|
|
||||||
|
if rand.IntN(2) == 0 {
|
||||||
|
filter.Limit = rand.IntN(100) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return filter
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateRandomHex(length int) string {
|
||||||
|
const hexChars = "0123456789abcdef"
|
||||||
|
result := make([]byte, length)
|
||||||
|
|
||||||
|
for i := range result {
|
||||||
|
result[i] = hexChars[rand.IntN(len(hexChars))]
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(result)
|
||||||
|
}
|
||||||
|
@ -321,8 +321,7 @@ func TestParseMessageSIMD(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
require.NotNil(t, envelope, "expected non-nil envelope but got nil")
|
require.NotNil(t, envelope, "expected non-nil envelope but got nil")
|
||||||
require.Equal(t, testCase.ExpectedEnvelope.Label(), envelope.Label())
|
require.Equal(t, testCase.ExpectedEnvelope, envelope)
|
||||||
require.Equal(t, testCase.ExpectedEnvelope.String(), envelope.String())
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user