Skip to content

Instantly share code, notes, and snippets.

@petrhosek
Last active May 11, 2016 19:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save petrhosek/6848a2a56bc5cbbb1ac637e2f6411b4c to your computer and use it in GitHub Desktop.
Save petrhosek/6848a2a56bc5cbbb1ac637e2f6411b4c to your computer and use it in GitHub Desktop.
LLVM MC bundle emission memory usage
#!/usr/bin/gnuplot
set xlabel "Input size"
set ylabel "Output size"
set terminal dumb size 200,80
plot 'test.dat' using 1:2 with lines
package main
import (
"bufio"
"container/list"
"flag"
"fmt"
"io/ioutil"
"math/rand"
"log"
"os"
"os/exec"
"os/signal"
"path"
"regexp"
"runtime"
"strconv"
"sync"
)
type point [2]int
type polygon [4]point
var (
funcs int
ops int
llvmPath string
jobs int
)
func generate_memory_test(filename string, funcs int, ops int) {
file, err := os.Create(filename)
if err != nil {
panic(err)
}
defer func() {
if err := file.Close(); err != nil {
panic(err)
}
}()
writer := bufio.NewWriter(file)
for i := 1; i < funcs; i++ {
fmt.Fprintf(writer, "void func_%v(int *p);\n", i)
}
fmt.Fprintln(writer)
for i := 1; i < funcs; i++ {
fmt.Fprintf(writer, "void func_%v(int *p) {\n", i)
for j := 0; j < ops; j++ {
fmt.Fprintf(writer, " p[%v] = p[%v];\n", rand.Intn(ops), rand.Intn(ops))
}
fmt.Fprintf(writer, "}\n\n")
}
fmt.Fprintln(writer, "int main(int argc, const char *argv[]) {")
for i := 1; i < funcs; i++ {
fmt.Fprintf(writer, " func_%v((void *)0x%v);\n", i, rand.Intn(ops))
}
fmt.Fprintln(writer, "}")
writer.Flush()
}
func generate_test(filename string, funcs int, ops int) {
file, err := os.Create(filename)
if err != nil {
panic(err)
}
defer func() {
if err := file.Close(); err != nil {
panic(err)
}
}()
writer := bufio.NewWriter(file)
for i := 0; i < funcs; i++ {
fmt.Fprintf(writer, "void func_%v(void);\n", i)
}
fmt.Fprintln(writer)
for i := 0; i < funcs; i++ {
fmt.Fprintf(writer, "void func_%v(void) {\n", i)
k := 0
for j := 0; k < ops; j++ {
if i != j % funcs {
fmt.Fprintf(writer, " func_%v();\n", j % funcs)
k++
}
}
fmt.Fprintf(writer, "}\n\n")
}
fmt.Fprintln(writer, "int main(int argc, const char *argv[]) {")
for i := 0; i < funcs; i++ {
fmt.Fprintf(writer, " func_%v();\n", i)
}
fmt.Fprintln(writer, "}")
writer.Flush()
}
func run_test(dir string, n int, m int) {
src := fmt.Sprintf("%v/test-%v-%v.c", dir, n, m)
asm := fmt.Sprintf("%v/test-%v-%v.s", dir, n, m)
obj := fmt.Sprintf("%v/test-%v-%v.o", dir, n, m)
generate_memory_test(src, n, m)
defer os.Remove(src)
cmd := exec.Command(path.Join(llvmPath, "clang"), src, "-S", "-o", asm)
if err := cmd.Run(); err != nil {
log.Printf("# %v %v clang error: %v\n", n, m, err)
return
}
defer os.Remove(asm)
data, err := ioutil.ReadFile(asm)
if err != nil {
panic(err)
}
str := "\t.bundle_align_mode 4\n" + string(data)
ioutil.WriteFile(asm, []byte(str), 0640)
if err != nil {
panic(err)
}
cmd = exec.Command("/usr/bin/time", "-f", "\"rsize %M\"", path.Join(llvmPath, "llvm-mc"), asm, "-filetype=obj", "-o", obj)
output, err := cmd.CombinedOutput()
if err != nil {
log.Printf("# %v %v llvm-mc error: %v\n", n, m, err)
return
}
defer os.Remove(obj)
rsize := 0
re := regexp.MustCompile("rsize ([0-9]+)")
for _, match := range re.FindAllStringSubmatch(string(output), -1) {
size, err := strconv.Atoi(match[1])
if err != nil {
continue
}
if size > rsize {
rsize = size
}
}
file, err := os.Open(obj)
if err != nil {
panic(err)
}
defer func() {
if err := file.Close(); err != nil {
panic(err)
}
}()
info, err := file.Stat()
if err != nil {
panic(err)
}
fmt.Printf("%v %v # %v %v\n", info.Size(), rsize, n, m)
}
func work(poly polygon, ch chan point) {
var queue list.List
queue.PushBack(poly)
for e := queue.Front(); e != nil; e = queue.Front() {
p := e.Value.(polygon)
if p[0][0] != p[2][0] && p[0][1] != p[2][1] {
p1 := point{(p[0][0] + p[1][0]) / 2, p[0][1]}
p2 := point{p[1][0], (p[1][1] + p[2][1]) / 2}
p3 := point{(p[2][0] + p[3][0]) / 2, p[2][1]}
p4 := point{p[3][0], (p[3][1] + p[0][1]) / 2}
p5 := point{(p[0][0] + p[2][0]) / 2, (p[0][1] + p[2][1]) / 2}
if (p5[0] != p[0][0] && p5[1] != p[0][1]) &&
(p5[0] != p[2][0] && p5[1] != p[2][1]) {
ch <- p5
queue.PushBack(polygon{p[0], p1, p5, p4})
queue.PushBack(polygon{p1, p[1], p2, p5})
queue.PushBack(polygon{p5, p2, p[2], p3})
queue.PushBack(polygon{p4, p5, p3, p[3]})
}
}
queue.Remove(e)
}
}
func worker(p polygon) <-chan point {
ch := make(chan point)
go func() {
work(p, ch)
close(ch)
}()
return ch
}
func main() {
flag.StringVar(&llvmPath, "path", "", "LLVM toolchain path")
flag.IntVar(&funcs, "funcs", 1, "number of functions")
flag.IntVar(&ops, "ops", 1, "number of operations")
flag.IntVar(&jobs, "jobs", runtime.NumCPU(), "number of parallel jobs")
flag.Parse()
pwd, err := os.Getwd()
if err != nil {
panic(err)
}
dir, err := ioutil.TempDir(pwd, "tests")
if err != nil {
panic(err)
}
defer os.Remove(dir)
go func() {
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt)
<-sig
os.Exit(0)
}()
tasks := make(chan struct {i int; j int}, jobs)
var group sync.WaitGroup
for i := 0; i < jobs; i++ {
group.Add(1)
go func() {
for req := range tasks {
run_test(dir, req.i, req.j)
}
group.Done()
}()
}
queue := worker(polygon{
point{1, 1},
point{1, ops},
point{funcs, ops},
point{funcs, 1},
})
for p := range queue {
tasks <- struct {i int; j int}{p[0], p[1]}
}
close(tasks)
group.Wait()
}
@petrhosek
Copy link
Author

To use the script:

$ go run test-rsize.go -path ${HOME}/clang-llvm/bin -funcs 1000 -ops 1000 > test.dat
$ gnuplot test.gnuplot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment