github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/bench/microbenchmarks/disk/compare/main.go (about) 1 // Package main 2 /* 3 * Copyright (c) 2018-2021, NVIDIA CORPORATION. All rights reserved. 4 */ 5 package main 6 7 import ( 8 "bufio" 9 "fmt" 10 "io" 11 "os" 12 "os/exec" 13 "strings" 14 "time" 15 16 "github.com/NVIDIA/aistore/cmn/cos" 17 "github.com/NVIDIA/aistore/cmn/nlog" 18 jsoniter "github.com/json-iterator/go" 19 ) 20 21 const iostatColumnCnt = 14 22 23 var ( 24 devices = make(map[string]struct{}) // Replace with the device name to track 25 26 pctUtil = make(map[string]string) 27 avgQuSz = make(map[string]string) 28 29 oldIOMs map[string]int64 30 oldIOQueueMs map[string]int64 31 oldMeasureTime time.Time 32 ) 33 34 func main() { 35 getDiskCommand := exec.Command("lsblk", "-no", "name", "-J") 36 outputBytes, err := getDiskCommand.Output() 37 if err != nil { 38 fmt.Fprintf(os.Stderr, "Failed to lsblk, err %v\n", err) 39 os.Exit(1) 40 } 41 if len(outputBytes) == 0 { 42 fmt.Fprintf(os.Stderr, "Failed to lsblk - no disks?\n") 43 os.Exit(1) 44 } 45 disks := lsblkOutput2disks(outputBytes) 46 47 if len(os.Args[1:]) == 0 { 48 fmt.Fprintln(os.Stderr, "available disks (pass as args to run):") 49 for k := range disks { 50 fmt.Fprintln(os.Stderr, k) 51 } 52 os.Exit(0) 53 } 54 55 for _, x := range os.Args[1:] { 56 if _, ok := disks[x]; !ok { 57 fmt.Fprintf(os.Stderr, "%s is not a disk\n", x) 58 os.Exit(1) 59 } 60 devices[x] = struct{}{} 61 } 62 63 responseCh := make(chan string) 64 65 go pollIostat(responseCh) 66 metricNames := make([]string, 0) 67 68 for { 69 line := <-responseCh 70 if line == "" { 71 continue 72 } 73 fields := strings.Fields(line) 74 if len(fields) < iostatColumnCnt { 75 continue 76 } 77 if strings.HasPrefix(fields[0], "Device") { 78 if len(metricNames) == 0 { 79 metricNames = append(metricNames, fields[1:]...) 80 } 81 continue 82 } 83 device := fields[0] 84 if _, ok := devices[device]; ok { 85 for i := 1; i < len(fields); i++ { 86 name := metricNames[i-1] 87 if name == "%util" { 88 pctUtil[device] = fields[i] 89 } else if name == "aqu-sz" || name == "avgqu-sz" { 90 avgQuSz[device] = fields[i] 91 } 92 } 93 } 94 if len(pctUtil) == len(devices) { 95 generateComparison() 96 97 pctUtil = make(map[string]string) 98 avgQuSz = make(map[string]string) 99 } 100 } 101 } 102 103 func pollIostat(responseCh chan string) { 104 refreshPeriod := "1" // seconds 105 cmd := exec.Command("iostat", "-dxm", refreshPeriod) // the iostat command 106 stdout, _ := cmd.StdoutPipe() 107 reader := bufio.NewReader(stdout) 108 cmd.Start() 109 process := cmd.Process 110 111 for { 112 b, err := reader.ReadBytes('\n') 113 if process == nil { 114 return 115 } 116 if err == io.EOF { 117 continue 118 } 119 responseCh <- string(b) 120 } 121 } 122 123 func generateComparison() { // to diskstats 124 newIOMs := make(map[string]int64) 125 newIOQueueMs := make(map[string]int64) 126 diskstats := GetDiskstats() 127 newMeasureTime := time.Now() 128 129 for k, v := range diskstats { 130 if _, ok := devices[k]; !ok { 131 continue 132 } 133 newIOMs[k] = v.IOMs 134 newIOQueueMs[k] = v.IOMsWeighted 135 } 136 137 if !oldMeasureTime.IsZero() { 138 for k := range devices { 139 dsPctUtil := fmt.Sprintf("%v", 100*float64(newIOMs[k]-oldIOMs[k])/timeGetMs(newMeasureTime.Sub(oldMeasureTime))) 140 dsAvgQuSz := fmt.Sprintf("%v", float64(newIOQueueMs[k]-oldIOQueueMs[k])/timeGetMs(newMeasureTime.Sub(oldMeasureTime))) 141 142 fmt.Println(strings.Join([]string{time.Now().Format(time.RFC3339Nano), "Pct Util Compare", k, pctUtil[k], dsPctUtil}, ",")) 143 fmt.Println(strings.Join([]string{time.Now().Format(time.RFC3339Nano), "Avg Queue Size Compare", k, avgQuSz[k], dsAvgQuSz}, ",")) 144 } 145 } 146 147 oldMeasureTime = newMeasureTime 148 oldIOMs = newIOMs 149 oldIOQueueMs = newIOQueueMs 150 } 151 152 func timeGetMs(d time.Duration) float64 { 153 return float64(d.Nanoseconds()) / (1000 * 1000) 154 } 155 156 // Code for querying for disks 157 158 type BlockDevice struct { 159 Name string `json:"name"` 160 BlockDevices []BlockDevice `json:"children"` 161 } 162 163 type LsBlk struct { 164 BlockDevices []BlockDevice `json:"blockdevices"` 165 } 166 167 func lsblkOutput2disks(lsblkOutputBytes []byte) (disks cos.StrSet) { 168 disks = make(cos.StrSet) 169 var lsBlkOutput LsBlk 170 err := jsoniter.Unmarshal(lsblkOutputBytes, &lsBlkOutput) 171 if err != nil { 172 nlog.Errorf("Unable to unmarshal lsblk output [%s]. Error: [%v]", string(lsblkOutputBytes), err) 173 return 174 } 175 176 findDevs(lsBlkOutput.BlockDevices, disks) 177 178 return disks 179 } 180 181 func findDevs(devList []BlockDevice, disks cos.StrSet) { 182 for _, bd := range devList { 183 if !strings.HasPrefix(bd.Name, "loop") { 184 disks[bd.Name] = struct{}{} 185 findDevs(bd.BlockDevices, disks) 186 } 187 } 188 }