0

以下代码将 0 作为函数的运行时。任何人都可以指出错误吗?

struct timeval start,end;
long seconds,useconds;
gettimeofday(&start, NULL);
int optimalpfs=optimal(n,ref,count);
gettimeofday(&end, NULL);
seconds  = end.tv_sec  - start.tv_sec;
useconds = end.tv_usec - start.tv_usec;
long opt_runtime = ((seconds) * 1000 + useconds/1000.0) + 0.5;
cout<<"\nOptimal Runtime is "<<opt_runtime<<"\n";

我得到相同的开始和结束时间。我得到以下输出

Optimal Runtime is 0

请告诉我错误。

4

4 回答 4

1

clock_gettime()POSIX 1003.1b-1993 为(and )指定接口clock_getres(),并通过 MON 选项提供了一种时钟类型,其clockid_t值为CLOCK_MONOTONIC(以便您的计时器不受系统时间调整的影响)。如果在您的系统上可用,那么这些函数会返回一个潜在分辨率低至 1 纳秒的结构,尽管后一个函数会准确地告诉您时钟的分辨率。

 struct timespec {
         time_t  tv_sec;         /* seconds */
         long    tv_nsec;        /* and nanoseconds */
 };

您可能仍需要在循环中多次运行测试函数,以使时钟记录超出其分辨率的任何时间,并且您可能希望循环运行足够多的时间以至少比时钟的分辨率。

请注意,显然 Linux 人员误读了 POSIX.1b 规范和/或不理解单调递增时钟的定义,并且他们的CLOCK_MONOTONIC时钟受系统时间调整的影响,因此您必须使用他们发明的非标准CLOCK_MONOTONIC_RAW时钟以获得真正的单调时钟。

或者,可以使用相关的 POSIX.1timer_settime()调用来设置一个正在运行的计时器,一个信号处理程序来捕获计时器传递的信号,并timer_getoverrun()找出在信号排队和最终传递之间经过了多长时间,然后设置您的循环运行直到计时器关闭,计算设置的时间间隔内的迭代次数,加上溢出。

当然,在抢占式多任务系统上,即使您的进程没有运行,这些时钟和计时器也会运行,因此它们对于基准测试并不是很有用。

稍微少见的是可选的 POSIX.1-1999clockid_t值,由fromCLOCK_PROCESS_CPUTIME_ID的存在表示,它表示调用进程的 CPU 时间时钟,给出的值表示调用进程的执行时间量。(更罕见的是 的 TCT 选项,由宏指示,它表示 CPU 时钟,给出的值表示调用线程的执行时间量。)_POSIX_CPUTIME<time.h>clockid_tCLOCK_THREAD_CPUTIME_ID_POSIX_THREAD_CPUTIME

不幸的是,POSIX 没有提到这些所谓的 CPUTIME 时钟是只计算用户时间,还是计算用户和系统(以及中断)时间,由进程或线程累积,所以如果你的代码在分析中进行任何系统调用,那么在内核模式下花费的时间可能会或可能不会被表示。

更糟糕的是,在多处理器系统上,如果您的进程在执行期间碰巧从一个 CPU 迁移到另一个 CPU,那么 CPUTIME 时钟的值可能完全是虚假的。实现这些 CPUTIME 时钟的计时器也可能在不同的 CPU 内核上以不同的速度运行,并且在不同的时间运行,这使它们的含义进一步复杂化。即它们可能并不意味着与实际挂钟时间相关的任何内容,而只是表明 CPU 周期数(只要始终使用相对时间并且用户知道执行时间可能因外部因素而异)。更糟糕的是,据报道,在 Linux CPU TimeStampCounter 上,基于 CPUTIME 时钟甚至可以报告进程已休眠的时间。

如果您的系统具有良好的工作getrusage()系统调用,那么它有望能够为您的进程在运行时分别struct timeval消耗的每个实际用户和系统时间提供一个。但是,由于这最多会使您回到微秒时钟,因此您需要重复运行测试代码足够多次以获得更准确的时间,调用getrusage()在循环之前一次,然后再一次,并计算给定时间之间的差异。对于简单的算法,这可能意味着运行它们数百万次,甚至更多。另请注意,在许多系统上,用户时间和系统时间之间的划分有些随意,如果在重复循环中单独检查,其中一个甚至可能出现倒退。但是,如果您的算法没有进行系统调用,那么对时间增量求和仍然应该是您的代码执行的合理总时间。

顺便说一句,在比较时间值时要小心,以免在字段中溢出或以负值结尾,正如@Nim 所建议的那样,或者可能像这样(来自 NetBSD's <sys/time.h>):

    #define timersub(tvp, uvp, vvp)                             \
        do {                                                    \
            (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;      \
            (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec;   \
            if ((vvp)->tv_usec < 0) {                           \
                (vvp)->tv_sec--;                                \
                (vvp)->tv_usec += 1000000;                      \
            }                                                   \
        } while (0)

(您甚至可能想要tv_usec在范围内更加偏执)

关于基准测试的另一重要注意事项:确保您的函数实际被调用,理想情况下是通过检查编译器的程序集输出。在驱动程序循环的单独源模块中编译您的函数通常会说服优化器保留调用。另一个技巧是让它返回一个你在循环内分配给定义为的变量的值volatile

于 2012-11-20T13:24:23.527 回答
0

我通常会做这样的计算:

long long ss = start.tv_sec * 1000000LL + start.tv_usec;
long long es = end.tv_sec * 1000000LL + end.tv_usec;

然后做点不一样的

long long microsec_diff = es - ss;

现在根据需要进行转换:

double seconds = microsec_diff / 1000000.;

通常,我不会为最后一步而烦恼,以微秒为单位进行所有计时。

于 2012-11-20T10:46:55.550 回答
0

你在这里有浮点数和整数的奇怪组合:

long opt_runtime = ((seconds) * 1000 + useconds/1000.0) + 0.5;

尝试使用:

long opt_runtime = (long)(seconds * 1000 + (float)useconds/1000);

这样,您将在几毫秒内获得结果。

于 2012-11-20T09:08:54.747 回答
0

的执行时间optimal(...)小于 的粒度gettimeofday(...)。这可能发生在 Windows 上。在 Windows 上,典型的粒度高达 20 毫秒。我在这里回答了一个相关的 gettimeofday(...)问题。对于 Linux,我问linux gettimeofday() 的微秒时间是如何获得的,它的精度是多少?并取得了不错的成绩。

SO 答案中描述了有关如何获得准确时间的更多信息。

于 2012-11-20T10:42:19.387 回答