Nvidia
设备跟踪和管理正成为机器学习工程的中心焦点。这个任务的核心是在模型训练过程中跟踪和报告gpu的使用效率。
有效的GPU监控可以配置一些非常重要的超参数,例如批大小,还可以有效的识别训练中的瓶颈,比如CPU活动(通常是预处理图像)占用的时间很长,导致GPU需要等待下一批数据的交付,从而处于空闲状态。

什么是利用率?

过去的一个采样周期内GPU 内核执行时间的百分比,就称作GPU的利用率。
如果这个值很低,则意味着您的 GPU 并没有全速的工作,可能是受到 CPU或者IO 操作的瓶颈,如果使用的按小时付费的云服务器,那么就是在浪费时间和金钱!

使用终端命令监控

  1. nvidia-smi

image.png
以下是收集的一些信息:

  • GPU:Tesla T4
  • 设备温度:设备当前运行温度为 25 摄氏度
  • 功耗:GPU 目前运行功率9W,官方设定的额定最大功率消耗 70W 。
  • 显存:0MiB / 15109MiB 上限
  • GPU利用率:0%。同样,NVIDIA 将利用率定义如下:过去采样周期中一个或多个内核在 GPU 上执行的时间百分比。

如果负责硬件相关的工作,温度和功率是跟踪的可能是关注的主要问题,这样就可以平衡尝试最大化计算和维护设备安全。如果是硬件使用者(就像一般使用云服务器一样),最关心的应该是内存使用和GPU利用率。
使用 nvidia-smi 进行监控的其他一些技巧:
调用 watch -n 1 nvidia-smi 可以每一秒进行自动的刷新。
nvidia-smi 也可以通过添加 --format=csv 以 CSV 格式输。在 CSV 格式中,可以通过添加 --gpu-query=... 参数来选择显示的指标。
为了实时显示 CSV 格式并同时写入文件,可以将 nvidia-smi 的输出传输到 tee 命令中,如下所示。这将写入选择的文件路径。

  1. nvidia-smi --query-gpu=timestamp,pstate,temperature.gpu,utilization.gpu,utilization.memory,memory.total,memory.free,memory.used --format=csv | tee gpu-log.csv

用 Python 代码监控

基于终端的工具很棒,但有时希望将 GPU 监控和日志记录直接整合到 Python 程序中。这里提供2中方法:

1、使用NVIDIA 管理库 (NVML)

NVML(nvidia-management-library)是CUDA中提供的可以查看显卡信息的工具包,nvidia-smi也是基于这个工具包
在python中NVML有很多个包,只比较其中的两个。nvvidia-ml-py3 ,它是 NVML 的简单接口,没有任何重要的附加功能。使用此库可能如下所示:

  1. # Install with "pip install nvidia-ml-p3"
  2. import pynvml# Must call this first
  3. pynvml.nvmlInit()
  4. # Use device index to get handle
  5. handle = pynvml.nvmlDeviceGetHandleByIndex(0)
  6. # Use handle to get device stats
  7. memory_info = pynvml.nvmlDeviceGetMemoryInfo(handle)
  8. utilization = pynvml.nvmlDeviceGetUtilizationRates(handle)
  9. # Report device stats
  10. print("Total memory:", memory_info.total)
  11. print("Free memory:", memory_info.free)
  12. print("Used memory:", memory_info.used)
  13. print("GPU Utilization:", utilization.gpu)
  14. print("Memory Utilization:", utilization.memory)

另一个比较好用的库是py3nvml,因为它添加了一些用于管理 GPU 的实用功能,而 nvidia-ml-py3 仅用于监控。除了上面显示的功能类型之外,该库还允许执行以下操作(摘自官方文档):

  1. import py3nvml
  2. import tensorflow as tf
  3. py3nvml.grab_gpus(3)
  4. sess = tf.Session() # now we only grab 3 gpus!

在这里,可以在一台可以访问多个 GPU 的机器上运行,但只想将其中三个用于 本次TensorFlow session。使用 py3nvml,可以简单地调用 py3nvml.grab_gpus(3) 来分配三个设备。

用Golang代码监控

利用Golang监控NVIDIA GPU的运行状态和使用情况实质是解析nvidia-smi指令的信息,主要使用的是Golang中xml解析包 “encoding/xml

解析的结构体

  1. type NvidiaSmiLog struct {
  2. DriverVersion string `xml:"driver_version"`
  3. AttachedGPUs string `xml:"attached_gpus"`
  4. GPUs []struct {
  5. ProductName string `xml:"product_name"`
  6. ProductBrand string `xml:"product_brand"`
  7. UUID string `xml:"uuid"`
  8. FanSpeed string `xml:"fan_speed"`
  9. PCI struct {
  10. PCIBus string `xml:"pci_bus"`
  11. } `xml:"pci"`
  12. FbMemoryUsage struct {
  13. Total string `xml:"total"`
  14. Used string `xml:"used"`
  15. Free string `xml:"free"`
  16. } `xml:"fb_memory_usage"`
  17. Utilization struct {
  18. GPUUtil string `xml:"gpu_util"`
  19. MemoryUtil string `xml:"memory_util"`
  20. } `xml:"utilization"`
  21. Temperature struct {
  22. GPUTemp string `xml:"gpu_temp"`
  23. GPUTempMaxThreshold string `xml:"gpu_temp_max_threshold"`
  24. GPUTempSlowThreshold string `xml:"gpu_temp_slow_threshold"`
  25. } `xml:"temperature"`
  26. PowerReadings struct {
  27. PowerDraw string `xml:"power_draw"`
  28. PowerLimit string `xml:"power_limit"`
  29. } `xml:"power_readings"`
  30. Clocks struct {
  31. GraphicsClock string `xml:"graphics_clock"`
  32. SmClock string `xml:"sm_clock"`
  33. MemClock string `xml:"mem_clock"`
  34. VideoClock string `xml:"video_clock"`
  35. } `xml:"clocks"`
  36. MaxClocks struct {
  37. GraphicsClock string `xml:"graphics_clock"`
  38. SmClock string `xml:"sm_clock"`
  39. MemClock string `xml:"mem_clock"`
  40. VideoClock string `xml:"video_clock"`
  41. } `xml:"max_clocks"`
  42. } `xml:"gpu"`
  43. }

解析的过程

  1. package domain
  2. import (
  3. "encoding/xml"
  4. "log"
  5. "os"
  6. "os/exec"
  7. "regexp"
  8. "strconv"
  9. )
  10. //https://www.jianshu.com/p/dd8a113b02a3 go语言执行shell脚本
  11. //https://www.cnblogs.com/caishunzhe/articles/12667889.html
  12. const NvidiaSmiPath = "/usr/bin/nvidia-smi"
  13. var testMode string
  14. /**
  15. GPU信息
  16. */
  17. type Gpu struct {
  18. Temp []GpuItem `json:"temp"` //温度(C)
  19. Usage []GpuItem `json:"usage"` //显存使用(使用情况 MiB)
  20. UsedPercent []GpuItem `json:"usedPercent"` //使用占比(%)
  21. }
  22. //temp: [{ data:["72","72C"],name: 0 },{ data:["72","72C"],name: 1 }]
  23. //usage: [{ data:["8493","8493Mib/15079Mib"],name: 0 },{ data:["9403","9403Mib/15079Mib"],name: 1 }]
  24. //usedPercent: [{ data:["56","56%"],name: 0 },{ data:["43","43%"],name: 1 }]
  25. type GpuItem struct {
  26. Data []string `json:"data"`
  27. Name int `json:"name"`
  28. }
  29. /**
  30. 获取gpu信息
  31. */
  32. func (gpu *Gpu) GpuInfo() {
  33. //获得GPU metrics 信息
  34. metrics(gpu)
  35. }
  36. type NvidiaSmiLog struct {
  37. DriverVersion string `xml:"driver_version"`
  38. AttachedGPUs string `xml:"attached_gpus"`
  39. GPUs []struct {
  40. ProductName string `xml:"product_name"`
  41. ProductBrand string `xml:"product_brand"`
  42. UUID string `xml:"uuid"`
  43. FanSpeed string `xml:"fan_speed"`
  44. PCI struct {
  45. PCIBus string `xml:"pci_bus"`
  46. } `xml:"pci"`
  47. FbMemoryUsage struct {
  48. Total string `xml:"total"`
  49. Used string `xml:"used"`
  50. Free string `xml:"free"`
  51. } `xml:"fb_memory_usage"`
  52. Utilization struct {
  53. GPUUtil string `xml:"gpu_util"`
  54. MemoryUtil string `xml:"memory_util"`
  55. } `xml:"utilization"`
  56. Temperature struct {
  57. GPUTemp string `xml:"gpu_temp"`
  58. GPUTempMaxThreshold string `xml:"gpu_temp_max_threshold"`
  59. GPUTempSlowThreshold string `xml:"gpu_temp_slow_threshold"`
  60. } `xml:"temperature"`
  61. PowerReadings struct {
  62. PowerDraw string `xml:"power_draw"`
  63. PowerLimit string `xml:"power_limit"`
  64. } `xml:"power_readings"`
  65. Clocks struct {
  66. GraphicsClock string `xml:"graphics_clock"`
  67. SmClock string `xml:"sm_clock"`
  68. MemClock string `xml:"mem_clock"`
  69. VideoClock string `xml:"video_clock"`
  70. } `xml:"clocks"`
  71. MaxClocks struct {
  72. GraphicsClock string `xml:"graphics_clock"`
  73. SmClock string `xml:"sm_clock"`
  74. MemClock string `xml:"mem_clock"`
  75. VideoClock string `xml:"video_clock"`
  76. } `xml:"max_clocks"`
  77. } `xml:"gpu"`
  78. }
  79. func formatValue(key string, meta string, value string) string {
  80. result := key
  81. if meta != "" {
  82. result += "{" + meta + "}"
  83. }
  84. return result + " " + value + "\n"
  85. }
  86. func filterNumber(value string) string {
  87. r := regexp.MustCompile("[^0-9.]")
  88. return r.ReplaceAllString(value, "")
  89. }
  90. func metrics(gpu *Gpu) {
  91. var cmd *exec.Cmd
  92. if testMode == "1" {
  93. dir, err := os.Getwd()
  94. if err != nil {
  95. log.Fatal(err)
  96. }
  97. cmd = exec.Command("/bin/cat", dir+"/test.xml")
  98. } else {
  99. cmd = exec.Command(NvidiaSmiPath, "-q", "-x")
  100. }
  101. // Execute system command
  102. stdout, err := cmd.Output()
  103. if err != nil {
  104. //初始信息
  105. log.Println("解析GPU信息错误,错误信息为:", err.Error())
  106. //一块显卡的温度信息
  107. dataTemp := []string{"72", "72C"}
  108. itemTemp := GpuItem{Data: dataTemp, Name: 0}
  109. itemsTemp := []GpuItem{itemTemp}
  110. gpu.Temp = itemsTemp
  111. //一块显卡的显存使用信息
  112. dataUsage := []string{"8493", "8493Mib/15079Mib"}
  113. itemUsage := GpuItem{Data: dataUsage, Name: 0}
  114. itemsUsage := []GpuItem{itemUsage}
  115. gpu.Usage = itemsUsage
  116. //一块显卡的使用占比信息
  117. dataUsedPercent := []string{"56", "56%"}
  118. itemUsedPercent := GpuItem{Data: dataUsedPercent, Name: 0}
  119. itemsUsedPercent := []GpuItem{itemUsedPercent}
  120. gpu.UsedPercent = itemsUsedPercent
  121. } else {
  122. //一块显卡的温度信息
  123. // Parse XML
  124. var xmlData NvidiaSmiLog
  125. _ = xml.Unmarshal(stdout, &xmlData)
  126. //创建GPU温度切片
  127. itemsTemp := make([]GpuItem, len(xmlData.GPUs))
  128. //创建显卡的显存使用信息切片
  129. itemsUsage := make([]GpuItem, len(xmlData.GPUs))
  130. //创建显卡的使用占比信息切片
  131. itemsUsedPercent := make([]GpuItem, len(xmlData.GPUs))
  132. log.Println("nvidiasmi_driver_version:%s", xmlData.DriverVersion)
  133. log.Println("nvidiasmi_attached_gpus:%s", filterNumber(xmlData.AttachedGPUs))
  134. for index, info := range xmlData.GPUs {
  135. //温度信息开始
  136. gpuTemp := filterNumber(info.Temperature.GPUTemp)
  137. dataTemp := []string{gpuTemp, gpuTemp + "C"}
  138. itemsTemp[index] = GpuItem{Data: dataTemp, Name: index}
  139. //温度信息结束
  140. //显存使用情况开始
  141. used := filterNumber(info.FbMemoryUsage.Used)
  142. total := filterNumber(info.FbMemoryUsage.Total)
  143. dataUsage := []string{used, used + "Mib/" + total + "Mib"}
  144. itemsUsage[index] = GpuItem{Data: dataUsage, Name: index}
  145. //显存使用情况结束
  146. usedInt, _ := strconv.ParseInt(used, 10, 64)
  147. totalInt, _ := strconv.ParseInt(used, 10, 64)
  148. UsedPercent := float32(0.0)
  149. if usedInt == 0 || totalInt == 0 {
  150. UsedPercent = 0.00
  151. } else {
  152. UsedPercent = float32(usedInt / totalInt)
  153. }
  154. //显卡的使用占比信息开始
  155. dataUsedPercent := []string{FloatToString(UsedPercent), (FloatToString(UsedPercent)) + "%"}
  156. itemsUsedPercent[index] = GpuItem{Data: dataUsedPercent, Name: index}
  157. //显卡的使用占比信息结束
  158. }
  159. gpu.Temp = itemsTemp
  160. gpu.Usage = itemsUsage
  161. gpu.UsedPercent = itemsUsedPercent
  162. }
  163. }
  164. //float32 转 String工具类,保留6位小数
  165. func FloatToString(input_num float32) string {
  166. // to convert a float number to a string
  167. return strconv.FormatFloat(float64(input_num), 'f', 2, 64)
  168. }

这里的解析过程部分是业务输出,为了监控信息完整添加了默认信息

  1. //初始信息
  2. log.Println("解析GPU信息错误,错误信息为:", err.Error())
  3. //一块显卡的温度信息
  4. dataTemp := []string{"72", "72C"}
  5. itemTemp := GpuItem{Data: dataTemp, Name: 0}
  6. itemsTemp := []GpuItem{itemTemp}
  7. gpu.Temp = itemsTemp
  8. //一块显卡的显存使用信息
  9. dataUsage := []string{"8493", "8493Mib/15079Mib"}
  10. itemUsage := GpuItem{Data: dataUsage, Name: 0}
  11. itemsUsage := []GpuItem{itemUsage}
  12. gpu.Usage = itemsUsage
  13. //一块显卡的使用占比信息
  14. dataUsedPercent := []string{"56", "56%"}
  15. itemUsedPercent := GpuItem{Data: dataUsedPercent, Name: 0}
  16. itemsUsedPercent := []GpuItem{itemUsedPercent}
  17. gpu.UsedPercent = itemsUsedPercent

这里使用者可以根据需要是否保留