Skip to content

Commit 55d0342

Browse files
committed
first time born now here
1 parent b513afb commit 55d0342

File tree

5 files changed

+269
-0
lines changed

5 files changed

+269
-0
lines changed

format.go

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
)
7+
8+
// graphData translate bench results to Google graph JSON structure
9+
func graphData(benchResults BenchNameSet, oBenchNames, oBenchArgs stringList) []byte {
10+
data := new(bytes.Buffer)
11+
12+
data.WriteString("[")
13+
sep := ""
14+
data.WriteString("[\"Argument\"")
15+
for _, oName := range oBenchNames {
16+
sep = ","
17+
data.WriteString(fmt.Sprintf("%s\"%s\"", sep, oName))
18+
}
19+
data.WriteString("]")
20+
21+
lsep := ""
22+
for _, oArg := range oBenchArgs {
23+
lsep = ","
24+
data.WriteString(fmt.Sprintf("%s[\"%s\"", lsep, oArg))
25+
sep := ""
26+
for _, oName := range oBenchNames {
27+
sep = ","
28+
data.WriteString(fmt.Sprintf("%s%.2f", sep, benchResults[oName][oArg]))
29+
}
30+
data.WriteString("]")
31+
}
32+
data.WriteString("]")
33+
34+
return data.Bytes()
35+
}

list.go

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
)
7+
8+
type stringList []string
9+
10+
func (list *stringList) String() string {
11+
return fmt.Sprint(*list)
12+
}
13+
14+
func (list *stringList) Set(value string) error {
15+
for _, elem := range strings.Split(value, ",") {
16+
list.Add(elem)
17+
}
18+
return nil
19+
}
20+
21+
func (list *stringList) Add(value string) error {
22+
*list = append(*list, value)
23+
return nil
24+
}
25+
26+
func (list *stringList) Len() int {
27+
return len(*list)
28+
}
29+
30+
func (list *stringList) stringInList(a string) bool {
31+
for _, b := range *list {
32+
if b == a {
33+
return true
34+
}
35+
}
36+
return false
37+
}

main.go

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package main
2+
3+
import (
4+
"bufio"
5+
"errors"
6+
"flag"
7+
"fmt"
8+
"io/ioutil"
9+
"net/http"
10+
"net/url"
11+
"os"
12+
13+
"github.com/fatih/color"
14+
"golang.org/x/tools/benchmark/parse"
15+
)
16+
17+
// uploadData sends data to server and expects graph url.
18+
func uploadData(apiUrl, data, title string) (string, error) {
19+
20+
resp, err := http.PostForm(apiUrl, url.Values{"data": {data}, "title": {title}})
21+
if err != nil {
22+
return "", err
23+
}
24+
defer resp.Body.Close()
25+
26+
body, err := ioutil.ReadAll(resp.Body)
27+
if err != nil {
28+
return "", err
29+
}
30+
31+
if resp.StatusCode != 200 {
32+
return "", errors.New("Server din't return graph URL")
33+
}
34+
35+
return string(body), nil
36+
}
37+
38+
func main() {
39+
40+
var oBenchNames, oBenchArgs stringList
41+
42+
// graph elements will be ordered as in benchmark output by default - unless the order was specified here
43+
flag.Var(&oBenchNames, "obn", "comma-separated list of benchmark names")
44+
flag.Var(&oBenchArgs, "oba", "comma-separated list of benchmark arguments")
45+
title := flag.String("title", "Graph: Benchmark results in ns/op", "title of a graph")
46+
apiUrl := flag.String("apiurl", "http://benchgraph.codingberg.com", "url to server api")
47+
flag.Parse()
48+
49+
var skipBenchNamesParsing, skipBenchArgsParsing bool
50+
51+
if oBenchNames.Len() > 0 {
52+
skipBenchNamesParsing = true
53+
}
54+
if oBenchArgs.Len() > 0 {
55+
skipBenchArgsParsing = true
56+
}
57+
58+
benchResults := make(BenchNameSet)
59+
60+
// parse Golang benchmark results, line by line
61+
scan := bufio.NewScanner(os.Stdin)
62+
green := color.New(color.FgGreen).SprintfFunc()
63+
red := color.New(color.FgRed).SprintFunc()
64+
for scan.Scan() {
65+
line := scan.Text()
66+
67+
mark := green("√")
68+
69+
b, err := parse.ParseLine(line)
70+
if err != nil {
71+
mark = red("?")
72+
}
73+
74+
// read bench name and arguments
75+
if b != nil {
76+
name, arg, _, err := parseNameArgThread(b.Name)
77+
if err != nil {
78+
mark = red("!")
79+
fmt.Printf("%s %s\n", mark, line)
80+
continue
81+
}
82+
83+
if !skipBenchNamesParsing && !oBenchNames.stringInList(name) {
84+
oBenchNames.Add(name)
85+
}
86+
87+
if !skipBenchArgsParsing && !oBenchArgs.stringInList(arg) {
88+
oBenchArgs.Add(arg)
89+
}
90+
91+
if _, ok := benchResults[name]; !ok {
92+
benchResults[name] = make(BenchArgSet)
93+
}
94+
95+
benchResults[name][arg] = b.NsPerOp
96+
}
97+
98+
fmt.Printf("%s %s\n", mark, line)
99+
}
100+
101+
if err := scan.Err(); err != nil {
102+
fmt.Fprintf(os.Stderr, "reading standard input: %v", err)
103+
os.Exit(1)
104+
}
105+
106+
if len(benchResults) == 0 {
107+
fmt.Fprintf(os.Stderr, "no data to show.\n\n")
108+
os.Exit(1)
109+
}
110+
111+
fmt.Println()
112+
fmt.Println("Waiting for server response ...")
113+
114+
data := graphData(benchResults, oBenchNames, oBenchArgs)
115+
116+
graphUrl, err := uploadData(*apiUrl, string(data), *title)
117+
if err != nil {
118+
fmt.Fprintf(os.Stderr, "uploading data: %v", err)
119+
os.Exit(1)
120+
}
121+
122+
fmt.Println("=========================================")
123+
fmt.Println()
124+
fmt.Println(graphUrl)
125+
fmt.Println()
126+
fmt.Println("=========================================")
127+
128+
}

main_test.go

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package main
2+
3+
import (
4+
"testing"
5+
6+
"golang.org/x/tools/benchmark/parse"
7+
)
8+
9+
var bTests = []struct {
10+
line string // input
11+
name string // expected result
12+
arg string
13+
nsperop float64
14+
}{
15+
{"BenchmarkF2_F0000000-4 50000000 29.4 ns/op", "F2", "F0000000", 29.4},
16+
{"BenchmarkF0_FF-2 10000000 37.4 ns/op", "F0", "FF", 37.4},
17+
{"BenchmarkF_0-2 40000000 11.2 ns/op", "F", "0", 11.2},
18+
}
19+
20+
func TestParser(t *testing.T) {
21+
for _, tt := range bTests {
22+
b, _ := parse.ParseLine(tt.line)
23+
name, arg, _, _ := parseNameArgThread(b.Name)
24+
if name != tt.name {
25+
t.Errorf("parseNameArgThread(%s): expected %s, actual %s", b.Name, tt.name, name)
26+
}
27+
if arg != tt.arg {
28+
t.Errorf("parseNameArgThread(%s): expected %s, actual %s", b.Name, tt.arg, arg)
29+
}
30+
if b.NsPerOp != tt.nsperop {
31+
t.Errorf("parseNameArgThread(%s): expected %f, actual %f", b.Name, tt.nsperop, b.NsPerOp)
32+
}
33+
}
34+
}

parse.go

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package main
2+
3+
import (
4+
"errors"
5+
"regexp"
6+
"strconv"
7+
)
8+
9+
// Coder should use following naming convention for Benchmark functions
10+
// Naming convention: Benchmark[Function_name]_[Function_argument](b *testing.B)
11+
var re *regexp.Regexp = regexp.MustCompile(`Benchmark([a-zA-Z0-9]+)_([_a-zA-Z0-9]+)-([0-9]+)$`)
12+
13+
// Storage for Func(Arg)=Result relations
14+
type BenchArgSet map[string]float64
15+
type BenchNameSet map[string]BenchArgSet
16+
17+
// parseNameArgThread parses function name, argument and number of threads from benchmark output.
18+
func parseNameArgThread(line string) (name string, arg string, c int, err error) {
19+
20+
arr := re.FindStringSubmatch(line)
21+
22+
// we expect 4 columns
23+
if len(arr) != 4 {
24+
return "", "", 0, errors.New("Can't parse benchmark result")
25+
}
26+
27+
name, arg = arr[1], arr[2]
28+
29+
c, err = strconv.Atoi(arr[3])
30+
if err != nil {
31+
return "", "", 0, errors.New("Can't parse benchmark result")
32+
}
33+
34+
return name, arg, c, nil
35+
}

0 commit comments

Comments
 (0)