3

Hey I'm learning psutil package and I want to know how to display current CPU usage when function is in progress? I suppose I need some threading or something like this, but how to do it? Thank u for any answers.

import psutil
import random

def iHateThis():
    tab = []
    for i in range(100000):
        tab.append(random.randint(1, 10000))

    tab.sort()
    return tab;

while(True):
    currentProcess = psutil.Process()
    print(currentProcess.cpu_percent(interval=1))
ajirebardyn
  • 43
  • 1
  • 6

4 Answers4

8

You can use threading to run iHateThis or to run function with cpu_percent(). I choose second version. I will run cpu_percent() in thread.

Because it uses while True so thread would run forever and there wouldn't be nice method to stop thread so I use global variaable running with while running to have method to stop this loop.

import threading
import psutil

def display_cpu():
    global running

    running = True

    currentProcess = psutil.Process()

    # start loop
    while running:
        print(currentProcess.cpu_percent(interval=1))

def start():
    global t

    # create thread and start it
    t = threading.Thread(target=display_cpu)
    t.start()

def stop():
    global running
    global t

    # use `running` to stop loop in thread so thread will end
    running = False

    # wait for thread's end
    t.join()

and now I can use it to start and stop thread which will display CPU. Because I may have to stop process using Ctrl+C so it will raise error so I use try/finally to stop thread even if there will be error.

def i_hate_this():
    tab = []
    for i in range(1000000):
        tab.append(random.randint(1, 10000))
    tab.sort()
    return tab

# ---

start()
try:
    result = i_hate_this()
finally: # stop thread even if I press Ctrl+C
    stop()

Full code:

import random
import threading
import psutil

def display_cpu():
    global running

    running = True

    currentProcess = psutil.Process()

    # start loop
    while running:
        print(currentProcess.cpu_percent(interval=1))

def start():
    global t

    # create thread and start it
    t = threading.Thread(target=display_cpu)
    t.start()

def stop():
    global running
    global t

    # use `running` to stop loop in thread so thread will end
    running = False

    # wait for thread's end
    t.join()

# ---

def i_hate_this():
    tab = []
    for i in range(1000000):
        tab.append(random.randint(1, 10000))
    tab.sort()
    return tab

# ---

start()
try:
    result = i_hate_this()
finally: # stop thread even if I press Ctrl+C
    stop()

BTW: this can be converted to class which inherits from class Thread and then it can hide variable running in class.

import psutil
import random
import threading

class DisplayCPU(threading.Thread):

    def run(self):

        self.running = True

        currentProcess = psutil.Process()

        while self.running:
            print(currentProcess.cpu_percent(interval=1))

    def stop(self):
        self.running = False

# ----

def i_hate_this():
    tab = []
    for i in range(1000000):
        tab.append(random.randint(1, 10000))
    tab.sort()
    return tab

# ---

display_cpu = DisplayCPU()

display_cpu.start()
try:
    result = i_hate_this()
finally: # stop thread even when I press Ctrl+C
    display_cpu.stop()

It could be also converted to context manager to run it as

with display_cpu():
    i_hate_this()

but I skip this part.

furas
  • 134,197
  • 12
  • 106
  • 148
  • Hi, dont know why it constantly showing 0.0 in console :/ I'm trying to make really basic resource monitor (something smiliar to windows feature. Maybe cpu_percent isnt right function? I dont know... – ajirebardyn Oct 22 '19 at 18:25
  • maybe your computer is so fast that it needs almost 0.0 CPU. My very old computer needs 98.0. You may try bigger value in `range()`. Or maybe it needs `interval` smaller then `1`. – furas Oct 22 '19 at 18:27
  • I use LInux but maybe it works diffently on Windows. You can used `print(psutil.Process().pid)` in thread and in main loop to check if thread counts CPU for full process or only for thread. – furas Oct 22 '19 at 18:34
  • So I did what u said and the results arent promising... I set range for 1000000000000000 and interval for 0.0000000015 - nothing happend Also i print pid and it display always the same numebr eg: 996, 1300, 6060, 10096 etc – ajirebardyn Oct 22 '19 at 18:40
  • I have no idea. I get `0.0` only if I use code from other answer which uses `multiprocessing` because function runs in separated process and `psutil.Process` check CPU only for current process. But my code uses `thread` which is part of current process. – furas Oct 22 '19 at 18:51
  • Please check my code, maybe I am doing something wrong https://pastebin.com/FHZh2gMC Psutil is the reliable package to do stuff like this? – ajirebardyn Oct 22 '19 at 18:53
  • problem is that `target=display_cpu` needs function's name without `()` (commonly called `"callback"`) and later it will use `()` to run it. Now you have `target=display_cpu()` so it first runs `display_cpu()` and it uses result from `display_cpu()` as value for `target=`. (PL: musisz użyć nazwę funkcji bez nawiasów czyli tak zwany "callback"). In your situation it runs `display_cpu()` in main thead and never ends it so it never runs `i_hate_this()` – furas Oct 22 '19 at 19:01
1

You can do this with the multiprocessing library. multiprocessing.Process is a class that represents a threaded process, is initiated with a function and name, and can be run at any time with .start().

import multiprocessing
import psutil
import random

def iHateThis():
    tab = []
    for i in range(100000):
        tab.append(random.randint(1, 10000))
    tab.sort()
    return tab;

hate = multiprocessing.Process(name='hate', target=iHateThis)
hate.start()

while(True):
    currentProcess = psutil.Process()
    print(currentProcess.cpu_percent(interval=1))
Nat Weiland
  • 199
  • 2
  • 14
0

I don't think you need to use psutil Process class as I think it is intended to be used to monitor a specific process. Using the code snippet from @furas (the accepted answer), you can do it with a thread like this:

def run(self):
    self.run = True 
    while self.run:
        psutil.cpu_percent(interval=1)

it works the same as the accepted answer in the following case:

    _monitor.start()
    try:
        for i in range(50):
            time.sleep(0.2)
    finally:    
        _monitor.stop()

If you don't want to code it, I am doing it in a public repo if it can be of any help for someone: https://github.com/GTimothee/monitor

ava_punksmash
  • 357
  • 1
  • 4
  • 13
0

Let approach the problem differently and propose a decorator that can serve to measure CPU utilization while running

from functools import partial, wraps


def log_cpu_usage(func=None, msg_prefix: str = None):
    """
    This function is a decorator that measures the execution time of a function and logs it.
    """
    debug = True
    if not debug:
        return func
    if func is None:
        return partial(log_cpu_usage, msg_prefix=msg_prefix)
    
    def new_func(data: mp.Queue, *args, **kwargs):
        result = func(*args, **kwargs)
        data.put(result)

    @wraps(func)
    def trace_execution(*args, **kwargs):
        manager = mp.Queue() # to save return val between multi process
        worker_process = mp.Process(target=new_func, args=(manager, *args), kwargs=kwargs)
        worker_process.start()
        p = psutil.Process(worker_process.pid)
        cpu_percents = []
        while worker_process.is_alive(): # while the subprocess is running
            cpu_percents.append(p.cpu_percent() / psutil.cpu_count())
            time.sleep(0.01)
        worker_process.join()
        ret_values = manager.get()
        return sum(cpu_percents) / len(cpu_percents), ret_values

@log_cpu_usage
def iHateThis():
    pass