Skip to content

Instantly share code, notes, and snippets.

@chewxy
Created February 19, 2019 21:31
Show Gist options
  • Save chewxy/6d20f889b2977ebf8c9cbed11a504830 to your computer and use it in GitHub Desktop.
Save chewxy/6d20f889b2977ebf8c9cbed11a504830 to your computer and use it in GitHub Desktop.
package main
import (
"encoding/csv"
"image/color"
"os"
"sort"
"strconv"
"gonum.org/v1/plot"
"gonum.org/v1/plot/plotter"
"gonum.org/v1/plot/vg"
"gonum.org/v1/plot/vg/draw"
"gonum.org/v1/plot/vg/vgimg"
"gorgonia.org/tensor"
"gorgonia.org/tensor/native"
)
func main() {
sizeCPU := func(a Bench) (float64, float64) { return a.Size, a.CPU }
// median := func(a []float64) float64 { return ntile(a, 0.5) }
all := parse(ingest("data.csv"))
list := []string{
"gcc", "ocaml", "node",
"java", "go", "ghc",
"rust", "julia", "swift",
}
ps := make([]*plot.Plot, 0, len(list))
for _, l := range list {
lang := filter(all, func(a Bench) bool { return a.Language == l })
lang = argminByName(lang, func(a Bench) float64 { return a.CPU })
p := plotAll(all, sizeCPU)
p.BackgroundColor = color.RGBA{R: 255}
p.X.Max = 2000
p.Y.Max = 150
p.Title.Text = l
plotStar(p, lang, sizeCPU, mean)
ps = append(ps, p)
}
// Draw plots in a tiled fashion
cols := 3
t := tensor.New(tensor.WithBacking(ps), tensor.WithShape(len(list)/cols, cols))
matUgh, err := native.Matrix(t)
dieIfErr(err)
mat := matUgh.([][]*plot.Plot)
tiles := draw.Tiles{Rows: t.Shape()[0], Cols: t.Shape()[1]}
img := vgimg.New(60*vg.Centimeter, 60*vg.Centimeter)
canvas := draw.New(img)
mini := plot.Align(mat, tiles, canvas)
for i := 0; i < tiles.Rows; i++ {
for j := 0; j < tiles.Cols; j++ {
mat[i][j].Draw(mini[i][j])
}
}
w, err := os.Create("gb.png")
dieIfErr(err)
png := vgimg.PngCanvas{Canvas: img}
png.WriteTo(w)
}
func ingest(filename string) [][]string {
f, err := os.Open(filename)
dieIfErr(err)
r := csv.NewReader(f)
records, err := r.ReadAll()
dieIfErr(err)
return records
}
type Bench struct {
Name string
Language string
Size float64
CPU, Mem float64
}
func parse(recs [][]string) []Bench {
retVal := make([]Bench, 0, len(recs))
for i, r := range recs {
if i == 0 {
continue
}
size, err := strconv.ParseFloat(r[4], 64)
dieIfErr(err)
cpu, err := strconv.ParseFloat(r[5], 64)
dieIfErr(err)
mem, err := strconv.ParseFloat(r[6], 64)
dieIfErr(err)
b := Bench{
Name: r[0],
Language: r[1],
Size: size,
CPU: cpu,
Mem: mem,
}
retVal = append(retVal, b)
}
return retVal
}
// filter filters a list of Bench according to the criteria given in f
func filter(a []Bench, f func(a Bench) bool) (retVal []Bench) {
for i := range a {
if f(a[i]) {
retVal = append(retVal, a[i])
}
}
return retVal
}
// reduce2 reduces a pair of numbers given by f, using the reduction function g
func reduce2(a []Bench, f func(a Bench) (float64, float64), g func([]float64) float64) (float64, float64) {
xs, ys := make([]float64, 0, len(a)), make([]float64, 0, len(a))
for i := range a {
x, y := f(a[i])
xs = append(xs, x)
ys = append(ys, y)
}
return g(xs), g(ys)
}
func argminByName(a []Bench, f func(a Bench) float64) (retVal []Bench) {
m := make(map[string]Bench)
for _, b := range a {
if v, ok := m[b.Name]; ok {
if f(b) < f(v) {
m[b.Name] = b
}
continue
}
m[b.Name] = b
}
for _, v := range m {
retVal = append(retVal, v)
}
return retVal
}
// makeXYs extracts pairs of desired numbers given by f
func makeXYs(a []Bench, f func(a Bench) (float64, float64)) plotter.XYs {
retVal := make(plotter.XYs, len(a))
for i := range a {
x, y := f(a[i])
retVal[i].X = x
retVal[i].Y = y
}
return retVal
}
// plotAll plots all the points of the given []Bench
func plotAll(a []Bench, f func(a Bench) (float64, float64)) *plot.Plot {
p, err := plot.New()
dieIfErr(err)
s, err := plotter.NewScatter(makeXYs(a, f))
dieIfErr(err)
p.Add(s)
return p
}
// plotstar adds a line star thing into the plot
func plotStar(p *plot.Plot, a []Bench, f func(a Bench) (float64, float64), g func([]float64) float64) {
s := new(star)
s.XYs = makeXYs(a, f)
s.mx, s.my = reduce2(a, f, g)
s.trx, s.try = p.X.Max, p.Y.Max
s.LineStyle = plotter.DefaultLineStyle
s.LineStyle.Color = color.RGBA{R: 255, A: 255}
p.Add(s)
}
// star is a data structure used for plotting line stars
type star struct {
plotter.XYs
draw.LineStyle
mx, my float64
trx, try float64 // truncate at
}
func (s *star) Plot(c draw.Canvas, p *plot.Plot) {
tx, ty := p.Transforms(&c)
trx, try := tx(s.trx), ty(s.try)
ls := s.LineStyle
mx, my := tx(s.mx), ty(s.my)
for _, xy := range s.XYs {
x := tx(xy.X)
y := ty(xy.Y)
if x > trx {
x = trx
}
if y > try {
y = try
}
c.StrokeLine2(ls, mx, my, x, y)
}
}
func dieIfErr(err error) {
if err != nil {
panic(err)
}
}
func mean(a []float64) float64 {
var s float64
for _, v := range a {
s += v
}
return s / float64(len(a))
}
func ntile(a []float64, p float64) float64 {
sort.Float64s(a)
at := int(float64(len(a)) * p)
return a[at]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment