如何在运行时使用 POSIX Clock() 函数监控 CPU 利用率?

发布于 2024-11-06 01:18:57 字数 334 浏览 3 评论 0原文

有没有可靠的方法可以测量程序在运行时的 CPU 利用率?

我可能必须使用 time.h。这个想法是首先使用一些毫秒来设置空闲CPU负载(步骤A),然后设置满CPU负载(步骤B),然后启动程序并不断调用clock()。因此,我可以计算相对于步骤 A 和步骤 B 计算的 CPU 利用率,以百分比形式监控 CPU 利用率。我假设所有其他后台进程都被忽略。

但是,我不确定,如何仅使用 C89 和 POSIX 正确实现这些步骤 A 和步骤 B,例如idle() 和 full_load() 函数?

Is there any reliable method how a program can measure its CPU utilization during its runtime?

I probably have to use a POSIX clock() function from time.h. The idea is first to use some milliseconds to set an idle CPU load (step A), then full CPU load (step B), then to start a program and call clock() constantly. Thus, I can calculate CPU utilization relative to a calculated on step A and step B to monitor a CPU utilization in the percentage. I assume that all other background processes are ignored.

However, I am not sure, how to implement these step A and step B, say idle() and full_load() functions properly using only C89 and POSIX?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

贱贱哒 2024-11-13 01:18:57

当您说“full_load”时,并且您只需要一个负载下的 CPU 或虚拟核心,一个简单的紧密循环就可以解决问题。当然,它不会使用芯片上的所有晶体管(即,我们不是在谈论“满载”的老化测试),但它会在 CPU 获得的预定时间片内使用,使用所有可用的时钟周期,没有系统调用,这会放弃对内核的控制,并可能导致稍后重新调度执行线程。您还可以使用带有信号处理程序的警报来退出循环。这样您就可以运行循环大约一秒的执行时间(警报的时间并不完全准确……它们很接近,但没有达到时钟周期)。

此外,对于“空闲”负载部分,您可以执行相同的操作,但使用 sigsuspend() 而不是紧密循环,这将等待警报响起。

因此,您的代码可能如下所示:

#include <signal.h>
#include <unistd.h>
#include <time.h>
#include <stdio.h>

static sig_atomic_t alarm_flag = 1;

void alarm_handler(int arg)
{
    alarm_flag = 0;
}

clock_t idle()
{
    //setup the alarm flag
    alarm_flag = 1;

    //setup the signal masks
    sigset_t old_signal_set;
    sigset_t new_signal_set;

    sigemptyset(&old_signal_set);
    sigemptyset(&new_signal_set);

    //block the alarm signal
    sigaddset(&new_signal_set, SIGALRM);
    sigprocmask(SIG_BLOCK, &new_signal_set, &old_signal_set);

    //setup the alarm
    alarm(1);

    clock_t time_before = clock();

    //sit idle while we wait for the alarm to go off
    while(alarm_flag)
        sigsuspend(&old_signal_set);

    clock_t time_after = clock();

    //restore the old signal mask
    sigprocmask(SIG_SETMASK, &old_signal_set, NULL);

    return time_after - time_before;
}

clock_t full_load()
{
    //set the alarm signal
    alarm_flag = 1;

    //set the 1-second alarm
    alarm(1);

    clock_t time_before = clock();

    //loop until the alarm goes off
    while(alarm_flag);

    clock_t time_after = clock();

    return time_after - time_before;
}

int main()
{
    //setup the signal handler for the alarm
    sigset(SIGALRM, alarm_handler);

    //call the functions
    clock_t idle_time = idle();
    clock_t load_time = full_load();

    //... do whatever else you need to-do with this info
    printf("Idle Time: %d\n", (int)idle_time);
    printf("Load Time: %d\n", (int)load_time);

    return 0;
}

请记住,根据 POSIX 标准,每秒应该有 100 万个时钟作为 clock_t 值的时基,因此您应该看到为“full_load”返回的数字接近该数字,因为我们将“满载”大约一秒钟。空载负载应该很小(当然)。以下是我在 Mac Pro 上生成的数字:

Idle Time: 31
Load Time: 1000099

因此,就了解您可能会看到从 clock() 返回的时钟周期而言,这似乎与您正在寻找的内容有些一致。当然,我会多次运行此操作并取平均值,以便更好地指示您可能会看到的差异。

When you say "full_load", and you only need a single CPU or virtual core under load, a simple tight loop will do the trick. Granted, it won't use all the transistors on the chip (i.e., we're not talking about a burn-in test for "full-load"), but it will, for the scheduled time-slice it gets of the CPU, use all the available clock-cycles with no syscalls that would give up control to the kernel and possibly cause the executing thread to be re-scheduled for later. Also you could use an alarm with a signal handler in order to exit from the loop. So that would let you run the loop for approximately a second of execution time (alarms aren't exactly time-accurate ... they're close, but not down to the clock-cycle).

In addition, for the "idle" load portion, you could do the same thing, but using a sigsuspend() instead of a tight loop, that would wait for the alarm to go off.

So your code could look something like the following:

#include <signal.h>
#include <unistd.h>
#include <time.h>
#include <stdio.h>

static sig_atomic_t alarm_flag = 1;

void alarm_handler(int arg)
{
    alarm_flag = 0;
}

clock_t idle()
{
    //setup the alarm flag
    alarm_flag = 1;

    //setup the signal masks
    sigset_t old_signal_set;
    sigset_t new_signal_set;

    sigemptyset(&old_signal_set);
    sigemptyset(&new_signal_set);

    //block the alarm signal
    sigaddset(&new_signal_set, SIGALRM);
    sigprocmask(SIG_BLOCK, &new_signal_set, &old_signal_set);

    //setup the alarm
    alarm(1);

    clock_t time_before = clock();

    //sit idle while we wait for the alarm to go off
    while(alarm_flag)
        sigsuspend(&old_signal_set);

    clock_t time_after = clock();

    //restore the old signal mask
    sigprocmask(SIG_SETMASK, &old_signal_set, NULL);

    return time_after - time_before;
}

clock_t full_load()
{
    //set the alarm signal
    alarm_flag = 1;

    //set the 1-second alarm
    alarm(1);

    clock_t time_before = clock();

    //loop until the alarm goes off
    while(alarm_flag);

    clock_t time_after = clock();

    return time_after - time_before;
}

int main()
{
    //setup the signal handler for the alarm
    sigset(SIGALRM, alarm_handler);

    //call the functions
    clock_t idle_time = idle();
    clock_t load_time = full_load();

    //... do whatever else you need to-do with this info
    printf("Idle Time: %d\n", (int)idle_time);
    printf("Load Time: %d\n", (int)load_time);

    return 0;
}

Keep in mind that according to the POSIX standard, there should be 1 million clocks per second as the time-base for the the clock_t value, so you should see the number returned for the "full_load" that is close to that number since we're going "full-load" for approximately a second. Idle load should be very small (of course). Here's numbers I generated on my Mac Pro:

Idle Time: 31
Load Time: 1000099

So that seems somewhat in-line with what you're looking for as far as knowing how many clock cycles you may see returned from clock(). I would of course run this multiple times and take an average to get a better indicator of the variance you might see.

芯好空 2024-11-13 01:18:57

我认为这是由 CLOCK_VIRTUAL (BSD/HP-UX) 或 Linux 上的 CLOCK_PROCESS_CPUTIME_ID 覆盖的 Clock_gettime(2) 。

I thought this is covered by CLOCK_VIRTUAL (BSD/HP-UX) or CLOCK_PROCESS_CPUTIME_ID on linux for clock_gettime(2).

巴黎夜雨 2024-11-13 01:18:57

您没有指定 POSIX 的版本,但如果您使用的是 C89,它一定很旧。 POSIX.1-2001 又名 SUSv3 需要 C99,还有 POSIX.1-2008。

校准似乎没有必要;只需将返回的值与单调时间(更好,但可能不可用)或挂钟时间进行比较。对于clock(),使用CLOCKS_PER_SEC定义(如果系统支持XSI选项,则保证为一百万,但如果系统仅支持POSIX则不然)。

考虑使用 getrusage() 而不是 clock() 以获得更高的准确性和灵活性。

在早期的 POSIX 版本中,函数 getrusage() 位于 XSI 选项下;更改历史显示它是在第 4 版版本 2 (SUSv1) 中引入的,并在第 5 版 (SUSv2) 中移至基础,但其上仍然有 XSI 标记。无论如何,它很常见,因为它是 4.2BSD 的函数。

另一个选项是 times() 函数。

You do not specify the version of POSIX, but if you are using C89, it must be pretty old. POSIX.1-2001 aka SUSv3 requires C99 and there is also POSIX.1-2008.

Calibration seems unnecessary; just compare the values returned to monotonic (better, but may not be available) or wall clock time. For clock(), use the CLOCKS_PER_SEC define (this is guaranteed to be one million if the system supports the XSI option, but not if it is just POSIX).

Consider getrusage() instead of clock() for more accuracy and flexibility.

The function getrusage() was under the XSI option in earlier POSIX versions; the change history says it was introduced in Issue 4 Version 2 (SUSv1) and was moved to the base in Issue 5 (SUSv2) but there are still XSI markings on it. In any case it is pretty common as it is a 4.2BSD function.

Another option is the times() function.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文