This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Benchmark.pm: avoid long walltime on calibration
authorDavid Mitchell <davem@iabyn.com>
Mon, 9 Jun 2014 10:41:22 +0000 (11:41 +0100)
committerDavid Mitchell <davem@iabyn.com>
Mon, 9 Jun 2014 10:51:18 +0000 (11:51 +0100)
RT #122003

runloop() in Benchmark.pm does the equivalent of the following:

    $t = (times)[0];
    while ($t == (times)[0]) {}

so that it finishes in a position where the user CPU counter has just
incremented.

This is all well and good, but on some platforms (specifically OpenBSD
running under VirtualBox), the system CPU burned each time round the loop
to get times() is far greater than the user CPU burned. This can cause the
loop to run for minutes consuming system CPU until enough user CPU has
been burned to tick the user CPU counter.

The fix in this commit is to replace the empty body of the while loop with
something that does a gradually increasing amount of busy work.

lib/Benchmark.pm

index 9a43a2b..73b3211 100644 (file)
@@ -700,8 +700,18 @@ sub runloop {
     # getting a too low initial $n in the initial, 'find the minimum' loop
     # in &countit.  This, in turn, can reduce the number of calls to
     # &runloop a lot, and thus reduce additive errors.
+    #
+    # Note that its possible for the act of reading the system clock to
+    # burn lots of system CPU while we burn very little user clock in the
+    # busy loop, which can cause the loop to run for a very long wall time.
+    # So gradually ramp up the duration of the loop. See RT #122003
+    #
     my $tbase = Benchmark->new(0)->[1];
-    while ( ( $t0 = Benchmark->new(0) )->[1] == $tbase ) {} ;
+    my $limit = 1;
+    while ( ( $t0 = Benchmark->new(0) )->[1] == $tbase ) {
+        for (my $i=0; $i < $limit; $i++) { my $x = $i / 1.5 } # burn user CPU
+        $limit *= 1.1;
+    }
     $subref->();
     $t1 = Benchmark->new($n);
     $td = &timediff($t1, $t0);