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  }