CPU、内存、GPU等硬件使用率监控方案

Mar 29,2024   11009 words   40 min


本篇笔记重点记录如何在各类硬件平台上获得CPU、内存、GPU等动态数据,以验证各类算法的性能表现。之前零零散散介绍过一些,但是不太全面,本篇笔记加以扩展、梳理。

1.PC平台

主要包含如笔记本、台式机、服务器等电脑,运行Windows或Linux(Ubuntu)系统。核心是基于Python的platform库获取平台相关基础信息、基于psutil库实现对CPU和内存信息的获取、基于Nvidia提供的nvidia-smi命令获取GPU相关信息。platform库为Python自带无需额外安装,psutil使用pip install psutil命令安装,nvidia-smi则是安装CUDA后自动安装。关于psutil库的详细使用可参考官方文档。安装CUDA则可参考之前这篇笔记

1.1 平台基础信息获取

核心利用Python的platform库,可以获取包括系统类型、架构、名称、版本、CPU型号等一系列信息。代码如下。

import platform

fout = open("info_platform.txt", "w")

# 返回平台架构
arch = platform.architecture()
arch_str = "architecture:"+str(arch)+"\n"
fout.write(arch_str)
print(arch_str)

# 返回平台CPU型号
cpu = platform.processor()
cpu_str = "cpu:"+cpu+"\n"
fout.write(cpu_str)
print(cpu_str)

# 返回平台型号
mcn = platform.machine()
mcn_str = "machine:"+mcn+"\n"
fout.write(mcn_str)
print(mcn_str)

# 返回平台系统名称
plm = platform.platform()
plm_str = "platform:"+plm+"\n"
fout.write(plm_str)
print(plm_str)

# 返回平台系统类型
sys = platform.system()
sys_str = "system:"+sys+"\n"
fout.write(sys_str)
print(sys_str)

# 返回系统精确版本
ver = platform.version()
ver_str = "version:"+ver+"\n"
fout.write(ver_str)
print(ver_str)

# 返回平台网络名称
node = platform.node()
node_str = "node:"+node+"\n"
fout.write(node_str)
print(node_str)

# 返回系统详细信息
uname = platform.uname()
uname_str = "uname:"+str(uname)+"\n"
fout.write(uname_str)
print(uname_str)

fout.close()

代码上传到了Github,点击查看。如下是在Windows电脑上运行脚本的效果。

1.2 CPU静态信息获取

当然你可以直接通过查看属性的方式查看相关内容。或者利用psutil库获取,如下。

import platform
import psutil

# 返回平台CPU型号
cpu = platform.processor()
cpu_str = "cpu:"+cpu
print(cpu_str)

# 返回平台CPU逻辑和物理核心数
core_num_logical = psutil.cpu_count()
core_num_physical = psutil.cpu_count(logical=False)
print("logical core:", core_num_logical)
print("physical core:", core_num_physical)

代码上传到了Github,点击查看。这样就能获取到CPU基本的信息,如下。

1.3 CPU动态信息监控

对于CPU的动态监控,我们重点是关注CPU的使用率。还是利用psutil库实现,代码如下。

import platform
import psutil
import time
import numpy as np

# 返回平台CPU型号
cpu = platform.processor()
cpu_str = "cpu:"+cpu
print(cpu_str)

# 返回平台CPU逻辑和物理核心数
core_num_logical = psutil.cpu_count()
core_num_physical = psutil.cpu_count(logical=False)
print("logical core:", core_num_logical)
print("physical core:", core_num_physical)

# 启动记录
fout = open("CPUlog.txt", "w")
fout.write(cpu_str+"\n")
fout.write("logical core:" + str(core_num_logical) + "\n")
fout.write("physical core:" + str(core_num_physical) + "\n")
tip_content = "#timestamp(sec), mean cpu percent"
for i in range(core_num_logical):
    tip_content += ", core_"+str(i)
tip_content += "\n"
fout.write(tip_content)

while True:
    cur_stats = psutil.cpu_percent(interval=0.5, percpu=True)
    cur_time = time.time()
    mean_stats = np.mean(cur_stats)
    fout.write(str(cur_time)+","+str(mean_stats))
    for i in range(len(cur_stats)):
        fout.write(","+str(cur_stats[i]))
    fout.write("\n")
    print(cur_time, mean_stats, cur_stats)

fout.close()

代码上传到了Github,点击查看。这样就能获取到CPU的使用信息,如下。

1.4 内存静态信息获取

利用psutil,主要获取内存大小信息,如下。

import psutil

GB = 1024*1024*1024

# 返回内存
vm = psutil.virtual_memory()
vm_total = vm.total/GB
print("total memory(GB):", vm_total)

# 返回交换内存
sm = psutil.swap_memory()
sm_total = sm.total/GB
print("swap memory(GB):", sm_total)

代码上传到了Github,点击查看。这样就能获取到内存基本的信息,如下。

1.5 内存动态信息监控

核心还是psutil,代码如下。

import psutil
import time


GB = 1024*1024*1024

fout = open("./MEMlog.txt", "w")

tip_content = "#timestamp(sec), total memory(GB), used memory(GB), percent, total swap memory(GB), used swap memory(GB), percent\n"
fout.write(tip_content)

while True:
    # 返回内存
    vm = psutil.virtual_memory()
    sm = psutil.swap_memory()
    cur_time = time.time()

    vm_total = vm.total/GB
    vm_used = vm.used/GB
    vm_percent = vm.percent
    sm_total = sm.total/GB
    sm_used = sm.used/GB
    sm_percent = sm.percent
    fout.write(str(cur_time)+","+str(vm_total)+","+str(vm_used)+","+str(vm_percent)+","+str(sm_total)+","+str(sm_used)+","+str(sm_percent)+"\n")
    print(str(cur_time)+":"+str(vm_used)+"GB/"+str(vm_total)+"GB", str(vm_percent)+"%")
    time.sleep(0.5)

fout.close()

代码上传到了Github,点击查看。获取到的内存使用信息如下。

1.6 GPU静态信息获取

对于GPU信息的获取,主要是通过Nvidia提供的nvidia-smi工具实现。其实在之前这篇笔记中已经介绍过了。需要注意的是,你应该先确保在终端或控制台中输入nvidia-smi命令可以正常使用,否则无法获得相关信息。获得信息的核心代码如下:

import os

def parseSMIVersion(text_content):
    lines = text_content.split("\n")
    target_line = lines[2]
    parts = target_line.split("NVIDIA-SMI")[1].split("Driver Version:")
    smi_version = parts[0].strip()
    driver_version = parts[1].split("CUDA Version:")[0].strip()
    cuda_version = parts[1].split("CUDA Version:")[1].split("|")[0].strip()
    return smi_version, driver_version, cuda_version

str_command = "nvidia-smi"
out = os.popen(str_command)
text_content = out.read()
out.close()

smi_ver, driver_ver, cuda_ver = parseSMIVersion(text_content)
print("smi version", smi_ver)
print("driver version", driver_ver)
print("cuda driver", cuda_ver)

代码上传到了Github,点击查看。获取到的GPU信息如下。

当然,还有第二种方式,利用Nvidia提供的pynvml库进行信息获取。首先,需要安装pip install pynvml库,然后执行以下代码即可:

import pynvml

# 初始化 pynvml
pynvml.nvmlInit()

# 获取 GPU 数量
deviceCount = pynvml.nvmlDeviceGetCount()
print("GPU number:", pynvml.nvmlDeviceGetCount())

# 获取GPU信息
for i in range(deviceCount):
   handle = pynvml.nvmlDeviceGetHandleByIndex(i)
   
   
   gpu_name = pynvml.nvmlDeviceGetName(handle)
   print("GPU name:", gpu_name)

   mem_info = pynvml.nvmlDeviceGetMemoryInfo(handle)
   print("GPU Memory:", mem_info.total/(1024*1024*1024),"GB")


# 清理和释放资源
pynvml.nvmlShutdown()

代码也上传到了Github,点击查看。获取到的GPU信息如下。

1.7 GPU动态信息监控

对于GPU的动态信息,主要是GPU的使用率和显存占用情况。首先是nvidia-smi方法,代码如下:


代码上传到了Github,点击查看。获取到的GPU信息如下。

第二种基于pynvml的方法,代码如下。

import time
import pynvml

fout = open("GPUlog.txt", "w")
fout.write("# timestamp(sec), total gpu memory(GB), used gpu memory(GB), GPU percent\n")

# 初始化 pynvml
pynvml.nvmlInit()

# 获取 GPU 数量
deviceCount = pynvml.nvmlDeviceGetCount()
print("GPU number:", pynvml.nvmlDeviceGetCount())

handle = pynvml.nvmlDeviceGetHandleByIndex(0)

while True:
   mem_info = pynvml.nvmlDeviceGetMemoryInfo(handle)
   gpu_info = pynvml.nvmlDeviceGetUtilizationRates(handle)
   cur_time = time.time()

   mem_used = mem_info.used/(1024*1024*1024)
   mem_total = mem_info.total/(1024*1024*1024)
   gpu_percent = gpu_info.gpu

   print("used memory:", mem_used, "GB","percnet:", gpu_percent)
   fout.write(str(cur_time)+","+str(mem_total)+","+str(mem_used)+","+str(gpu_percent)+"\n")

   time.sleep(0.5)

# 清理和释放资源
pynvml.nvmlShutdown()
fout.close()

代码上传到了Github,点击查看。获取到的GPU信息如下。

2.Nvidia Jetson平台

在Nvidia Jetson平台,主要是指Jetson的TX系列、Orin系列、Xavier系列等开发板。CPU、内存等信息的获取方式和上面PC平台可以互通,但GPU相关信息获取方式则有一些差异。信息获取的核心方式是利用Nvidia提供的jetson-stats工具,Github网页是这里。安装方式非常简单,如下:

sudo apt-get install python3-dev
sudo apt-get install python3-pip
sudo pip3 install -U jetson-stats

官方也提供了很多示例可供参考。

2.1 平台基础信息获取

直接运行1.1部分的代码,效果如下。 与PC平台通用。

或者,也可以使用jetson-stats提供的接口获得详细信息。主要参考这个官方示例,如下。

from jtop import jtop

if __name__ == "__main__":

    print("Simple jtop hardware reader")

    with jtop() as jetson:
        # jetson.ok() will provide the proper update frequency
        if jetson.ok():
            # Read hardware, platform and libraries list
            # Print all values
            for name_category, category in jetson.board.items():
                print("{name}:".format(name=name_category))
                # Print all category
                for name, value in category.items():
                    print(" - {name}: {value}".format(name=name, value=value))

代码上传到了Github,点击查看。获取到的信息如下。

2.2 CPU静态信息获取

直接运行1.2部分的代码,效果如下。 与PC平台通用。但由于此类嵌入式开发板的硬件比较特殊,因此还可以通过其他方式获得更详细的信息,例如在这篇笔记中提到的,通过cat /proc/cpuinfo可以获得更丰富的内容,如下。

2.3 CPU动态信息监控

直接运行1.3部分的代码,效果如下。 与PC平台通用。

或者,通过jetson-stats获得,参考官方示例,如下。

from jtop import jtop

if __name__ == "__main__":

    print("Simple jtop cpu reader")

    with jtop() as jetson:
        # jetson.ok() will provide the proper update frequency
        if jetson.ok():
            # Print all cpu
            for idx, cpu in enumerate(jetson.cpu['cpu']):
                print("------ CPU{idx} ------".format(idx=idx))
                for key, value in cpu.items():
                    print("{key}: {value}".format(key=key, value=value))
            # read aggregate CPU status
            total = jetson.cpu['total']
            print("------ TOTAL ------")
            for key, value in total.items():
                print("{key}: {value}".format(key=key, value=value))

代码上传到了Github,点击查看。获取到的信息如下。

2.4 内存静态信息获取

直接运行1.4部分的代码,效果如下。 与PC平台通用。

2.5 内存动态信息监控

直接运行1.5部分的代码,效果如下。 与PC平台通用。

或者,通过jetson-stats获得,参考官方示例,如下。

from jtop import jtop

if __name__ == "__main__":

    print("Simple jtop memory reader")

    with jtop() as jetson:
        # jetson.ok() will provide the proper update frequency
        if jetson.ok():
            # Print all cpu
            for name, data in jetson.memory.items():
                print("------ {name} ------".format(name=name))
                print(data)

代码上传到了Github,点击查看。获取到的信息如下。

2.6 GPU静态信息获取

Jetson平台的GPU信息无法通过前面的命令获得。不过,Jetson平台提供了jtop命令,可以获得关于GPU硬件的详细信息,可以参考这篇笔记。参考官方文档示例,我们也可以很容易获得相关信息:

from jtop import jtop

if __name__ == "__main__":

    print("All accessible jtop properties")

    with jtop() as jetson:
        # boards
        print('*** board ***')
        print(jetson.board)
        # jetson.ok() will provide the proper update frequency
        while jetson.ok():
            # CPU
            print('*** CPUs ***')
            print(jetson.cpu)
            # CPU
            print('*** Memory ***')
            print(jetson.memory)
            # GPU
            print('*** GPU ***')
            print(jetson.gpu)
            # Engines
            print('*** engine ***')
            print(jetson.engine)
            # nvpmodel
            print('*** NV Power Model ***')
            print(jetson.nvpmodel)
            # jetson_clocks
            print('*** jetson_clocks ***')
            print(jetson.jetson_clocks)
            # Status disk
            print('*** disk ***')
            print(jetson.disk)
            # Status fans
            print('*** fan ***')
            print(jetson.fan)
            # uptime
            print('*** uptime ***')
            print(jetson.uptime)
            # local interfaces
            print('*** local interfaces ***')
            print(jetson.local_interfaces)
            # Temperature
            print('*** temperature ***')
            print(jetson.temperature)
            # Power
            print('*** power ***')
            print(jetson.power)

代码上传到了Github,点击查看。获取到的信息如下。 可以看到,事实上除了GPU,还输出了非常丰富的软硬件信息。

2.7 GPU动态信息监控

对于GPU动态监控,官方示例提供了一个很好的logger,我们直接拿过来用即可。

from jtop import jtop, JtopException
import csv
import argparse


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Simple jtop logger')
    # Standard file to store the logs
    parser.add_argument('--file', action="store", dest="file", default="GPUlog.csv")
    args = parser.parse_args()

    print("Simple jtop logger")
    print("Saving log on {file}".format(file=args.file))

    try:
        with jtop() as jetson:
            # Make csv file and setup csv
            with open(args.file, 'w') as csvfile:
                stats = jetson.stats
                # Initialize cws writer
                writer = csv.DictWriter(csvfile, fieldnames=stats.keys())
                # Write header
                writer.writeheader()
                # Write first row
                writer.writerow(stats)
                # Start loop
                while jetson.ok():
                    stats = jetson.stats
                    # Write row
                    writer.writerow(stats)
                    print("Log at {time}".format(time=stats['time']))
    except JtopException as e:
        print(e)
    except KeyboardInterrupt:
        print("Closed with CTRL-C")
    except IOError:
        print("I/O error")

代码上传到了Github,点击查看。获取到的信息如下。 事实上,这里不仅仅保存了GPU使用信息,还保存了CPU的动态使用信息等一系列你关心的数据,比如温度等,使用起来十分方便。

3.树莓派Raspberry Pi平台

对于树莓派平台而言,部分CPU和内存信息可以以通用方式获得。其余则需要有专门的方式。

3.1 平台基础信息获取

直接运行1.1部分的代码,效果如下。 与PC平台通用。

3.2 CPU静态信息获取

直接运行1.2部分的代码,效果如下。 与PC平台通用。

当然,还是可以通过cat /proc/cpuinfo可以获得更丰富的内容,如下。

3.3 CPU动态信息监控

直接运行1.3部分的代码,效果如下。 与PC平台通用。

3.4 内存静态信息获取

直接运行1.4部分的代码,效果如下。 与PC平台通用。

3.5 内存动态信息监控

直接运行1.5部分的代码,效果如下。 与PC平台通用。

3.6 GPU静态信息获取

某种程度来说,树莓派并没有真正的GPU,不过,我们可以通过如下命令查看相关硬件信息。

sudo /opt/vc/bin/vcdbg reloc stats

效果如下。

3.7 GPU动态信息监控

和上面类似的,树莓派并没有真正的GPU,只有VideoCore。不过通过上面的命令依然可以获得GPU内存的占用情况。此外,还可以通过一些命令查看CPU和GPU的温度,如下。

# CPU温度
cat /sys/class/thermal/thermal_zone0/temp | awk '{print "CPU Temp:"(int($0) / 1000)}'
# GPU温度
vcgencmd measure_temp | cut -c6-9 | awk '{print "GPU Temp:"$0}'

效果如下。

4.小结

最后,我们对在上面三个平台上获取动态信息的方式进行总结,如下。

本文中所有代码均上传到了Github,点击查看。最后,我们利用2.7部分提供的代码,对Jeton AGX Orin平台运行YoLoV5网络的资源占用情况进行测试,效果如下图所示。 测试时前后30秒为空白。可以看到,当网络启动的时候,首先CPU短暂“拉满”,这主要是利用CPU在进行模型的加载,之后主要就是CPU在执行运算了。同时也可以看到,温度、占用率等数据也都是同步变化的。

5.参考资料

  • [1] https://pypi.org/project/psutil/
  • [2] https://www.jb51.net/article/281902.htm
  • [3] https://www.jb51.net/python/290893r3m.htm
  • [4] https://blog.csdn.net/mouday/article/details/134476278
  • [5] https://github.com/rbonghi/jetson_stats
  • [6] https://shumeipai.nxez.com/2014/10/04/get-raspberry-the-current-status-and-data.html
  • [7] http://129.226.226.195/post/21019.html

本文作者原创,未经许可不得转载,谢谢配合

返回顶部