内容

名称

Time::HiRes - 高精度闹钟、休眠、gettimeofday、间隔计时器

概要

use Time::HiRes qw( usleep ualarm gettimeofday tv_interval nanosleep
                    clock_gettime clock_getres clock_nanosleep clock
                    stat lstat utime);

usleep ($microseconds);
nanosleep ($nanoseconds);

ualarm ($microseconds);
ualarm ($microseconds, $interval_microseconds);

$t0 = [gettimeofday];
($seconds, $microseconds) = gettimeofday;

$elapsed = tv_interval ( $t0, [$seconds, $microseconds]);
$elapsed = tv_interval ( $t0, [gettimeofday]);
$elapsed = tv_interval ( $t0 );

use Time::HiRes qw ( time alarm sleep );

$now_fractions = time;
sleep ($floating_seconds);
alarm ($floating_seconds);
alarm ($floating_seconds, $floating_interval);

use Time::HiRes qw( setitimer getitimer );

setitimer ($which, $floating_seconds, $floating_interval );
getitimer ($which);

use Time::HiRes qw( clock_gettime clock_getres clock_nanosleep
                    ITIMER_REAL ITIMER_VIRTUAL ITIMER_PROF
                    ITIMER_REALPROF );

$realtime   = clock_gettime(CLOCK_REALTIME);
$resolution = clock_getres(CLOCK_REALTIME);

clock_nanosleep(CLOCK_REALTIME, 1.5e9);
clock_nanosleep(CLOCK_REALTIME, time()*1e9 + 10e9, TIMER_ABSTIME);

my $ticktock = clock();

use Time::HiRes qw( stat lstat );

my @stat = stat("file");
my @stat = stat(FH);
my @stat = lstat("file");

use Time::HiRes qw( utime );
utime $floating_seconds, $floating_seconds, file...;

描述

Time::HiRes 模块实现了 Perl 对 usleepnanosleepualarmgettimeofdaysetitimer/getitimer 系统调用的接口,换句话说,就是高精度时间和计时器。有关用法,请参见下面的 "示例" 部分和测试脚本;有关底层 nanosleepusleepualarmgettimeofdaysetitimer/getitimer 调用的描述,请参见您的系统文档。

如果您的系统缺少gettimeofday()或其模拟,则您无法获得gettimeofday()tv_interval()的单参数形式。如果您的系统缺少nanosleep()usleep()select()poll,则您无法获得Time::HiRes::usleep()Time::HiRes::nanosleep()Time::HiRes::sleep()。如果您的系统同时缺少ualarm()setitimer(),则您无法获得Time::HiRes::ualarm()Time::HiRes::alarm()

如果您尝试在use语句中导入未实现的函数,它将在编译时失败。

如果您的亚秒级睡眠是使用nanosleep()而不是usleep()实现的,您可以将亚秒级睡眠与信号混合使用,因为nanosleep()不使用信号。但是,这不可移植,您应该首先检查&Time::HiRes::d_nanosleep的真值以查看您是否拥有nanosleep,然后仔细阅读您的nanosleep() C API 文档以了解任何特殊情况。

如果您将nanosleep用于除将睡眠与信号混合之外的其他用途,请考虑一下 Perl 是否是您应该用于需要纳秒精度的工作的工具。

请记住,除非您正在使用硬实时系统,否则任何时钟和计时器都会不精确,尤其是在您使用抢占式多用户系统时。了解挂钟时间和进程时间之间的区别(在类 Unix 系统中,用户时间和系统时间的总和)。任何尝试睡眠 X 秒的操作很可能最终会睡眠超过该时间,但不要惊讶,如果您最终睡眠时间略

以下函数可以从此模块导入。默认情况下不导出任何函数。

gettimeofday ()

在数组上下文中,返回一个包含自纪元以来的秒数和微秒数的两个元素数组。在标量上下文中,返回浮点秒数,如Time::HiRes::time()(见下文)。

usleep ( $useconds )

睡眠指定的微秒数(百万分之一秒)。返回实际睡眠的微秒数。可以睡眠超过一秒,与usleep系统调用不同。也可以睡眠零秒,这通常类似于线程让步。另请参见Time::HiRes::sleep()clock_nanosleep().

不要期望 usleep() 精确到微秒。

nanosleep ( $nanoseconds )

休眠指定纳秒数(1e9 秒)。返回实际休眠的纳秒数(仅精确到微秒,最接近的千分之一秒)。可以休眠超过一秒。也可以休眠零秒,这通常类似于线程让步。另请参见Time::HiRes::sleep()Time::HiRes::usleep()clock_nanosleep()

不要期望 nanosleep() 精确到纳秒。即使精确到千分之一秒也是不错的。

ualarm ( $useconds [, $interval_useconds ] )

发出ualarm调用;$interval_useconds是可选的,如果未指定,则为零,导致类似alarm的行为。

返回闹钟中剩余的时间(以微秒为单位),如果发生错误,则返回undef

ualarm(0) 将取消未完成的 ualarm()。

请注意,闹钟和休眠之间的交互是未指定的。

tv_interval

tv_interval ( $ref_to_gettimeofday [, $ref_to_later_gettimeofday] )

返回两个时间之间的浮点秒数,这两个时间应该由gettimeofday()返回。如果省略第二个参数,则使用当前时间。

time ()

返回自纪元以来的浮点秒数。此函数可以导入,从而成为核心 Perl 提供的time的良好替代品;请参见下面的"示例"

注意 1:此更高分辨率计时器返回的值可能小于或大于核心time(),具体取决于您的平台是向上、向下还是舍入到最接近的秒以获得核心time(),但自然地,差异不应超过半秒。如果您的系统可用,另请参见"clock_getres"

注意 2:自 2001 年 9 月 9 日星期日格林威治标准时间凌晨 1:46:40 起,当自纪元以来的time()秒数翻转到 1_000_000_000 时,Perl 的默认浮点格式和自纪元以来的秒数共同导致了明显的错误:如果您打印Time::HiRes::time()的值,您似乎只获得了五位小数,而不是承诺的六位(微秒)。不用担心,微秒就在那里(假设您的平台首先支持这种粒度)。发生的事情是 Perl 的默认浮点格式只输出 15 位数字。在这种情况下,这意味着小数点前十位数字和小数点后五位数字。要查看微秒,您可以使用printf/sprintf"%.6f",或者使用列表上下文中的gettimeofday()函数,它将为您提供秒和微秒作为两个单独的值。

sleep ( $floating_seconds )

休眠指定秒数。返回实际休眠的秒数(浮点数)。此函数可以导入,从而成为 perl 提供的 sleep 的良好替代品,请参见下面的 "示例"

请注意,闹钟和休眠之间的交互是未指定的。

alarm ( $floating_seconds [, $interval_floating_seconds ] )

在指定秒数后发送 SIGALRM 信号。如果可用,则使用 setitimer() 实现,否则使用 ualarm()$interval_floating_seconds 参数是可选的,如果未指定,则为零,从而导致类似 alarm() 的行为。此函数可以导入,从而成为 perl 提供的 alarm 的良好替代品,请参见下面的 "示例"

返回警报中剩余的时间(以秒为单位),如果发生错误,则返回 undef

注意 1:对于某些操作系统和 Perl 版本的组合,SIGALRM 会重新启动 select(),而不是中断它。这意味着 alarm() 后跟 select() 可能一起花费为 alarm()select() 指定的时间之和,而不仅仅是 alarm() 的时间。

请注意,闹钟和休眠之间的交互是未指定的。

setitimer ( $which, $floating_seconds [, $interval_floating_seconds ] )

启动一个间隔计时器:在一定时间后,会收到一个信号($which),并且可能会以一定间隔继续收到更多信号。要禁用“itimer”,请使用 $floating_seconds 为零。如果 $interval_floating_seconds 设置为零(或未指定),则计时器将在下一个发送的信号之后禁用。

使用间隔计时器可能会干扰 alarm()sleep()usleep()。用标准术语来说,“交互是未指定的”,这意味着任何事情都可能发生:它可能有效,也可能无效。

在标量上下文中,返回计时器中剩余的时间。

在列表上下文中,返回剩余时间和间隔。

通常有三个或四个间隔计时器(信号)可用:$which 可以是 ITIMER_REALITIMER_VIRTUALITIMER_PROFITIMER_REALPROF。请注意,哪些可用取决于:真正的 UNIX 平台通常具有前三个,但只有 Solaris 似乎具有 ITIMER_REALPROF(用于分析多线程程序)。不幸的是,Win32 没有间隔计时器。

ITIMER_REAL 导致类似于 alarm() 的行为。时间以实际时间计算;也就是说,时钟时间。当计时器到期时,会传递 SIGALRM

ITIMER_VIRTUAL 在(进程)虚拟时间中计算时间;也就是说,只有在进程运行时才会计算。在多处理器/用户/CPU 系统中,这可能比实际时间或时钟时间多或少。(此时间也称为用户时间。)当计时器到期时,会传递 SIGVTALRM

ITIMER_PROF 在进程虚拟时间或操作系统代表进程运行(例如 I/O)时计算时间。(此时间也称为系统时间。)(用户时间和系统时间的总和称为CPU 时间。)当计时器到期时,会传递 SIGPROFSIGPROF 可以中断系统调用。

多线程程序的间隔计时器的语义是特定于系统的,一些系统可能支持额外的间隔计时器。例如,哪个线程接收信号是未指定的。请参阅您的 setitimer(2) 文档。

getitimer ( $which )

返回由 $which 指定的间隔计时器中的剩余时间。

在标量上下文中,将返回剩余时间。

在列表上下文中,将返回剩余时间和间隔。间隔始终是您使用 setitimer() 设置的值。

clock_gettime ( $which )

以秒为单位返回由 $which 指定的 POSIX 高分辨率计时器的当前值。所有支持 POSIX 高分辨率计时器的实现都应该至少支持 $which 值为 CLOCK_REALTIME,它应该返回接近 gettimeofday 结果的结果,或自 1970 年 1 月 1 日 00:00:00:00 格林威治标准时间 (GMT) 以来经过的秒数。不要假设 CLOCK_REALTIME 为零,它可能为一或其他值。另一个可能有用(但并非在所有地方都可用)的值是 CLOCK_MONOTONIC,它保证单调递增的时间值(与 time() 或 gettimeofday() 不同,它们可以被调整)。请参阅您的系统文档以了解其他可能支持的值。

clock_getres ( $which )

以秒为单位返回由 $which 指定的 POSIX 高分辨率计时器的分辨率。所有支持 POSIX 高分辨率计时器的实现都应该至少支持 $which 值为 CLOCK_REALTIME,请参阅 "clock_gettime"

注意:返回的分辨率可能过于乐观。即使分辨率很高(一个很小的数字),它也只意味着您可以使用该分辨率指定 clock_gettime() 和 clock_nanosleep() 的参数。系统可能无法真正以该分辨率测量事件,并且各种开销和整体系统负载肯定会影响任何计时。

clock_nanosleep ( $which, $nanoseconds, $flags = 0)

休眠指定纳秒数(1e9 秒)。返回实际休眠的纳秒数。$which 是“时钟 ID”,与 clock_gettime() 和 clock_getres() 一致。flags 默认值为零,但可以指定 TIMER_ABSTIME(必须显式导出),这意味着 $nanoseconds 不是时间间隔(默认值),而是一个绝对时间。可以休眠超过一秒。也可以休眠零秒,这通常类似于线程让出。另请参见 Time::HiRes::sleep()Time::HiRes::usleep()Time::HiRes::nanosleep()

不要期望 clock_nanosleep() 精确到纳秒。即使达到千分之一秒的精度也很好。

clock()

以秒为单位返回自第一次调用 clock() 以来进程花费的进程时间(用户时间 + 系统时间)(定义不是“自进程启动以来”,尽管如果幸运的话,这些时间可能非常接近,具体取决于系统)。这意味着您可能需要存储第一次调用 clock() 的结果,并将该值从后续的 clock() 结果中减去。

返回的时间还包括已执行 wait() 的已终止子进程的进程时间。此值有点类似于核心 Perl 的 times() 返回的第二个值,但不一定完全相同。请注意,由于向后兼容性限制,返回值可能会在大约 2147 秒或大约 36 分钟后环绕。

stat
stat FH
stat EXPR
lstat
lstat FH
lstat EXPR

"perlfunc 中的 stat""perlfunc 中的 lstat" 相同,但如果操作系统和文件系统都支持此类时间戳,则访问/修改/更改文件时间戳的精度为亚秒级。要覆盖标准 stat()

use Time::HiRes qw(stat);

测试 &Time::HiRes::d_hires_stat 的值以确定操作系统是否支持亚秒级文件时间戳:大于零的值表示是。不幸的是,没有简单的方法来确定文件系统是否支持此类时间戳。UNIX 文件系统通常支持;NTFS 支持;FAT 不支持(FAT 时间戳粒度为秒)。

&Time::HiRes::d_hires_stat 的返回值为零表示 Time::HiRes::stat 是 CORE::stat() 的无操作直通(lstat 也是如此),因此时间戳将保持整数。即使 &Time::HiRes::d_hires_stat 非零,如果文件系统不支持亚秒级时间戳,也会发生这种情况。

无论如何,不要期望纳秒级分辨率,甚至微秒级分辨率。还要注意,修改/访问时间戳可能具有不同的分辨率,并且它们不需要同步,例如,如果操作是

write
stat # t1
read
stat # t2

来自 t2 的访问时间戳不必大于来自 t1 的修改时间戳:它可能相等或更小

utime LIST

"perlfunc 中的 utime" 相同,但能够以亚秒级分辨率设置访问/修改文件时间戳,如果操作系统和文件系统以及文件系统的挂载选项都支持此类时间戳。

要覆盖标准 utime()

use Time::HiRes qw(utime);

测试 &Time::HiRes::d_hires_utime 的值,以确定操作系统是否支持设置亚秒级文件时间戳。

与 CORE::utime() 一样,将 undef 作为 atime 和 mtime 传递将使用 NULL 参数调用系统调用。

实际可实现的亚秒级分辨率取决于操作系统和文件系统的组合。

修改时间戳可能根本不可能:例如,noatime 文件系统挂载选项可能会禁止您更改访问时间时间戳。

返回成功更改的文件数量。

示例

use Time::HiRes qw(usleep ualarm gettimeofday tv_interval);

$microseconds = 750_000;
usleep($microseconds);

# signal alarm in 2.5s & every .1s thereafter
ualarm(2_500_000, 100_000);
# cancel that ualarm
ualarm(0);

# get seconds and microseconds since the epoch
($s, $usec) = gettimeofday();

# measure elapsed time
# (could also do by subtracting 2 gettimeofday return values)
$t0 = [gettimeofday];
# do bunch of stuff here
$t1 = [gettimeofday];
# do more stuff here
$t0_t1 = tv_interval $t0, $t1;

$elapsed = tv_interval ($t0, [gettimeofday]);
$elapsed = tv_interval ($t0); # equivalent code

#
# replacements for time, alarm and sleep that know about
# floating seconds
#
use Time::HiRes;
$now_fractions = Time::HiRes::time;
Time::HiRes::sleep (2.5);
Time::HiRes::alarm (10.6666666);

use Time::HiRes qw ( time alarm sleep );
$now_fractions = time;
sleep (2.5);
alarm (10.6666666);

# Arm an interval timer to go off first at 10 seconds and
# after that every 2.5 seconds, in process virtual time

use Time::HiRes qw ( setitimer ITIMER_VIRTUAL time );

$SIG{VTALRM} = sub { print time, "\n" };
setitimer(ITIMER_VIRTUAL, 10, 2.5);

use Time::HiRes qw( clock_gettime clock_getres CLOCK_REALTIME );
# Read the POSIX high resolution timer.
my $high = clock_gettime(CLOCK_REALTIME);
# But how accurate we can be, really?
my $reso = clock_getres(CLOCK_REALTIME);

use Time::HiRes qw( clock_nanosleep TIMER_ABSTIME );
clock_nanosleep(CLOCK_REALTIME, 1e6);
clock_nanosleep(CLOCK_REALTIME, 2e9, TIMER_ABSTIME);

use Time::HiRes qw( clock );
my $clock0 = clock();
... # Do something.
my $clock1 = clock();
my $clockd = $clock1 - $clock0;

use Time::HiRes qw( stat );
my ($atime, $mtime, $ctime) = (stat("istics"))[8, 9, 10];

C API

除了上面描述的 perl API 之外,还为扩展编写者提供了 C API。以下 C 函数在 modglobal 哈希中可用

name             C prototype
---------------  ----------------------
Time::NVtime     NV (*)()
Time::U2time     void (*)(pTHX_ UV ret[2])

这两个函数返回等效的信息(如 gettimeofday),但表示形式不同。名称 NVtimeU2time 主要是因为它们与操作系统无关。(gettimeofday 是以 Unix 为中心的,尽管一些平台(如 Win32 和 VMS)对其进行了模拟。)

以下是如何从 C 中使用 NVtime 的示例

NV (*myNVtime)(); /* Returns -1 on failure. */
SV **svp = hv_fetchs(PL_modglobal, "Time::NVtime", 0);
if (!svp)         croak("Time::HiRes is required");
if (!SvIOK(*svp)) croak("Time::NVtime isn't a function pointer");
myNVtime = INT2PTR(NV(*)(), SvIV(*svp));
printf("The current time is: %" NVff "\n", (*myNVtime)());

诊断

useconds 或间隔大于...

在 ualarm() 中,您尝试使用的微秒数或间隔(也以微秒为单位)大于 1_000_000,并且您的系统中没有 setitimer() 来模拟这种情况。

负时间尚未发明

您尝试使用负时间参数。

内部错误:useconds < 0(无符号... 有符号...)

出现严重错误 - 微秒数不能为负数,但现在却变为负数。可能是您的编译器有问题?

useconds 或 uinterval 等于或大于 1000000

在某些平台上,无法获得低于一秒且超过一秒的闹钟。

此平台未实现

某些调用在每个平台上都不可用,无论是真实的还是模拟的。

注意事项

请注意,核心 time() 可能进行的是四舍五入而不是截断。这意味着核心 time() 可能将时间报告为比 gettimeofday()Time::HiRes::time() 晚一秒。

调整系统时钟(无论是手动还是通过 ntp 等服务)可能会导致问题,特别是对于假设时间单调递增的长时间运行程序(请注意,并非所有平台都像 UNIX ntp 一样优雅地调整时间)。例如,在 Win32(以及 Cygwin 和 MinGW 等派生平台)中,Time::HiRes::time() 可能暂时偏离系统时钟(以及原始 time())最多 0.5 秒。Time::HiRes 最终会注意到这一点并重新校准。请注意,自 Time::HiRes 1.77 以来,clock_gettime(CLOCK_MONOTONIC) 可能会有所帮助(如果您的系统支持 CLOCK_MONOTONIC)。

某些系统具有 API 但没有实现:例如 QNX 和 Haiku 具有间隔计时器 API,但没有功能。

在 Sierra 之前的 macOS(10.12 之前的 OS X)中,clock_getres()、clock_gettime() 和 clock_nanosleep() 是使用 Mach 计时器模拟的;作为模拟的副作用,CLOCK_REALTIME 和 CLOCK_MONOTONIC 是同一个计时器。

gnukfreebsd 似乎具有非功能性的 futimens() 和 utimensat()(至少在 10.1 中):因此 hires utime() 不起作用。

另请参见

Perl 模块 BSD::ResourceTime::TAI64

您的系统文档,有关 clock(3)clock_gettime(2)clock_getres(3)clock_nanosleep(3)clock_settime(2)getitimer(2)gettimeofday(2)setitimer(2)sleep(3)stat(2)ualarm(3)

作者

D. Wegscheid <[email protected]> R. Schertler <[email protected]> J. Hietaniemi <[email protected]> G. Aas <[email protected]>

版权和许可

版权所有 (c) 1996-2002 Douglas E. Wegscheid。保留所有权利。

版权所有 (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008 Jarkko Hietaniemi。保留所有权利。

版权所有 (C) 2011, 2012, 2013 Andrew Main (Zefram) <[email protected]>

本程序是自由软件;您可以根据与 Perl 本身相同的条款重新发布和/或修改它。