【问题标题】:C: Different implementation of clock() in Windows and other OS?C:在 Windows 和其他操作系统中,clock() 的不同实现?
【发布时间】:2015-01-26 12:43:52
【问题描述】:

我必须为大学编写一个非常简单的控制台程序,该程序必须测量输入所需的时间。

因此,我在fgets()call 之前和之后使用了clock()。在我的 Windows 计算机上运行时,它运行良好。然而,当在我的朋友 Mac-Book 和 Linux-PC 上运行时,结果非常小(仅几微秒的时间)。

我在所有 3 个操作系统上都尝试了以下代码:

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

void main()
{
    clock_t t;

    printf("Sleeping for a bit\n");

    t = clock();

    // Alternatively some fgets(...)
    usleep(999999);

    t = clock() - t;

    printf("Processor time spent: %lf", ((double)t) / CLOCKS_PER_SEC);
}

在 Windows 上,输出显示 1 秒(或您在使用 fgets 时输入的时间),而在其他两个操作系统上则不超过 0 秒。

现在我的问题是为什么在这些操作系统上实现clock() 会有如此大的差异。对于 Windows,当线程处于睡眠/等待状态时,时钟似乎一直在滴答作响,但对于 Linux 和 Mac 却不是?

编辑: 谢谢你们到目前为止的答案,所以这只是微软的错误实施。

谁能回答我最后一个问题:

还有没有办法测量我想要在所有 3 个使用 C 标准库的系统上测量的内容,因为 clock() 似乎只在 Windows 上以这种方式工作?

【问题讨论】:

标签: c linux windows macos clock


【解决方案1】:

您在 Microsoft 的 C 运行时遇到 a known bug。即使该行为不符合任何 ISO C 标准,它也不会被修复。来自错误报告:

但是,我们选择避免​​重新实现 clock() 以使其返回的时间值可能会以每物理秒 1 秒的速度前进,因为这种更改会根据之前的行为静默地破坏程序(我们预计会出现这种情况)有很多这样的程序)。

【讨论】:

【解决方案2】:

如果我们在 Mac OS X 上将 look at the source code 替换为 clock(),我们会看到它是使用 getrusage 实现的,并且读取为 ru_utime + ru_stime。这两个字段measure CPU time used by the process (or by the system, on behalf of the process)。这意味着如果usleep(或fgets)导致操作系统在不同的程序中交换执行直到发生某些事情,那么任何数量的实时(也称为“挂钟”,如“挂钟”)过去了不计入clock() 在 Mac OS X 上返回的值。您可能会深入研究并在 Linux 中找到类似的东西。

但是,在 Windows 上,clock() returns the amount of wall time 自进程开始后已过。

在纯 C 语言中,我不知道 OS X、Linux 和 Windows 上可用的函数会以亚秒级精度返回挂钟时间(time.h 相当有限)。 Windows 上的 GetSystemTimeAsFileTime 将以 100ns 为单位返回时间,BSD 上的 gettimeofday 将时间返回到微秒精度。

如果您可以接受秒精度,您可以使用time(NULL)

如果 C++ 是一个选项,您可以使用std::chrono 中的一个时钟来获得所需精度的时间。

【讨论】:

  • 谢谢,我现在确实会使用time()difftime() 来解决这个问题。这不完全是我想要的精度,但是哦,我的。
【解决方案3】:

在 Linux 上,您应该阅读 time(7)。它建议使用最近的 MacOSX(和 Linux)上应该存在的 POSIX 2001 clock_gettime。在 Linux 上,在不太旧的硬件上运行(例如,不到 6 年的笔记本电脑或台式机),clock_gettime 提供了很好的精度,通常为几十微秒或更好。它给出了秒和纳秒的度量(在struct timespec 中),但我不认为纳秒的数字非常准确。

确实,clock(3) 被告知要符合...

C89、C99、POSIX.1-2001。 POSIX 要求 CLOCKS_PER_SEC 等于 1000000 与实际分辨率无关。

最后,一些框架库提供了函数(包装目标特定的系统函数)来测量时间。查看POCO(C++ 中)或Glib(来自 GTK 和 C 中的 Gnome)。

【讨论】:

    【解决方案4】:

    我建议您尝试创建自己的clock() 函数。如果你想使用它,我已经创建了这个简单的函数,类似于 Linux 上的 clock() 函数:

    clock_t clock_unix_like_systems(void)
    {   
        struct timespec current_time;
        if (clock_gettime(CLOCK_BOOTTIME,&current_time) != 0) return -1;
        return current_time.tv_sec * CLOCKS_PER_SEC + current_time.tv_nsec /(1000000000 /CLOCKS_PER_SEC); 
    }
    

    注意: 请记住关于此函数的两件事,1)您可以更改变量的类型。 2) 您可以使用errno 全局变量来描述有关特定操作失败原因的详细信息。

    例如,如果您想从头开始创建 Arduino 的 delay() 函数,则可以使用它,如下例所示:

        int delay(int millis)
        {   
            if (millis < 0)
            {
                printf("Time must be positive");
            
    
        return -1;
        }
        clock_t start;int  finish;
        long int compare = (long int) millis * 1000;
        start = clock_unix_like_systems();
        while(finish <= compare) 
        {
            finish = (long int) (clock_unix_like_systems() - start);
    
        }
        return 0;
    }
    

    我希望这会有所帮助。

    【讨论】:

    • 为了测量经过的时间(例如“elapsed = clock() - old_clock_value”),gettimeofday()(受时间调整和可能的闰秒影响)基本上是不可靠的。例如。 clock_unix_like_systems() - old_clock_value 的结果可能是否定的。
    • 我完全同意gettimeofday() 的时间调整影响。但是,问题与 Linux 上的 clock() 函数有关。如果您尝试在 Linux 上获取经过的时间(即 elapsed = clock() - old_clock_value)并使用它,例如,与我之前创建的 delay() 一起使用,您会看到获取的时间不是我们感知的物理时间。因此,我建议使用一个新的" clock() " 函数,不管它有什么影响,尝试在 Linux 上像 clock() 在 Windows 上一样工作。
    • 我认为在 Linux 上测量经过时间的正确工具是 clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ...),尤其是在您想要高精度和低开销的情况下。请注意,在现代系统上,1 毫秒的延迟相当于几百万个 CPU 周期(最好要求操作系统进行任务切换,并避免在延迟很长时忙于等待)。
    • clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ...) 函数不起作用,因为来自timespec 结构的tv_sec 返回零。 (在这种情况下,您不知道已经过去了多少秒)。但是,我将clock_id CLOCK_PROCESS_CPUTIME_ID 更改为CLOCK_REALTIME,并获得了tv_sec 值。感谢您对@Brendan 的评论,我已经使用clock_gettime(CLOCK_REALTIME,...) 函数更新了代码。
    • 糟糕。我把它搞砸了。 CLOCK_PROCESS_CPUTIME_ID 是错误的,因为它不计算进程挂起的时间。 CLOCK_REALTIME 也不正确(受系统时间不连续跳转的影响 - 与 gettimeofday() 不正确的原因相同)。你想要的是CLOCK_MONOTONIC_RAWCLOCK_BOOTTIME。对不起。
    猜你喜欢
    • 2011-11-17
    • 2012-08-27
    • 1970-01-01
    • 1970-01-01
    • 2020-09-12
    • 2016-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多