-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
169 lines (151 loc) · 4.58 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
package main
import (
"flag"
"fmt"
"os"
"sync"
"time"
)
type resultData struct {
throuput float64
latency float64
}
type calculatedData struct {
id int
throuput_avg float64
latency_avg float64
throuput_best float64
latency_best float64
throuput_worst float64
latency_worst float64
}
// Write data to a file and measure the time.
func writeDataToFile(fileSize uint64, file *os.File, fileName string, blockSize uint64) ([]resultData, error) {
var totalSize uint64 = 0
var splitStart time.Time
var splitEnd time.Duration
var results []resultData
data := make([]byte, blockSize)
for totalSize < fileSize {
writeSize := blockSize
if fileSize-totalSize < blockSize {
writeSize = fileSize - totalSize
}
splitStart = time.Now()
n, err := file.Write(data[:writeSize])
if err != nil {
fmt.Println("Error: failed to write data: %s", fileName)
file.Close()
err := os.Remove(fileName)
if err != nil {
fmt.Println("Error: failed to remove file: %s", fileName)
}
return nil, err
}
splitEnd = time.Since(splitStart)
results = append(results, resultData{
throuput: float64(n) / 1000 / splitEnd.Seconds(),
latency: float64(splitEnd.Seconds()) * 1000})
totalSize += uint64(n)
}
return results, nil
}
// Calculate the average, best and worst values of throughput and latency.
func calculateData(results []resultData, id int) calculatedData {
var throuputSum float64 = 0
var latencySum float64 = 0
var throuputBest float64 = 0
var latencyBest float64 = 0
var throuputWorst float64 = 0
var latencyWorst float64 = 0
for _, result := range results {
throuputSum += result.throuput
latencySum += result.latency
if throuputBest == 0 || result.throuput > throuputBest {
throuputBest = result.throuput
}
if latencyBest == 0 || result.latency < latencyBest {
latencyBest = result.latency
}
if throuputWorst == 0 || result.throuput < throuputWorst {
throuputWorst = result.throuput
}
if latencyWorst == 0 || result.latency > latencyWorst {
latencyWorst = result.latency
}
}
return calculatedData{
id: id,
throuput_avg: throuputSum / float64(len(results)),
latency_avg: latencySum / float64(len(results)),
throuput_best: throuputBest,
latency_best: latencyBest,
throuput_worst: throuputWorst,
latency_worst: latencyWorst,
}
}
// Create a file and test it.
func processBenchmark(id int, blockSize uint64, fileSize uint64, waitGroup *sync.WaitGroup, ch chan calculatedData, sem chan struct{}) {
defer waitGroup.Done()
sem <- struct{}{}
defer func() { <-sem }()
fileName := fmt.Sprintf("benchmark_file_%d_%d", id, time.Now().Unix())
file, err := os.OpenFile(fileName, os.O_CREATE | os.O_WRONLY | os.O_SYNC, 0644)
if err != nil {
fmt.Println("Error: failed to open file: %s", fileName)
return
}
var results []resultData
results, err = writeDataToFile(fileSize, file, fileName, blockSize)
if err != nil {
return
}
var calc calculatedData
calc = calculateData(results, id)
ch <- calc
file.Close()
err = os.Remove(fileName)
if err != nil {
fmt.Println("Error: failed to remove file: %s", fileName)
}
}
// Processes the arguments, spawns a goroutine, and displays the results.
func main() {
blockSize := flag.Uint64("blocksize", 512, "Blocksize in byte")
fileSize := flag.Uint64("filesize", 1024, "Filesize in byte")
numJobs := flag.Int("numjobs", 4, "The number of jobs to run in parallel")
numTests := flag.Int("numtests", 4, "The number of times to run the test")
flag.Parse()
if *blockSize <= 0 {
fmt.Println("Error: blocksize must be greater than 0")
return
}
if *fileSize <= 0 {
fmt.Println("Error: filesize must be greater than 0")
return
}
if *numJobs < 1 {
fmt.Println("Error: numJobs must be at least 1")
return
}
if *numTests < 1 {
fmt.Println("Error: numTests must be at least 1")
return
}
fmt.Printf("blocksize: %d, filesize: %d, numjobs: %d numtests: %d\n", *blockSize, *fileSize, *numJobs, *numTests)
var waitGroup sync.WaitGroup
ch := make(chan calculatedData, *numTests)
sem := make(chan struct{}, *numJobs)
defer close(ch)
for i := 0; i < *numTests; i++ {
waitGroup.Add(1)
go processBenchmark(i, *blockSize, *fileSize, &waitGroup, ch, sem)
}
waitGroup.Wait()
for i := 0; i < *numTests; i++ {
calc := <-ch
fmt.Printf("ID: %d\n", calc.id)
fmt.Printf("Throughput_avg: %.3f kb/s, Throughput_best: %.3f kb/s, Throughput_worst: %.3f kb/s\n", calc.throuput_avg, calc.throuput_best, calc.throuput_worst)
fmt.Printf("Latency_avg: %.3f ms, Latency_best: %.3f ms, Latency_worst: %.3f ms\n", calc.latency_avg, calc.latency_best, calc.latency_worst)
}
}