@@ -12,6 +12,7 @@ import (
1212 "encoding/json"
1313 "fmt"
1414 "os"
15+ "path/filepath"
1516 "regexp"
1617 "strings"
1718 "time"
@@ -29,6 +30,8 @@ import (
2930 "github.com/cockroachdb/cockroach/pkg/util/timeutil"
3031 "github.com/cockroachdb/errors"
3132 "github.com/stretchr/testify/require"
33+ "golang.org/x/text/cases"
34+ "golang.org/x/text/language"
3235)
3336
3437type sysbenchWorkload int
@@ -239,7 +242,22 @@ func runSysbench(ctx context.Context, t test.Test, c cluster.Cluster, opts sysbe
239242 return errors .Errorf ("no SQL statistics found in sysbench output:\n %s" , result .Stdout )
240243 }
241244 t .L ().Printf ("sysbench results:\n %s" , result .Stdout [idx :])
242- return exportSysbenchResults (t , c , result .Stdout , start , opts )
245+
246+ if err := exportSysbenchResults (t , c , result .Stdout , start , opts ); err != nil {
247+ return err
248+ }
249+
250+ // Also produce standard Go benchmark output. This can be used to run
251+ // benchstat comparisons.
252+ goBenchOutput , err := sysbenchToGoBench (t .Name (), result .Stdout [idx :])
253+ if err != nil {
254+ return err
255+ }
256+ if err := os .WriteFile (filepath .Join (t .ArtifactsDir (), "bench.txt" ), []byte (goBenchOutput ), 0666 ); err != nil {
257+ return err
258+ }
259+
260+ return nil
243261 }
244262 if opts .usePostgres {
245263 if err := runWorkload (ctx ); err != nil {
@@ -515,3 +533,78 @@ func detectSysbenchCrash(result install.RunResultDetails) (string, bool) {
515533 }
516534 return "" , false
517535}
536+
537+ // sysbenchToGoBench converts sysbench output into Go benchmark format.
538+ func sysbenchToGoBench (name string , result string ) (string , error ) {
539+ // Extract key metrics from sysbench output using regex patterns.
540+ var qps , tps string
541+ var minLat , avgLat , p95Lat , maxLat string
542+
543+ // Parse transactions per second.
544+ m := regexp .MustCompile (`transactions:\s+\d+\s+\(([\d.]+)\s+per sec` ).FindStringSubmatch (result )
545+ if len (m ) <= 1 {
546+ return "" , errors .New ("failed to parse transactions per second" )
547+ }
548+ tps = m [1 ]
549+
550+ // Parse queries per second.
551+ m = regexp .MustCompile (`queries:\s+\d+\s+\(([\d.]+)\s+per sec` ).FindStringSubmatch (result )
552+ if len (m ) <= 1 {
553+ return "" , errors .New ("failed to parse queries per second" )
554+ }
555+ qps = m [1 ]
556+
557+ // Parse each latency metric using a loop.
558+ metrics := map [string ]* string {
559+ "min" : & minLat ,
560+ "avg" : & avgLat ,
561+ "max" : & maxLat ,
562+ "95th percentile" : & p95Lat ,
563+ }
564+ for metric , ptr := range metrics {
565+ pattern := fmt .Sprintf (`%s:\s+([\d.]+)` , metric )
566+ m = regexp .MustCompile (pattern ).FindStringSubmatch (result )
567+ if len (m ) <= 1 {
568+ return "" , errors .Newf ("failed to parse %s latency" , metric )
569+ }
570+ * ptr = m [1 ]
571+ }
572+
573+ // Process the test name.
574+ parts := strings .Split (name , "/" )
575+ if len (parts ) == 0 {
576+ return "" , errors .New ("empty test name" )
577+ }
578+
579+ // Normalize first segment (e.g. "sysbench-settings" -> "SysbenchSettings").
580+ firstPart := parts [0 ]
581+ // Split on non-alphanumeric characters.
582+ words := regexp .MustCompile (`[^a-zA-Z0-9]+` ).Split (firstPart , - 1 )
583+ // Capitalize each word and join them.
584+ var sb strings.Builder
585+ for _ , word := range words {
586+ if word == "" {
587+ continue
588+ }
589+ sb .WriteString (cases .Title (language .Und ).String (strings .ToLower (word )))
590+ }
591+ firstPart = sb .String ()
592+
593+ // Build the benchmark name.
594+ benchName := "Benchmark" + firstPart
595+
596+ // Add remaining parts, using auto-assigned keys only for parts without keys.
597+ nextKey := 'a'
598+ for _ , part := range parts [1 :] {
599+ if strings .Contains (part , "=" ) {
600+ benchName += "/" + part
601+ } else {
602+ benchName += fmt .Sprintf ("/%s=%s" , string (nextKey ), part )
603+ nextKey ++
604+ }
605+ }
606+
607+ // Return formatted benchmark string with all metrics.
608+ return fmt .Sprintf ("%s\t 1\t %s queries/sec\t %s txns/sec\t %s ms/min\t %s ms/avg\t %s ms/p95\t %s ms/max" ,
609+ benchName , qps , tps , minLat , avgLat , p95Lat , maxLat ), nil
610+ }
0 commit comments