2017-05-09 77 views
0

目前我通過Python的psutil modul監控多個進程,並以execution_time/total_time爲基礎檢索CPU使用率百分比。這樣做的問題是動態電壓和頻率縮放(DVFS或ACPI的P狀態或cpufreq等)。當前CPU頻率越低,進程需要執行的時間越長,CPU使用率就越高。與此相反,我需要固定的 CPU使用率相對於CPU的最高性能。相對於最大CPU頻率的CPU/CPU進程/ CPU週期

爲了避免多次重複計算時永久更改「當前頻率」,一種方法是直接使用該進程使用的CPU週期。原則上,這可以通過在C命令中的perf_event.h或在Linux命令行上的perf完成。不幸的是,我找不到一個提供類似功能的Python模塊(基於前面提到的)。

回答

2

感謝的BlackJack的評論

怎麼樣用C實現它的共享庫,並通過​​Python中使用它嗎?

庫調用的開銷較小。子進程調用會啓動整個外部進程,並在每次需要該值時將結果作爲字符串通過管道傳遞。共享庫加載一次進入當前進程並將結果傳遞到內存中。

我將它作爲共享庫實現。該庫的源代碼cpucycles.c是(主要基於的perf_event_open's man page的示例):

$ gcc -c -fPIC cpucycles.c -o cpucycles.o 
$ gcc -shared -Wl,-soname,libcpucycles.so.1 -o libcpucycles.so.1.0.1 cpucycles.o 

#include <stdlib.h> 
#include <unistd.h> 
#include <string.h> 
#include <sys/ioctl.h> 
#include <linux/perf_event.h> 
#include <asm/unistd.h> 

static long 
perf_event_open(struct perf_event_attr *hw_event, pid_t pid, 
       int cpu, int group_fd, unsigned long flags) 
{ 
    int ret; 

    ret = syscall(__NR_perf_event_open, hw_event, pid, cpu, 
        group_fd, flags); 
    return ret; 
} 

long long 
cpu_cycles(unsigned int microseconds, 
      pid_t pid, 
      int cpu, 
      int exclude_user, 
      int exclude_kernel, 
      int exclude_hv, 
      int exclude_idle) 
{ 
    struct perf_event_attr pe; 
    long long count; 
    int fd; 

    memset(&pe, 0, sizeof(struct perf_event_attr)); 
    pe.type = PERF_TYPE_HARDWARE; 
    pe.size = sizeof(struct perf_event_attr); 
    pe.config = PERF_COUNT_HW_CPU_CYCLES; 
    pe.disabled = 1; 
    pe.exclude_user = exclude_user; 
    pe.exclude_kernel = exclude_kernel; 
    pe.exclude_hv = exclude_hv; 
    pe.exclude_idle = exclude_idle; 

    fd = perf_event_open(&pe, pid, cpu, -1, 0); 
    if (fd == -1) { 
     return -1; 
    } 
    ioctl(fd, PERF_EVENT_IOC_RESET, 0); 
    ioctl(fd, PERF_EVENT_IOC_ENABLE, 0); 
    usleep(microseconds); 
    ioctl(fd, PERF_EVENT_IOC_DISABLE, 0); 
    read(fd, &count, sizeof(long long)); 

    close(fd); 
    return count; 
} 

該代碼通過以下兩個命令編譯成一個共享庫

import ctypes 
import os 

cdll = ctypes.cdll.LoadLibrary(os.path.join(os.path.dirname(__file__), "libcpucycles.so.1.0.1")) 
cdll.cpu_cycles.argtypes = (ctypes.c_uint, ctypes.c_int, ctypes.c_int, 
          ctypes.c_int, ctypes.c_int, ctypes.c_int, 
          ctypes.c_int) 
cdll.cpu_cycles.restype = ctypes.c_longlong 

def cpu_cycles(duration=1.0, pid=0, cpu=-1, 
       exclude_user=False, exclude_kernel=False, 
       exclude_hv=True, exclude_idle=True): 
    """ 
    See man page of perf_event_open for all the parameters. 

    :param duration: duration of counting cpu_cycles [seconds] 
    :type duration: float 
    :returns: cpu-cycle count of pid 
    :rtype: int 
    """ 
    count = cdll.cpu_cycles(int(duration*1000000), pid, cpu, 
          exclude_user, exclude_kernel, 
          exclude_hv, exclude_idle) 
    if count < 0: 
       raise OSError("cpu_cycles(pid={}, duration={}) from {} exited with code {}.".format(
        pid, duration, cdll._name, count)) 

    return count 
0

最後,我通過perf命令行工具讀取CPU週期做的幷包裹成Python(簡化代碼):

import subprocess 
maximum_cpu_frequency = 3e9 
cpu_percent = [] 
while True: # some stop criteria 
    try: 
     cpu_percent.append(int(
       subprocess.check_output(["perf", "stat", "-e", "cycles", 
         "-p", pid, "-x", ",", "sleep", "1"], 
         stderr=subprocess.STDOUT).decode().split(",")[0] 
       )/maximum_cpu_frequency) 
    except ValueError: 
     cpu_percent.append(0.0) 

不幸的是,這是不準確由於不精確sleep命令以及由於爲每個樣品產生了新的perf過程,所以效率很高。

+1

什麼impleme:最後,庫可以被Python在cpucycles.py使用在C中作爲共享庫它,並通過Python中的'ctypes'使用它? – BlackJack

+0

庫調用是否會引入比進程調用更少的開銷?如果是這樣,這可能是一個更有效的方法。不幸的是,它需要更多的我在C中不太熟悉的實現。 – Chickenmarkus

+1

庫調用會減少開銷。子進程調用會啓動整個外部進程,並在每次需要該值時將結果作爲字符串通過管道傳遞。共享庫將_once_加載到當前進程中,並將結果傳遞到內存中。 – BlackJack