mirror of
https://github.com/pion/stun.git
synced 2025-09-26 20:01:18 +08:00
137 lines
4.0 KiB
Go
137 lines
4.0 KiB
Go
// 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/v3"
|
|
)
|
|
|
|
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,cyclop
|
|
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++ {
|
|
client, 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
|
|
}
|
|
req.Type = stun.BindingRequest
|
|
req.WriteHeader()
|
|
atomic.AddInt64(&request, 1)
|
|
if doErr := client.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))
|
|
}
|