Chris Lu
11 years ago
2 changed files with 289 additions and 0 deletions
@ -0,0 +1,288 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"bufio" |
||||
|
"code.google.com/p/weed-fs/go/glog" |
||||
|
"code.google.com/p/weed-fs/go/operation" |
||||
|
"code.google.com/p/weed-fs/go/util" |
||||
|
"fmt" |
||||
|
"github.com/patrick-higgins/summstat" |
||||
|
"io" |
||||
|
"os" |
||||
|
"strings" |
||||
|
"sync" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
type BenchmarkOptions struct { |
||||
|
server *string |
||||
|
concurrency *int |
||||
|
numberOfFiles *int |
||||
|
fileSize *int |
||||
|
idListFile *string |
||||
|
write *bool |
||||
|
read *bool |
||||
|
vid2server map[string]string //cache for vid locations
|
||||
|
} |
||||
|
|
||||
|
var ( |
||||
|
b BenchmarkOptions |
||||
|
) |
||||
|
|
||||
|
func init() { |
||||
|
cmdBenchmark.Run = runbenchmark // break init cycle
|
||||
|
cmdBenchmark.IsDebug = cmdBenchmark.Flag.Bool("debug", false, "verbose debug information") |
||||
|
b.server = cmdBenchmark.Flag.String("server", "localhost:9333", "weedfs master location") |
||||
|
b.concurrency = cmdBenchmark.Flag.Int("c", 7, "number of concurrent write or read processes") |
||||
|
b.fileSize = cmdBenchmark.Flag.Int("size", 1024, "simulated file size in bytes") |
||||
|
b.numberOfFiles = cmdBenchmark.Flag.Int("n", 1024*1024, "number of files to write for each thread") |
||||
|
b.idListFile = cmdBenchmark.Flag.String("list", os.TempDir()+"/benchmark_list.txt", "list of uploaded file ids") |
||||
|
b.write = cmdBenchmark.Flag.Bool("write", true, "enable write") |
||||
|
b.read = cmdBenchmark.Flag.Bool("read", true, "enable read") |
||||
|
} |
||||
|
|
||||
|
var cmdBenchmark = &Command{ |
||||
|
UsageLine: "benchmark -server=localhost:9333 -c=10 -n=100000", |
||||
|
Short: "benchmark on writing millions of files and read out", |
||||
|
Long: `benchmark on an empty weed file system. |
||||
|
|
||||
|
Two tests during benchmark: |
||||
|
1) write lots of small files to the system |
||||
|
2) read the files out |
||||
|
|
||||
|
The file content is mostly zero, but no compression is done. |
||||
|
|
||||
|
By default, write 1 million files of 1KB each with 7 concurrent threads, |
||||
|
and randomly read them out with 7 concurrent threads. |
||||
|
|
||||
|
You can choose to only benchmark read or write. |
||||
|
During write, the list of uploaded file ids is stored in "-list" specified file. |
||||
|
You can also use your own list of file ids to run read test. |
||||
|
|
||||
|
Write speed and read speed will be collected. |
||||
|
The numbers are used to get a sense of the system. |
||||
|
But usually your network or the hard drive is |
||||
|
the real bottleneck. |
||||
|
|
||||
|
`, |
||||
|
} |
||||
|
|
||||
|
var ( |
||||
|
countWritten int64 |
||||
|
sizeWritten int64 |
||||
|
countRead int64 |
||||
|
sizeRead int64 |
||||
|
wait sync.WaitGroup |
||||
|
writeStats stats |
||||
|
readStats stats |
||||
|
) |
||||
|
|
||||
|
func runbenchmark(cmd *Command, args []string) bool { |
||||
|
finishChan := make(chan bool) |
||||
|
fileIdLineChan := make(chan string) |
||||
|
b.vid2server = make(map[string]string) |
||||
|
|
||||
|
if *b.write { |
||||
|
writeStats = newStats() |
||||
|
idChan := make(chan int) |
||||
|
wait.Add(*b.concurrency) |
||||
|
go writeFileIds(*b.idListFile, fileIdLineChan, finishChan) |
||||
|
for i := 0; i < *b.concurrency; i++ { |
||||
|
go writeFiles(idChan, fileIdLineChan) |
||||
|
} |
||||
|
for i := 0; i < *b.numberOfFiles; i++ { |
||||
|
idChan <- i |
||||
|
} |
||||
|
close(idChan) |
||||
|
wait.Wait() |
||||
|
wait.Add(1) |
||||
|
finishChan <- true |
||||
|
wait.Wait() |
||||
|
println("completed", countWritten, "write requests, bytes:", sizeWritten) |
||||
|
writeStats.printStats() |
||||
|
} |
||||
|
|
||||
|
if *b.read { |
||||
|
readStats = newStats() |
||||
|
wait.Add(*b.concurrency) |
||||
|
go readFileIds(*b.idListFile, fileIdLineChan) |
||||
|
for i := 0; i < *b.concurrency; i++ { |
||||
|
go readFiles(fileIdLineChan) |
||||
|
} |
||||
|
wait.Wait() |
||||
|
println("completed", countRead, "read requests, bytes:", sizeRead) |
||||
|
readStats.printStats() |
||||
|
} |
||||
|
|
||||
|
return true |
||||
|
} |
||||
|
|
||||
|
func writeFiles(idChan chan int, fileIdLineChan chan string) { |
||||
|
for { |
||||
|
if id, ok := <-idChan; ok { |
||||
|
countWritten++ |
||||
|
start := time.Now() |
||||
|
fp := &operation.FilePart{Reader: &FakeReader{id: uint64(id), size: int64(*b.fileSize)}, FileSize: int64(*b.fileSize)} |
||||
|
if assignResult, err := operation.Assign(*b.server, 1, ""); err == nil { |
||||
|
fp.Server, fp.Fid = assignResult.PublicUrl, assignResult.Fid |
||||
|
fp.Upload(0, *b.server, "") |
||||
|
writeStats.addSample(time.Now().Sub(start)) |
||||
|
fileIdLineChan <- fp.Fid |
||||
|
sizeWritten += int64(*b.fileSize) |
||||
|
if *cmdBenchmark.IsDebug { |
||||
|
fmt.Printf("writing %d file %s\n", id, fp.Fid) |
||||
|
} |
||||
|
} else { |
||||
|
println("writing file error:", err.Error()) |
||||
|
} |
||||
|
} else { |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
wait.Done() |
||||
|
} |
||||
|
|
||||
|
func readFiles(fileIdLineChan chan string) { |
||||
|
for { |
||||
|
if fid, ok := <-fileIdLineChan; ok { |
||||
|
if len(fid) == 0 { |
||||
|
continue |
||||
|
} |
||||
|
if fid[0] == '#' { |
||||
|
continue |
||||
|
} |
||||
|
if *cmdBenchmark.IsDebug { |
||||
|
fmt.Printf("reading file %s\n", fid) |
||||
|
} |
||||
|
parts := strings.SplitN(fid, ",", 2) |
||||
|
vid := parts[0] |
||||
|
start := time.Now() |
||||
|
if server, ok := b.vid2server[vid]; !ok { |
||||
|
if ret, err := operation.Lookup(*b.server, vid); err == nil { |
||||
|
if len(ret.Locations) > 0 { |
||||
|
server = ret.Locations[0].PublicUrl |
||||
|
b.vid2server[vid] = server |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
if server, ok := b.vid2server[vid]; ok { |
||||
|
url := "http://" + server + "/" + fid |
||||
|
if bytesRead, err := util.Get(url); err == nil { |
||||
|
countRead++ |
||||
|
sizeRead += int64(len(bytesRead)) |
||||
|
readStats.addSample(time.Now().Sub(start)) |
||||
|
} else { |
||||
|
println("!!!! Failed to read from ", url, " !!!!!") |
||||
|
} |
||||
|
} else { |
||||
|
println("!!!! volume id ", vid, " location not found!!!!!") |
||||
|
} |
||||
|
} else { |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
wait.Done() |
||||
|
} |
||||
|
|
||||
|
func writeFileIds(fileName string, fileIdLineChan chan string, finishChan chan bool) { |
||||
|
file, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) |
||||
|
if err != nil { |
||||
|
glog.Fatalf("File to create file %s: %s\n", fileName, err) |
||||
|
} |
||||
|
defer file.Close() |
||||
|
|
||||
|
for { |
||||
|
select { |
||||
|
case <-finishChan: |
||||
|
wait.Done() |
||||
|
return |
||||
|
case line := <-fileIdLineChan: |
||||
|
file.Write([]byte(line)) |
||||
|
file.Write([]byte("\n")) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func readFileIds(fileName string, fileIdLineChan chan string) { |
||||
|
file, err := os.Open(fileName) // For read access.
|
||||
|
if err != nil { |
||||
|
glog.Fatalf("File to read file %s: %s\n", fileName, err) |
||||
|
} |
||||
|
defer file.Close() |
||||
|
|
||||
|
r := bufio.NewReader(file) |
||||
|
for { |
||||
|
if line, err := Readln(r); err == nil { |
||||
|
fileIdLineChan <- string(line) |
||||
|
} else { |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
close(fileIdLineChan) |
||||
|
} |
||||
|
|
||||
|
type stats struct { |
||||
|
stats *summstat.Stats |
||||
|
} |
||||
|
|
||||
|
func newStats() stats { |
||||
|
s := stats{stats: summstat.NewStats()} |
||||
|
s.stats.CreateBins(21, 1000, 20000) |
||||
|
return s |
||||
|
} |
||||
|
|
||||
|
func (s stats) addSample(d time.Duration) { |
||||
|
//fmt.Printf("adding %d μs\n", int(d / 1000))
|
||||
|
s.stats.AddSample(summstat.Sample(int(d / 1000))) |
||||
|
} |
||||
|
func (s stats) printStats() { |
||||
|
fmt.Printf("\n%s Count:%d ", time.Now().Local(), s.stats.Count()) |
||||
|
fmt.Printf("Min:%.2fms ", s.stats.Min()/1000) |
||||
|
fmt.Printf("Max:%.2fms ", s.stats.Max()/1000) |
||||
|
fmt.Printf("Stddev:%.2fms\n", s.stats.Stddev()/1000) |
||||
|
for b := 0; b < s.stats.NBins(); b++ { |
||||
|
count, _, _ := s.stats.Bin(b) |
||||
|
if b+1 != s.stats.NBins() { |
||||
|
fmt.Printf("%2d ~ %2d ms: %2.4f%% %d\n", b, (b + 1), float64(count)*100.0/float64(s.stats.Count()), count) |
||||
|
} else { |
||||
|
fmt.Printf("%2d ~ ms: %2.4f%% %d\n", b, float64(count)*100.0/float64(s.stats.Count()), count) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// a fake reader to generate content to upload
|
||||
|
type FakeReader struct { |
||||
|
id uint64 // an id number
|
||||
|
size int64 // max bytes
|
||||
|
} |
||||
|
|
||||
|
func (l *FakeReader) Read(p []byte) (n int, err error) { |
||||
|
if l.size <= 0 { |
||||
|
return 0, io.EOF |
||||
|
} |
||||
|
if int64(len(p)) > l.size { |
||||
|
n = int(l.size) |
||||
|
} else { |
||||
|
n = len(p) |
||||
|
} |
||||
|
for i := 0; i < n-8; i += 8 { |
||||
|
for s := uint(0); s < 8; s++ { |
||||
|
p[i] = byte(l.id >> (s * 8)) |
||||
|
} |
||||
|
} |
||||
|
l.size -= int64(n) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
func Readln(r *bufio.Reader) ([]byte, error) { |
||||
|
var ( |
||||
|
isPrefix bool = true |
||||
|
err error = nil |
||||
|
line, ln []byte |
||||
|
) |
||||
|
for isPrefix && err == nil { |
||||
|
line, isPrefix, err = r.ReadLine() |
||||
|
ln = append(ln, line...) |
||||
|
} |
||||
|
return ln, err |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue