Skip to content

Instantly share code, notes, and snippets.

@temoto
Created September 17, 2012 21:35
Show Gist options
  • Select an option

  • Save temoto/3739938 to your computer and use it in GitHub Desktop.

Select an option

Save temoto/3739938 to your computer and use it in GitHub Desktop.
cp benchmarks
package main
import (
"flag"
"fmt"
"io"
"os"
"path"
"runtime"
)
var offchan chan int64
var zerochan chan int
func copy(from, to string, todir bool) bool {
if todir {
_, file := path.Split(from)
to = to + "/" + file
}
dirb, err := os.Stat(from)
if err != nil {
fmt.Printf("can't stat %s: %v\n", from, err)
return true
}
tob, err := os.Stat(to)
if err == nil {
if sameFile(dirb.Sys(), tob.Sys()) {
fmt.Printf("%s and %s are the same file\n", from, to)
return true
}
}
if dirb.IsDir() {
fmt.Printf("%s is a directory\n", from)
return true
}
mode := dirb.Mode() & 0777
f, err := os.Open(from)
if err != nil {
fmt.Printf("can't open %s: %v\n", from, err)
return true
}
defer f.Close()
t, err := os.OpenFile(to, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode)
if err != nil {
fmt.Printf("can't create %s: %v\n", to, err)
f.Close()
return true
}
defer t.Close()
return copy1(f, t, from, to)
}
func copy1(f, t *os.File, from, to string) (ret bool) {
zerochan <- 0
fail := make(chan bool, Nwork)
for i := 0; i < Nwork; i++ {
go worker(f, t, from, to, fail)
}
for i := 0; i < Nwork; i++ {
end := <-fail
if end == true {
ret = true
}
}
return
}
func worker(f, t *os.File, from, to string, fail chan bool) {
buf := make([]byte, BufSize)
var bp []byte
l := len(buf)
bp = buf[0:]
o := <-offchan
for {
n, err := f.ReadAt(bp, o)
if err != nil && err != io.EOF {
fmt.Printf("reading %s at %v: %v\n", from, o, err)
fail <- true
return
}
if n == 0 {
break
}
nb := bp[0:n]
n, err = t.WriteAt(nb, o)
if err != nil {
fmt.Printf("writing %s: %v\n", to, err)
fail <- true
return
}
bp = buf[n:]
o += int64(n)
l -= n
if l == 0 {
l = len(buf)
bp = buf[0:]
o = <-offchan
}
}
fail <- false
}
func nextoff() {
off := int64(0)
for {
select {
case <-zerochan:
off = 0
case offchan <- off:
off += BufSize
}
}
}
func usage() {
fmt.Printf("usage: cp [-w workers] from to\n")
os.Exit(1) // sysfatal
}
var Nwork int
var BufSize int64
var Nthreads int
func main() {
todir := false
flag.IntVar(&Nwork, "w", 16, "number of worker goroutines")
flag.Int64Var(&BufSize, "bs", 4, "buffer size in KB")
flag.IntVar(&Nthreads, "t", 1, "number of OS threads (GOMAXPROCS). 0=number of CPU cores")
flag.Parse()
BufSize = BufSize << 10
switch flag.NArg() {
case 0, 1:
usage()
}
if Nthreads == 0 {
Nthreads = runtime.NumCPU()
}
runtime.GOMAXPROCS(Nthreads)
files := flag.Args()
lf := files[len(files)-1]
lfdir, err := os.Stat(lf)
if err == nil {
todir = lfdir.IsDir()
}
if flag.NArg() > 2 && todir == false {
fmt.Printf("not a directory: %s\n", lf)
os.Exit(1) // sysfatal
}
offchan = make(chan int64, 0)
zerochan = make(chan int, 0)
go nextoff()
failed := false
for i := 0; i < flag.NArg()-1; i++ {
if copy(files[i], lf, todir) {
failed = true
}
}
if failed {
os.Exit(2)
}
return
}
# Story: f2f on irc.freenode#go-nuts posted his program
# http://mirtchovski.com/go/cp/
# Which implements plan9 cp using concurrent pread/pwrite blocks.
$ cat drop-caches
#!/bin/bash -e
sync
sleep 1s
sync
echo 3 >/proc/sys/vm/drop_caches
$ for i in {1..5}; do sudo ./drop-caches ; time ./f2f-cp -w=1 -t=1 -bs=8 200m 200m.1 ; cmp 200m 200m.1 || echo error; rm -f 200m.1 ; done
0.30user 1.31system 0:04.89elapsed 33%CPU (0avgtext+0avgdata 1200maxresident)k
411912inputs+409600outputs (9major+345minor)pagefaults 0swaps
0.26user 1.37system 0:04.98elapsed 32%CPU (0avgtext+0avgdata 1196maxresident)k
411912inputs+409600outputs (9major+343minor)pagefaults 0swaps
0.32user 1.30system 0:04.98elapsed 32%CPU (0avgtext+0avgdata 1192maxresident)k
411912inputs+409600outputs (9major+344minor)pagefaults 0swaps
0.26user 1.30system 0:04.89elapsed 32%CPU (0avgtext+0avgdata 1200maxresident)k
411912inputs+409600outputs (9major+346minor)pagefaults 0swaps
0.25user 1.25system 0:05.02elapsed 30%CPU (0avgtext+0avgdata 1196maxresident)k
411912inputs+409600outputs (9major+343minor)pagefaults 0swaps
$ for i in {1..5}; do sudo ./drop-caches ; time ./f2f-cp -w=4 -t=4 -bs=8 200m 200m.1 ; cmp 200m 200m.1 || echo error; rm -f 200m.1 ; done
0.09user 0.81system 0:05.05elapsed 18%CPU (0avgtext+0avgdata 1320maxresident)k
411912inputs+409600outputs (9major+378minor)pagefaults 0swaps
0.11user 0.79system 0:04.95elapsed 18%CPU (0avgtext+0avgdata 1300maxresident)k
411912inputs+409600outputs (9major+373minor)pagefaults 0swaps
0.10user 0.80system 0:05.02elapsed 18%CPU (0avgtext+0avgdata 1304maxresident)k
411912inputs+409600outputs (9major+374minor)pagefaults 0swaps
0.11user 0.79system 0:05.08elapsed 17%CPU (0avgtext+0avgdata 1316maxresident)k
411912inputs+409600outputs (9major+378minor)pagefaults 0swaps
0.13user 0.77system 0:05.27elapsed 17%CPU (0avgtext+0avgdata 1316maxresident)k
411912inputs+409600outputs (9major+376minor)pagefaults 0swaps
$ for i in {1..5}; do sudo ./drop-caches ; time ./f2f-cp -w=4 -t=1 -bs=8 200m 200m.1 ; cmp 200m 200m.1 || echo error; rm -f 200m.1 ; done
0.16user 0.89system 0:05.30elapsed 20%CPU (0avgtext+0avgdata 1324maxresident)k
411912inputs+409600outputs (9major+393minor)pagefaults 0swaps
0.17user 0.88system 0:05.25elapsed 20%CPU (0avgtext+0avgdata 1320maxresident)k
411912inputs+409600outputs (9major+392minor)pagefaults 0swaps
0.15user 0.91system 0:05.06elapsed 21%CPU (0avgtext+0avgdata 1324maxresident)k
411912inputs+409600outputs (9major+393minor)pagefaults 0swaps
0.15user 0.91system 0:05.11elapsed 20%CPU (0avgtext+0avgdata 1328maxresident)k
411912inputs+409600outputs (9major+394minor)pagefaults 0swaps
0.16user 0.90system 0:05.40elapsed 19%CPU (0avgtext+0avgdata 1332maxresident)k
411912inputs+409600outputs (9major+395minor)pagefaults 0swaps
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment