1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
|
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
// Package main implements benchmarks for the STUN package
package main
import (
"context"
"crypto/rand"
"errors"
"flag"
"log"
mathRand "math/rand"
"os"
"os/signal"
"runtime"
"runtime/pprof"
"sync/atomic"
"time"
"github.com/pion/stun"
)
var (
workers = flag.Int("w", runtime.GOMAXPROCS(0), "concurrent workers") //nolint:gochecknoglobals
uriStr = flag.String("uri", "stun:localhost:3478", "URI of STUN server") //nolint:gochecknoglobals
duration = flag.Duration("d", time.Minute, "benchmark duration") //nolint:gochecknoglobals
cpuProfile = flag.String("cpuprofile", "", "file output of pprof cpu profile") //nolint:gochecknoglobals
memProfile = flag.String("memprofile", "", "file output of pprof memory profile") //nolint:gochecknoglobals
realRand = flag.Bool("crypt", false, "use crypto/rand as random source") //nolint:gochecknoglobals
)
func main() { //nolint:gocognit
flag.Parse()
uri, err := stun.ParseURI(*uriStr)
if err != nil {
log.Fatalf("Failed to parse URI '%s': %s", *uriStr, err)
}
signals := make(chan os.Signal, 1)
signal.Notify(signals, os.Interrupt)
start := time.Now()
var (
request int64
requestOK int64
requestErr int64
)
if *cpuProfile != "" {
f, createErr := os.Create(*cpuProfile)
if createErr != nil {
log.Fatalf("Failed to create CPU profile output file: %s", createErr)
}
if pprofErr := pprof.StartCPUProfile(f); pprofErr != nil {
log.Fatalf("Failed to start pprof CPU profiling: %s", pprofErr)
}
defer func() {
pprof.StopCPUProfile()
if closeErr := f.Close(); closeErr != nil {
log.Printf("Failed to close CPU profile output file: %s", closeErr)
} else {
log.Printf("Saved cpu profile to: %s", *cpuProfile)
}
}()
}
if *memProfile != "" {
f, createErr := os.Create(*memProfile)
if createErr != nil {
log.Panicf("Failed to create memory profile output file: %s", createErr)
}
defer func() {
if pprofErr := pprof.Lookup("heap").WriteTo(f, 1); pprofErr != nil {
log.Fatalf("Failed to write pprof memory profiling: %s", pprofErr)
}
if closeErr := f.Close(); closeErr != nil {
log.Printf("Failed to close memory profile output file: %s", closeErr)
} else {
log.Printf("Saved memory profile to %s", *memProfile)
}
}()
}
ctx, cancel := context.WithTimeout(context.Background(), *duration)
go func() {
for sig := range signals {
log.Printf("Stopping on %s", sig)
cancel()
}
}()
if *realRand {
log.Print("Using crypto/rand as random source for transaction id")
}
for i := 0; i < *workers; i++ {
c, clientErr := stun.DialURI(uri, &stun.DialConfig{})
if clientErr != nil {
log.Panicf("Failed to create client: %s", clientErr)
}
go func() {
req := stun.New()
for {
if *realRand {
if _, err := rand.Read(req.TransactionID[:]); err != nil { //nolint:gosec
log.Fatalf("Failed to generate transaction ID: %s", err)
}
} else {
mathRand.Read(req.TransactionID[:]) //nolint:gosec
}
req.Type = stun.BindingRequest
req.WriteHeader()
atomic.AddInt64(&request, 1)
if doErr := c.Do(req, func(event stun.Event) {
if event.Error != nil {
if !errors.Is(event.Error, stun.ErrTransactionTimeOut) {
log.Printf("Failed STUN transaction: %s", event.Error)
}
atomic.AddInt64(&requestErr, 1)
return
}
atomic.AddInt64(&requestOK, 1)
}); doErr != nil {
if !errors.Is(doErr, stun.ErrTransactionExists) {
log.Printf("Failed STUN transaction: %s", doErr)
}
atomic.AddInt64(&requestErr, 1)
}
}
}()
}
log.Print("Workers started")
<-ctx.Done()
stop := time.Now()
rps := int(float64(atomic.LoadInt64(&requestOK)) / stop.Sub(start).Seconds())
log.Printf("RPS: %v", rps)
if reqErr := atomic.LoadInt64(&requestErr); requestErr != 0 {
log.Printf("Errors: %d", reqErr)
}
log.Printf("Total: %d", atomic.LoadInt64(&request))
}
|