Source file 
src/math/big/calibrate_graph.go
     1  
     2  
     3  
     4  
     5  
     6  
     7  
     8  
     9  
    10  
    11  
    12  
    13  
    14  
    15  
    16  
    17  package main
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/csv"
    22  	"flag"
    23  	"fmt"
    24  	"log"
    25  	"math"
    26  	"os"
    27  	"strconv"
    28  )
    29  
    30  func usage() {
    31  	fmt.Fprintf(os.Stderr, "usage: go run calibrate_graph.go file.csv >file.svg\n")
    32  	os.Exit(2)
    33  }
    34  
    35  
    36  type Point struct {
    37  	X, Y float64
    38  }
    39  
    40  
    41  type Graph struct {
    42  	Title   string    
    43  	Geomean []Point   
    44  	Lines   [][]Point 
    45  	XAxis   string    
    46  	YAxis   string    
    47  	Min     Point     
    48  	Max     Point     
    49  }
    50  
    51  var yMax = flag.Float64("ymax", 1.2, "maximum y axis value")
    52  var alphaNorm = flag.Float64("alphanorm", 0.1, "alpha for a single norm line")
    53  
    54  func main() {
    55  	flag.Usage = usage
    56  	flag.Parse()
    57  	if flag.NArg() != 1 {
    58  		usage()
    59  	}
    60  
    61  	
    62  	
    63  	
    64  	
    65  	
    66  	fdata, err := os.ReadFile(flag.Arg(0))
    67  	if err != nil {
    68  		log.Fatal(err)
    69  	}
    70  	if _, after, ok := bytes.Cut(fdata, []byte(".csv --\n")); ok {
    71  		fdata = after
    72  	}
    73  	if before, _, ok := bytes.Cut(fdata, []byte("-- eof --\n")); ok {
    74  		fdata = before
    75  	}
    76  	rd := csv.NewReader(bytes.NewReader(fdata))
    77  	rd.FieldsPerRecord = -1
    78  	records, err := rd.ReadAll()
    79  	if err != nil {
    80  		log.Fatal(err)
    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  	g := &Graph{
   108  		YAxis: "Relative Slowdown",
   109  		Min:   Point{0, 1},
   110  		Max:   Point{1, 1.2},
   111  	}
   112  	meta := make(map[string]string)
   113  	table := 0 
   114  	var thresholds []float64
   115  	maxNorm := 0.0
   116  	for _, rec := range records {
   117  		if len(rec) == 0 {
   118  			continue
   119  		}
   120  		if len(rec) == 2 {
   121  			meta[rec[0]] = rec[1]
   122  			continue
   123  		}
   124  		if rec[0] == `size \ threshold` {
   125  			table++
   126  			if table == 2 {
   127  				thresholds = parseFloats(rec)
   128  				g.Min.X = thresholds[0]
   129  				g.Max.X = thresholds[len(thresholds)-1]
   130  			}
   131  			continue
   132  		}
   133  		if rec[0] == "geomean" {
   134  			table = 3 
   135  			geomeans := parseFloats(rec)
   136  			g.Geomean = floatsToLine(thresholds, geomeans)
   137  			continue
   138  		}
   139  		if table == 2 {
   140  			if _, err := strconv.Atoi(rec[0]); err != nil { 
   141  				log.Fatalf("invalid table line: %q", rec)
   142  			}
   143  			norms := parseFloats(rec)
   144  			if len(norms) > len(thresholds) {
   145  				log.Fatalf("too many timings (%d > %d): %q", len(norms), len(thresholds), rec)
   146  			}
   147  			g.Lines = append(g.Lines, floatsToLine(thresholds, norms))
   148  			for _, y := range norms {
   149  				maxNorm = max(maxNorm, y)
   150  			}
   151  			continue
   152  		}
   153  	}
   154  
   155  	g.Max.Y = min(*yMax, math.Ceil(maxNorm*100)/100)
   156  	g.XAxis = meta["calibrate"] + "Threshold"
   157  	g.Title = meta["goos"] + "/" + meta["goarch"] + " " + meta["cpu"]
   158  
   159  	os.Stdout.Write(g.SVG())
   160  }
   161  
   162  
   163  
   164  func parseFloats(rec []string) []float64 {
   165  	floats := make([]float64, 0, len(rec)-1)
   166  	for _, v := range rec[1:] {
   167  		if v == "" {
   168  			floats = append(floats, math.Inf(+1))
   169  			continue
   170  		}
   171  		f, err := strconv.ParseFloat(v, 64)
   172  		if err != nil {
   173  			log.Fatalf("invalid record: %q (%v)", rec, err)
   174  		}
   175  		floats = append(floats, f)
   176  	}
   177  	return floats
   178  }
   179  
   180  
   181  func floatsToLine(x, y []float64) []Point {
   182  	var line []Point
   183  	for i, yi := range y {
   184  		if !math.IsInf(yi, 0) {
   185  			line = append(line, Point{x[i], yi})
   186  		}
   187  	}
   188  	return line
   189  }
   190  
   191  const svgHeader = `<svg width="%d" height="%d" version="1.1" xmlns="http://www.w3.org/2000/svg">
   192    <defs>
   193      <style type="text/css"><![CDATA[
   194        text { stroke-width: 0; white-space: pre; }
   195        text.hjc { text-anchor: middle; }
   196        text.hjl { text-anchor: start; }
   197        text.hjr { text-anchor: end; }
   198        .def { stroke-linecap: round; stroke-linejoin: round; fill: none; stroke: #000000; stroke-width: 1px; }
   199        .tick { stroke: #000000; fill: #000000; font: %dpx Times; }
   200        .title { stroke: #000000; fill: #000000; font: %dpx Times; font-weight: bold; }
   201        .axis { stroke-width: 2px; }
   202        .norm { stroke: rgba(0,0,0,%f); }
   203        .geomean { stroke: #6666ff; stroke-width: 2px; }
   204      ]]></style>
   205    </defs>
   206    <g class="def">
   207  `
   208  
   209  
   210  const (
   211  	DX   = 600          
   212  	DY   = 150          
   213  	ML   = 80           
   214  	MT   = 30           
   215  	MR   = 10           
   216  	MB   = 50           
   217  	PS   = 14           
   218  	W    = ML + DX + MR 
   219  	H    = MT + DY + MB 
   220  	Tick = 5            
   221  )
   222  
   223  
   224  
   225  type SVGPoint struct {
   226  	X, Y int
   227  }
   228  
   229  func (p SVGPoint) String() string {
   230  	return fmt.Sprintf("%d,%d", p.X, p.Y)
   231  }
   232  
   233  
   234  func (g *Graph) pt(x, y float64) SVGPoint {
   235  	return SVGPoint{
   236  		X: ML + int((x-g.Min.X)/(g.Max.X-g.Min.X)*DX),
   237  		Y: H - MB - int((y-g.Min.Y)/(g.Max.Y-g.Min.Y)*DY),
   238  	}
   239  }
   240  
   241  
   242  func (g *Graph) SVG() []byte {
   243  
   244  	var svg bytes.Buffer
   245  	fmt.Fprintf(&svg, svgHeader, W, H, PS, PS, *alphaNorm)
   246  
   247  	
   248  	fmt.Fprintf(&svg, "<clipPath id=\"cp\"><path d=\"M %v L %v L %v L %v Z\" /></clipPath>\n",
   249  		g.pt(g.Min.X, g.Min.Y), g.pt(g.Max.X, g.Min.Y), g.pt(g.Max.X, g.Max.Y), g.pt(g.Min.X, g.Max.Y))
   250  	fmt.Fprintf(&svg, "<g clip-path=\"url(#cp)\">\n")
   251  	for _, line := range g.Lines {
   252  		if len(line) == 0 {
   253  			continue
   254  		}
   255  		fmt.Fprintf(&svg, "<path class=\"norm\" d=\"M %v", g.pt(line[0].X, line[0].Y))
   256  		for _, v := range line[1:] {
   257  			fmt.Fprintf(&svg, " L %v", g.pt(v.X, v.Y))
   258  		}
   259  		fmt.Fprintf(&svg, "\"/>\n")
   260  	}
   261  	
   262  	if len(g.Geomean) > 0 {
   263  		line := g.Geomean
   264  		fmt.Fprintf(&svg, "<path class=\"geomean\" d=\"M %v", g.pt(line[0].X, line[0].Y))
   265  		for _, v := range line[1:] {
   266  			fmt.Fprintf(&svg, " L %v", g.pt(v.X, v.Y))
   267  		}
   268  		fmt.Fprintf(&svg, "\"/>\n")
   269  	}
   270  	fmt.Fprintf(&svg, "</g>\n")
   271  
   272  	
   273  	fmt.Fprintf(&svg, "<path class=\"axis\" d=\"")
   274  	fmt.Fprintf(&svg, " M %v L %v", g.pt(g.Min.X, g.Min.Y), g.pt(g.Max.X, g.Min.Y)) 
   275  	fmt.Fprintf(&svg, " M %v L %v", g.pt(g.Min.X, g.Min.Y), g.pt(g.Min.X, g.Max.Y)) 
   276  	xscale := 10.0
   277  	if g.Max.X-g.Min.X < 100 {
   278  		xscale = 1.0
   279  	}
   280  	for x := int(math.Ceil(g.Min.X / xscale)); float64(x)*xscale <= g.Max.X; x++ {
   281  		if x%5 != 0 {
   282  			fmt.Fprintf(&svg, " M %v l 0,%d", g.pt(float64(x)*xscale, g.Min.Y), Tick)
   283  		} else {
   284  			fmt.Fprintf(&svg, " M %v l 0,%d", g.pt(float64(x)*xscale, g.Min.Y), 2*Tick)
   285  		}
   286  	}
   287  	yscale := 100.0
   288  	if g.Max.Y-g.Min.Y > 0.5 {
   289  		yscale = 10
   290  	}
   291  	for y := int(math.Ceil(g.Min.Y * yscale)); float64(y) <= g.Max.Y*yscale; y++ {
   292  		if y%5 != 0 {
   293  			fmt.Fprintf(&svg, " M %v l -%d,0", g.pt(g.Min.X, float64(y)/yscale), Tick)
   294  		} else {
   295  			fmt.Fprintf(&svg, " M %v l -%d,0", g.pt(g.Min.X, float64(y)/yscale), 2*Tick)
   296  		}
   297  	}
   298  	fmt.Fprintf(&svg, "\"/>\n")
   299  
   300  	
   301  	for x := int(math.Ceil(g.Min.X / xscale)); float64(x)*xscale <= g.Max.X; x++ {
   302  		if x%5 == 0 {
   303  			p := g.pt(float64(x)*xscale, g.Min.Y)
   304  			fmt.Fprintf(&svg, "<text x=\"%d\" y=\"%d\" class=\"tick hjc\">%d</text>\n", p.X, p.Y+2*Tick+PS, x*int(xscale))
   305  		}
   306  	}
   307  	for y := int(math.Ceil(g.Min.Y * yscale)); float64(y) <= g.Max.Y*yscale; y++ {
   308  		if y%5 == 0 {
   309  			p := g.pt(g.Min.X, float64(y)/yscale)
   310  			fmt.Fprintf(&svg, "<text x=\"%d\" y=\"%d\" class=\"tick hjr\">%.2f</text>\n", p.X-2*Tick-Tick, p.Y+PS/3, float64(y)/yscale)
   311  		}
   312  	}
   313  
   314  	
   315  	fmt.Fprintf(&svg, "<text x=\"%d\" y=\"%d\" class=\"title hjc\">%s</text>\n", ML+DX/2, MT-PS/3, g.Title)
   316  	fmt.Fprintf(&svg, "<text x=\"%d\" y=\"%d\" class=\"title hjc\">%s</text>\n", ML+DX/2, MT+DY+2*Tick+2*PS+PS/2, g.XAxis)
   317  	fmt.Fprintf(&svg, "<g transform=\"translate(%d,%d) rotate(-90)\"><text x=\"0\" y=\"0\" class=\"title hjc\">%s</text></g>\n", ML-Tick-Tick-3*PS, MT+DY/2, g.YAxis)
   318  
   319  	fmt.Fprintf(&svg, "</g></svg>\n")
   320  	return svg.Bytes()
   321  }
   322  
View as plain text