【教程】Python实时检测CPU和GPU的功耗

news/2024/11/25 20:00:18/

目录

前言

GPU功耗检测方法

CPU功耗检测方法

sudo的困扰与解决

完整功耗分析示例代码


转载请注明出处:小锋学长生活大爆炸[xfxuezhang.cn]

前言

        相关一些检测工具挺多的,比如powertop、powerstat、s-tui等。但如何通过代码的方式来实时检测,是个麻烦的问题。通过许久的搜索和自己的摸索,发现了可以检测CPU和GPU功耗的方法。如果有什么不对,或有更好的方法,欢迎评论留言!

        文末附完整功耗分析的示例代码

GPU功耗检测方法

        如果是常规的工具,可以使用官方的NVML。但这里需要Python控制,所以使用了对应的封装:pynvml。

        先安装:

pip install pynvml

     关于这个库,网上的使用教程挺多的。这里直接给出简单的示例代码:

import pynvml
pynvml.nvmlInit()handle = pynvml.nvmlDeviceGetHandleByIndex(0)
powerusage = pynvml.nvmlDeviceGetPowerUsage(handle) / 1000

        这个方法获取的值,跟使用“nvidia-smi”指令得到的是一样的。

         附赠一个来自网上的获取更详细信息的函数:

def get_sensor_values():"""get Sensor values:return:"""values = list()# get gpu driver versionversion = pynvml.nvmlSystemGetDriverVersion()values.append("GPU_device_driver_version:" + version.decode())gpucount = pynvml.nvmlDeviceGetCount()  # 显示有几块GPUfor gpu_id in range(gpucount):handle = pynvml.nvmlDeviceGetHandleByIndex(gpu_id)name = pynvml.nvmlDeviceGetName(handle).decode()meminfo = pynvml.nvmlDeviceGetMemoryInfo(handle)# print(meminfo.total)  # 显卡总的显存大小gpu_id = str(gpu_id)values.append("GPU " + gpu_id + " " + name + " 总共显存大小:" + str(common.bytes2human(meminfo.total)))# print(meminfo.used)  # 显存使用大小values.append("GPU " + gpu_id + " " + name + " 显存使用大小:" + str(common.bytes2human(meminfo.used)))# print(meminfo.free)  # 显卡剩余显存大小values.append("GPU " + gpu_id + " " + name + " 剩余显存大小:" + str(common.bytes2human(meminfo.free)))values.append("GPU " + gpu_id + " " + name + " 剩余显存比例:" + str(int((meminfo.free / meminfo.total) * 100)))utilization = pynvml.nvmlDeviceGetUtilizationRates(handle)# print(utilization.gpu)  # gpu利用率values.append("GPU " + gpu_id + " " + name + " GPU利用率:" + str(utilization.gpu))powerusage = pynvml.nvmlDeviceGetPowerUsage(handle)# print(powerusage / 1000) # 当前功耗, 原始单位是mWavalues.append("GPU " + gpu_id + " " + name + " 当前功耗(W):" + str(powerusage / 1000))# 当前gpu power capacity# pynvml.nvmlDeviceGetEnforcedPowerLimit(handle)# 通过以下方法可以获取到gpu的温度,暂时采用ipmi sdr获取gpu的温度,此处暂不处理# temp = pynvml.nvmlDeviceGetTemperature(handle,0)print('\n'.join(values))return values

CPU功耗检测方法

        这个没有找到开源可以直接用的库。但经过搜索,发现大家都在用的s-tui工具是开源的!通过查看源码,发现他是有获取CPU功耗部分的代码,所以就参考他的源码写了一下。

        先安装:

sudo apt install s-tui
pip install s-tui

        先直接运行工具看一下效果(不使用sudo是不会出来Power的):

sudo s-tui

        说明这个工具确实能获取到CPU的功耗。其中package就是2个CPU,dram是内存条功耗(一般不准,可以不用)。

        直接给出简单的示例代码:

from s_tui.sources.rapl_power_source import RaplPowerSourcesource.update()
summary = dict(source.get_sensors_summary())cpu_power_total = str(sum(list(map(float, [summary[key] for key in summary.keys() if key.startswith('package')]))))

        不过注意!由于需要sudo权限,所以运行这个py文件时候,也需要sudo方式,比如:

sudo python demo.py

sudo的困扰与解决

        上面提到,由于必须要sudo方式,但sudo python就换了运行脚本的环境了呀,这个比较棘手。后来想了个方法,曲线救国一下。通过sudo运行一个脚本,并开启socket监听;而我们自己真正的脚本,在需要获取CPU功耗时候,连接一下socket就行。

        为什么这里使用socket而不是http呢?因为socket更高效一点!

我们写一个“power_listener.py”来监听:

from s_tui.sources.rapl_power_source import RaplPowerSource
import socket
import jsondef output_to_terminal(source):results = {}if source.get_is_available():source.update()source_name = source.get_source_name()results[source_name] = source.get_sensors_summary()for key, value in results.items():print(str(key) + ": ")for skey, svalue in value.items():print(str(skey) + ": " + str(svalue) + ", ")source = RaplPowerSource()
# output_to_terminal(source)s = socket.socket()
host = socket.gethostname()
port = 8888
s.bind((host, port))
s.listen(5)
print("等待客户端连接...")
while True:c, addr = s.accept()source.update()summary = dict(source.get_sensors_summary())#msg = json.dumps(summary)# package表示CPU,dram表示内存(一般不准)power_total = str(sum(list(map(float, [summary[key] for key in summary.keys() if key.startswith('package')]))))print(f'发送给{addr}:{power_total}')c.send(power_total.encode('utf-8'))c.close()                # 关闭连接

        因此,在需要获取CPU功耗时候,只需要:

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host = socket.gethostname()
port = 8888
s.connect((host, port))
msg = s.recv(1024)
s.close()
power_usage_cpu = float(msg.decode('utf-8'))

完整功耗分析示例代码

        提供一个我自己编写和使用的功耗分析代码,仅供参考。(注意上面的power_listener.py需要运行着)

import cv2
import socket
import sys
import threading
import json
import statistics
from psutil import _common as common
import pynvml
pynvml.nvmlInit()class Timer: def __init__(self, name = '', is_verbose = False):self._name = name self._is_verbose = is_verboseself._is_paused = False self._start_time = None self._accumulated = 0 self._elapsed = 0         self.start()def start(self):self._accumulated = 0         self._start_time = cv2.getTickCount()def pause(self): now_time = cv2.getTickCount()self._accumulated += (now_time - self._start_time)/cv2.getTickFrequency() self._is_paused = True   def resume(self): if self._is_paused: # considered only if paused self._start_time = cv2.getTickCount()self._is_paused = False                      def elapsed(self):if self._is_paused:self._elapsed = self._accumulatedelse:now = cv2.getTickCount()self._elapsed = self._accumulated + (now - self._start_time)/cv2.getTickFrequency()        if self._is_verbose is True:      name =  self._nameif self._is_paused:name += ' [paused]'message = 'Timer::' + name + ' - elapsed: ' + str(self._elapsed) timer_print(message)return self._elapsed   class PowerUsage:'''demo:power_usage = PowerUsage()power_usage.analyze_start()time.sleep(2)time_used, power_usage_gpu, power_usage_cpu = power_usage.analyze_end()print(time_used)print(power_usage_gpu)print(power_usage_cpu)'''def __init__(self):self.start_analyze = Falseself.power_usage_gpu_values = list()self.power_usage_cpu_values = list()self.thread = Noneself.timer = Timer(name='GpuPowerUsage', is_verbose=False)def analyze_start(self, gpu_id=0, delay=0.1):handle = pynvml.nvmlDeviceGetHandleByIndex(gpu_id)def start():self.power_usage_gpu_values.clear()self.power_usage_cpu_values.clear()self.start_analyze = Trueself.timer.start()while self.start_analyze:powerusage = pynvml.nvmlDeviceGetPowerUsage(handle)self.power_usage_gpu_values.append(powerusage/1000)s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)host = socket.gethostname()port = 8888s.connect((host, port))msg = s.recv(1024)s.close()self.power_usage_cpu_values.append(float(msg.decode('utf-8')))time.sleep(delay)self.thread = threading.Thread(target=start, daemon=True)self.thread.start()def analyze_end(self, mean=True):self.start_analyze = Falsewhile self.thread and self.thread.isAlive():time.sleep(0.01)time_used = self.timer.elapsed()self.thread = Nonepower_usage_gpu = statistics.mean(self.power_usage_gpu_values) if mean else self.power_usage_gpu_valuespower_usage_cpu = statistics.mean(self.power_usage_cpu_values) if mean else self.power_usage_cpu_valuesreturn time_used, power_usage_gpu, power_usage_cpupower_usage = PowerUsage()
def power_usage_api(func, note=''):@wraps(func)def wrapper(*args, **kwargs):power_usage.analyze_start()result = func(*args, **kwargs)print(f'{note}{power_usage.analyze_end()}')return resultreturn wrapperdef power_usage_api2(note=''):def decorator(func):@wraps(func)def wrapper(*args, **kwargs):power_usage.analyze_start()result = func(*args, **kwargs)print(f'{note}{power_usage.analyze_end()}')return resultreturn wrapperreturn decorator

        用法示例:

power_usage = PowerUsage()
power_usage.analyze_start()
# ----------------------
# xxx 某一段待分析的代码
# 这里以sleep表示运行时长
time.sleep(2)
# ----------------------
time_used, power_usage_gpu, power_usage_cpu = power_usage.analyze_end()
print(f'time_used: {time_used}')
print(f'power_usage_gpu: {power_usage_gpu}')
print(f'power_usage_cpu: {power_usage_cpu}')


http://www.ppmy.cn/news/19957.html

相关文章

ubuntu 学习笔记

环境:Ubuntu 22.04 桌面版和server版 一、更换国内源,下载更快 1、源文件路径:/etc/apt/sources.list,到这个路径下备份一下源文件。 #备份原有配置文件命令 sudo cp -r /etc/apt/sources.list /etc/apt/sources.list.backup …

【algorithm】认真讲解前缀和与差分 (图文搭配)

🚀write in front🚀 📝个人主页:认真写博客的夏目浅石. 📣系列专栏:AcWing算法笔记 今天的月色好美 文章目录前言一、前缀和算法1.1 什么是前缀和?1.2 一维前缀和二、二维前缀和三、一维差分四…

stm32mp157在使用SSH时,出现“Connection refused”问题解决

最近在使用STM32MP157开发项目,在配置ssh时,出现上面的错误,经过一番的折腾,算是解决了,在这里做一下记录,以备后续查阅。 我的板子有以下两个前提: 1、文件系统使用了buildroot构建&#xff0…

Java基础语法——数组概念、数组内存图解(一个数组、二个数组)及二元数组的应用

目录 数组概述 数组定义格式 数组概念 数组的定义格式 数组的初始化 数组初始化概述 数组的初始化方式 Java中的内存分配 Java中一个数组的内存图解 Java中二个数组的内存图解 两个数组指向同一个地址的内存图解 数组操作中两个常见的小问题 二维数组 二维数组概述…

蓝桥杯刷题015——最少刷题数(二分法+前缀和)

问题描述 小蓝老师教的编程课有 N 名学生, 编号依次是 1…N 。第 i 号学生这学期刷题的数量是 Ai​ 。 对于每一名学生, 请你计算他至少还要再刷多少道题, 才能使得全班刷题比他多的学生数不超过刷题比他少的学生数。 输入格式 第一行包含一个正整数 N 。 第二行包含 N 个整数:…

2023-1-26 刷题情况

具有给定数值的最小字符串 题目描述 小写字符 的 数值 是它在字母表中的位置(从 1 开始),因此 a 的数值为 1 ,b 的数值为 2 ,c 的数值为 3 ,以此类推。 字符串由若干小写字符组成,字符串的数…

Linux pdflush机制

在做进程安全监控的时候,拍脑袋决定的,如果发现一个进程在D状态时,即TASK_UNINTERRUPTIBLE(不可中断的睡眠状态),时间超过了8min,就将系统panic掉。恰好DB组做日志时,将整个log缓存到…

linux 网络编程socket

前言 socket(套接字)是linux下进程间通信的一种方式,通常使用C-S(客户端-服务端)的方式通信,它可以是同一主机下的不同进程间通信或者不同主机的进程通信。 socket是夹在应用层和TCP/UDP协议层间的软件抽象…