【问题标题】:Unable to time command execution within C (popen)?无法在 C (popen) 中计时命令执行?
【发布时间】:2017-06-22 20:09:11
【问题描述】:

我一直在尝试计算调用 popen 需要多长时间才能完成。 popen 初始化一个进程,然后创建一个管道、分叉并调用 shell。在我的特殊情况下,我正在使用调用来读取另一个程序 stdout 输出。

问题:我希望我所做的调用能够返回正确的程序执行时间长度(测试程序大约需要 15 秒)。我得到的是程序完全没有时间完成(0.000223s)。尽管我尝试了所有各种功能,但我似乎无法正确计时。

这是我的问题的可重现示例。它由计时程序和计时程序运行的子程序组成(子程序在我的系统上运行大约需要15s):

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#ifdef __MACH__
#include <mach/clock.h>
#include <mach/mach.h>
#endif

#define MAXBUF 10

static void gettime (struct timespec *t) {
#ifdef __MACH__
    clock_serv_t cclock;
    mach_timespec_t mts;
    host_get_clock_service(mach_host_self(), REALTIME_CLOCK, &cclock);
    clock_get_time(cclock, &mts);
    mach_port_deallocate(mach_task_self(), cclock);
    t->tv_sec = mts.tv_sec;
    t->tv_nsec = mts.tv_nsec;
#else
    clock_gettime(CLOCK_REALTIME, t);
#endif
}

int main (void) {
    FILE *fp;
    struct timespec tic, toc;
    char *executableName = "./a.out";
    char answer[MAXBUF];

    gettime(&tic);
    if ((fp = popen(executableName, "r")) == NULL) {
        fprintf(stderr, "The file couldn't be opened.\n");
        return 1;
    }
    gettime(&toc);

    fgets(answer, MAXBUF, fp);
    double elapsed = (double)(toc.tv_nsec - tic.tv_nsec) / 1E9;
    fprintf(stdout, "The program says %s, and took %fs to run!\n", answer, elapsed);

    pclose(fp);
    return 0;
}

这是子程序:

#include <stdio.h>
#include <stdlib.h>

int timeWastingFunction (long long n) {
    if ((n % 2) == 0) {
        return 1;
    }
    for (int i = 1; i < (n / 2); i += 2) {
        if ((n % i) == 0) {
            return 1;
        }
    }
    return 0;
}

int main (void) {
    int start = 687217000;

    while (start--) {
        timeWastingFunction(start);
    }
    fprintf(stdout, "Hello!");
    return 0;
}

这可能看起来有点过头了,但我之前曾尝试使用clock_t(基于 CPU 的计时工具)进行计时,并从中得到了相同的答案。因此,我尝试了您在上面看到的this 解决方案。我选择了:CLOCK_REALTIME,因为它似乎适合这份工作。不幸的是,我无法指定此时钟是在每个进程还是每个线程级别(我希望它是独立于进程的)。

注意:我还没有尝试使用gettimeofday,但我不想这样做,因为它显然不适合以这种方式计时,具体取决于系统使用它的日期,并且正在逐步淘汰以支持clock_gettime

编辑:需要明确的是,当我运行它时会发生什么,调用 popen 的程序实际上会在其他程序运行的 15 秒内停止,然后打印“错误” ' 时间。它不会立即打印暗示它没有等待调用完成的时间。

【问题讨论】:

  • 您对clock_gettime 的使用是正确的[并且优先于gettimeofday]。您的问题是 popen 立即返回 [几乎]。它等待目标进程完成。试试:gettime(&amp;tic); popen(); ... ; pclose(); gettime(&amp;toc);
  • @CraigEstey 谢谢!如果我想使用我打开的文件,这是否需要我在计时后重新打开(在这种情况下运行)?或者有没有一种方法可以在没有重新打开开销并且不会毒害时间的情况下使用它?
  • 请注意,macOS Sierra (10.12.x) 现在确实支持clock_gettime()。但是,如果您在 Sierra 上编译代码以在旧系统上运行,您仍然无法使用它(如果您在旧版本的 Mac OS X 上编译它不可用,因此无法移植到 Sierra(很容易) 使用它)。
  • @JonathanLeffler 不幸的是,我仍然在运行 El-Capitan,但似乎我对 popen 计时的不当使用是其根源,而不是计时方法。感谢您让我知道 Sierra!

标签: c macos time clock


【解决方案1】:

popen() 只能叉开一个管道。您的测试仅显示 popen() 创建子节点和管道所花费的时间。

解决您的问题的一个简单方法是在您的pclose() 之后获取时间,请注意这并不完美,因为当您阅读孩子返回的数据时,它可能会在您致电pclose() 之前完成

加上你得到结果的解决方案坏了,你只做纳秒的区别,我在git上找到了解决方案:

void timespec_diff(struct timespec *start, struct timespec *stop,
                   struct timespec *result)
{
    if ((stop->tv_nsec - start->tv_nsec) < 0) {
        result->tv_sec = stop->tv_sec - start->tv_sec - 1;
        result->tv_nsec = stop->tv_nsec - start->tv_nsec + 1000000000;
    } else {
        result->tv_sec = stop->tv_sec - start->tv_sec;
        result->tv_nsec = stop->tv_nsec - start->tv_nsec;
    }

    return;
}

最后一件事是如果你想要日期,应该使用CLOCK_REALTIME。在这里,您只需要一个持续时间。所以你应该使用CLOCK_MONOTONIC 如果它在你的系统上可用,因为CLOCK_REALTIME 可以回滚。 (REALTIME_CLOCKhost_get_clock_service() 也是单调的)。

CLOCK_MONOTONIC:无法设置的时钟,表示自某个未指定起点以来的单调时间。

REALTIME_CLOCK:一种中等分辨率的时钟服务,(通常)跟踪自系统上次启动以来的时间。

所以工作代码可能如下所示:

int main (void) {
    FILE *fp;
    struct timespec tic, toc;
    char *executableName = "./a.out";
    char answer[MAXBUF];

    gettime(&tic);
    if ((fp = popen(executableName, "r")) == NULL) {
        fprintf(stderr, "The file couldn't be opened.\n");
        return 1;
    }

    fgets(answer, MAXBUF, fp);

    pclose(fp);
    gettime(&toc);
    struct timespec result;
    timespec_diff(&tic, &toc, &result);
    fprintf(stdout, "The program says %s, and took %lld.%.9lds\n", answer, (long long)result.tv_sec, result.tv_nsec);

    return 0;
}

学分:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-15
    • 2011-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多