内容

名称

Benchmark - Perl 代码运行时间的基准测试

语法

    use Benchmark qw(:all) ;

    timethis ($count, "code");

    # Use Perl code in strings...
    timethese($count, {
	'Name1' => '...code1...',
	'Name2' => '...code2...',
    });

    # ... or use subroutine references.
    timethese($count, {
	'Name1' => sub { ...code1... },
	'Name2' => sub { ...code2... },
    });

    # cmpthese can be used both ways as well
    cmpthese($count, {
	'Name1' => '...code1...',
	'Name2' => '...code2...',
    });

    cmpthese($count, {
	'Name1' => sub { ...code1... },
	'Name2' => sub { ...code2... },
    });

    # ...or in two stages
    $results = timethese($count,
        {
	    'Name1' => sub { ...code1... },
	    'Name2' => sub { ...code2... },
        },
	'none'
    );
    cmpthese( $results ) ;

    $t = timeit($count, '...other code...')
    print "$count loops of other code took:",timestr($t),"\n";

    $t = countit($time, '...other code...')
    $count = $t->iters ;
    print "$count loops of other code took:",timestr($t),"\n";

    # enable hires wallclock timing if possible
    use Benchmark ':hireswallclock';

说明

Benchmark 模块封装了许多例程,帮助你了解执行某些代码需要多长时间。

timethis - 多次运行一段代码

timethese - 多次运行多段代码

cmpthese - 将 timethese 的结果打印为比较图表

timeit - 运行一段代码并查看它运行了多长时间

countit - 查看一段代码在给定时间内运行了多少次

方法

new

返回当前时间。示例

use Benchmark;
$t0 = Benchmark->new;
# ... your code here ...
$t1 = Benchmark->new;
$td = timediff($t1, $t0);
print "the code took:",timestr($td),"\n";
debug

通过设置 $Benchmark::Debug 标志启用或禁用调试

Benchmark->debug(1);
$t = timeit(10, ' 5 ** $Global ');
Benchmark->debug(0);
iters

返回迭代次数。

标准导出

如果您使用 Benchmark 模块,以下例程将导出到您的命名空间中

timeit(COUNT, CODE)

参数:COUNT 是运行循环的次数,CODE 是要运行的代码。CODE 可以是代码引用或要评估的字符串;无论哪种方式,它都将在调用者的包中运行。

返回:Benchmark 对象。

timethis ( COUNT, CODE, [ TITLE, [ STYLE ]] )

对 CODE 进行 COUNT 次迭代。CODE 可以是用于评估的字符串或代码引用;无论哪种方式,CODE 都将在调用者的包中运行。结果将以 TITLE 后跟时间的方式打印到 STDOUT。如果未提供 TITLE,则默认为“timethis COUNT”。STYLE 确定输出的格式,如下面 timestr() 所述。

COUNT 可以为零或负数:这意味着运行的最少 CPU 秒数。零表示默认值 3 秒。例如,要至少运行 10 秒

timethis(-10, $code)

或至少运行 3 秒的两段代码测试

timethese(0, { test1 => '...', test2 => '...'})

CPU 秒数在 UNIX 术语中是指进程本身的用户时间加上系统时间,而不是实际(时钟)时间和子进程花费的时间。小于 0.1 秒的时间不被接受(例如,-0.01 作为计数将导致致命运行时异常)。

请注意,CPU 秒数是最少时间:CPU 调度和其他操作系统因素可能会使尝试变得复杂,从而花费更多时间。但是,基准输出也会告诉 $code 运行/秒的次数,这应该是一个比实际花费的秒数更有趣的数据。

返回 Benchmark 对象。

timethese ( COUNT, CODEHASHREF, [ STYLE ] )

CODEHASHREF 是对哈希的引用,其中包含名称作为键,以及每个值的字符串以进行评估或代码引用。对于 CODEHASHREF 中的每个 (KEY, VALUE) 对,此例程将调用

timethis(COUNT, VALUE, KEY, STYLE)

例程按 KEY 的字符串比较顺序调用。

COUNT 可以为零或负数,请参见 timethis()。

返回按名称键入的 Benchmark 对象的哈希引用。

timediff ( T1, T2 )

返回两个 Benchmark 时间之间的差值,作为适合传递给 timestr() 的 Benchmark 对象。

timestr ( TIMEDIFF, [ STYLE, [ FORMAT ] ] )

返回一个字符串,该字符串以请求的 STYLE 格式化 TIMEDIFF 对象中的时间。TIMEDIFF 预计是类似于 timediff() 返回的 Benchmark 对象。

STYLE 可以是 'all'、'none'、'noc'、'nop' 或 'auto' 中的任何一个。'all' 显示 5 个可用时间中的每一个('wallclock' 时间、用户时间、系统时间、子进程的用户时间和子进程的系统时间)。'noc' 显示除两个子进程时间之外的所有时间。'nop' 仅显示 wallclock 和两个子进程时间。'auto'(默认值)将作为 'all',除非子进程时间都为零,在这种情况下它将作为 'noc'。'none' 阻止输出。

FORMAT 是 printf(3)- 样式格式说明符(不带前导 '%'),用于打印时间。其默认值为 '5.2f'。

可选导出

如果您明确要求导入以下例程,它们将导出到您的名称空间中

clearcache ( COUNT )

清除空循环的 COUNT 轮的缓存时间。

clearallcache ( )

清除所有缓存时间。

cmpthese ( COUNT, CODEHASHREF, [ STYLE ] )
cmpthese ( RESULTSHASHREF, [ STYLE ] )

可选地调用 timethese(),然后输出比较图表。此

cmpthese( -1, { a => "++\$i", b => "\$i *= 2" } ) ;

输出类似于

       Rate    b    a
b 2831802/s   -- -61%
a 7208959/s 155%   --

此图表按从最慢到最快的顺序排序,并显示每对测试之间的百分比速度差。

cmpthese 还可以传递 timethese() 返回的数据结构

$results = timethese( -1,
    { a => "++\$i", b => "\$i *= 2" } ) ;
cmpthese( $results );

以防您想同时查看两组结果。如果第一个参数是未祝福的哈希引用,那就是 RESULTSHASHREF;否则就是 COUNT。

返回对行的 ARRAY 的引用,每行都是来自上述图表的一个 ARRAY 的单元格,包括标签。此

my $rows = cmpthese( -1,
    { a => '++$i', b => '$i *= 2' }, "none" );

返回类似于

[
    [ '',       'Rate',   'b',    'a' ],
    [ 'b', '2885232/s',  '--', '-59%' ],
    [ 'a', '7099126/s', '146%',  '--' ],
]

注意:此结果值与返回 timethese() 结果结构的旧版本不同。如果您需要旧版本,只需使用上面所示的两条语句 timethese...cmpthese 惯用语即可。

顺便说一句,请注意两个示例之间结果值的差异;这是基准测试的典型情况。如果这是真正的基准测试,您可能需要运行更多迭代。

countit(TIME, CODE)

参数:TIME 是运行 CODE 的最短时间,而 CODE 是要运行的代码。CODE 可以是代码引用或要进行 eval 的字符串;无论哪种方式,它都将在调用者的程序包中运行。

TIME 不是负数。countit() 将多次运行循环,以计算在运行 TIME 之前 CODE 的速度。由于系统时钟分辨率,实际运行时间通常会大于 TIME,因此最好查看迭代次数除以您关注的时间,而不仅仅是迭代次数。

返回:Benchmark 对象。

disablecache ( )

禁用空循环的计时缓存。这将强制 Benchmark 为每个新计时代码重新计算这些计时。

enablecache ( )

启用空循环的计时缓存。空循环的 COUNT 轮所花费的时间仅针对使用的每个不同的 COUNT 计算一次。

timesum ( T1, T2 )

将两个 Benchmark 时间相加,并返回一个 Benchmark 对象,该对象适合传递给 timestr()。

:hireswallclock

如果已安装 Time::HiRes 模块,则可以为 Benchmark 指定特殊标记 :hireswallclock(如果 Time::HiRes 不可用的,则该标记将被忽略)。此标记将使时钟时间以微秒为单位测量,而不是整数秒。但请注意,速度计算仍以 CPU 时间进行,而不是时钟时间。

基准对象

此模块中的许多函数返回基准对象,或者在 timethese() 的情况下,返回对哈希的引用,其值是基准对象。如果您想存储或进一步处理基准函数的结果,这将非常有用。

基准对象在内部保存计时值,如下面 "NOTES" 中所述。可以使用以下方法访问它们

cpu_p

主(父)进程的总 CPU(用户 + 系统)。

cpu_c

任何子进程的总 CPU(用户 + 系统)。

cpu_a

父进程和任何子进程的总 CPU。

real

实际经过时间“时钟秒”。

iters

运行的迭代次数。

以下说明了基准对象的用法

$result = timethis(100000, sub { ... });
print "total CPU = ", $result->cpu_a, "\n";

NOTES

数据存储为 time 和 times 函数的值列表

($real, $user, $system, $children_user, $children_system, $iters)

以整个循环的秒数为单位(不除以回合数)。

计时使用 time(3) 和 times(3) 完成。

代码在调用者的包中执行。

空循环的时间(具有相同回合数但循环体为空的循环)从实际循环的时间中减去。

可以缓存空循环时间,键是回合数。可以使用如下调用控制缓存

clearcache($key);
clearallcache();

disablecache();
enablecache();

默认情况下,缓存处于关闭状态,因为它(通常会轻微地)降低准确性,并且通常不会明显影响运行时间。

EXAMPLES

例如,

use Benchmark qw( cmpthese ) ;
$x = 3;
cmpthese( -5, {
    a => sub{$x*$x},
    b => sub{$x**2},
} );

输出类似以下内容

Benchmark: running a, b, each for at least 5 CPU seconds...
       Rate    b    a
b 1559428/s   -- -62%
a 4152037/s 166%   --

use Benchmark qw( timethese cmpthese ) ;
$x = 3;
$r = timethese( -5, {
    a => sub{$x*$x},
    b => sub{$x**2},
} );
cmpthese $r;

输出类似以下内容

Benchmark: running a, b, each for at least 5 CPU seconds...
         a: 10 wallclock secs ( 5.14 usr +  0.13 sys =  5.27 CPU) @ 3835055.60/s (n=20210743)
         b:  5 wallclock secs ( 5.41 usr +  0.00 sys =  5.41 CPU) @ 1574944.92/s (n=8520452)
       Rate    b    a
b 1574945/s   -- -59%
a 3835056/s 144%   --

INHERITANCE

Benchmark 不从除 Exporter 之外的任何其他类继承。

CAVEATS

将 eval 后的字符串与代码引用进行比较会得到不准确的结果:代码引用显示的执行时间会比等效的 eval 后的字符串略慢。

实际时间计时使用 time(2) 完成,因此粒度仅为一秒。

短测试可能会产生负数,因为 perl 看起来执行空循环的时间比短测试的时间长;请尝试

timethis(100,'1');

空循环的系统时间可能略长于具有实际代码的循环的系统时间,因此差值最终可能为 < 0。

SEE ALSO

Devel::NYTProf - Perl 代码分析器

作者

Jarkko Hietaniemi <[email protected]>、Tim Bunce <[email protected]>

修改历史

1994 年 9 月 8 日;作者 Tim Bunce。

1997 年 3 月 28 日;作者 Hugo van der Sanden:增加了对代码引用的支持和已记录的“debug”方法;改进了文档。

1997 年 4 月 4 日至 7 日:作者 Jarkko Hietaniemi,增加了运行一段时间的功能。

1999 年 9 月;作者 Barrie Slaymaker:数学修正以及准确性和效率调整。添加了 cmpthese()。现在从 timethese() 返回结果。公开了 countit()(以前是 runfor())。

2001 年 12 月;作者 Nicholas Clark:让 timestr() 识别“none”样式并返回空字符串。如果 cmpthese 调用 timethese,则让它传递样式。(以便“none”会禁止输出)。让子 new 将其调试输出转储到 STDERR,以与其他所有内容保持一致。在编写回归测试时发现的所有错误。

2002 年 9 月;作者 Jarkko Hietaniemi:添加“:hireswallclock”特殊标记。

2004 年 2 月;作者 Chia-liang Kao:当样式为“nop”时,让 cmpthese 和 timestr 对子项使用时间统计信息,而不是父项。

2007 年 11 月;作者 Christophe Grosjean:让 cmpthese 和 timestr 根据样式参数一致地计算时间,默认值不再是“noc”而是“all”。