Well here's an interesting thing. With yet another example of building filenames out of localtime kicking around, I thought I'd build a benchmark once and for all to show how much faster using an anonymous sub than the other techniques. This is the code I wrote:
#! /usr/bin/perl -w
use strict;
use POSIX;
use Benchmark;
sub via_anonsub {
sub { sprintf '%04d%02d%02d%02d%02d%02d%05d',
$_[5]+1900, $_[4]+1, $_[3], $_[2], $_[1], $_[0], $$
}->(localtime())
}
sub via_sprintf {
my @time = localtime();
sprintf '%04d%02d%02d%02d%02d%02d%05d',
$time[5]+1900, $time[4]+1, $time[3], $time[2], $time[1], $time[0],
+ $$
}
sub via_concat {
my @time = localtime();
$time[4]++;
$time[5]+=1900;
$time[$_] = $time[$_]<10? "0".$time[$_]:$time[$_] for (0..5);
my $filename = $time[5].$time[4].$time[3].$time[2].$time[1].$time[0]
+.$$
}
sub via_posix {
strftime( "%Y%m%d%H%M%S$$", localtime() )
}
print <<"PROOF";
via_concat: ${\via_concat()}
via_anonsub: ${\via_anonsub()}
via_posix: ${\via_posix()}
via_sprintf: ${\via_sprintf()}
PROOF
timethese( shift || 10000, {
'via_anonsub' => \&via_anonsub,
'via_concat' => \&via_concat,
'via_posix' => \&via_posix,
'via_sprintf' => \&via_sprintf,
});
When run on an older Perl (v5.005_03) this produces the following output:
$ perl filename 200000
via_concat: 2002091114542256876
via_anonsub: 2002091114542256876
via_posix: 2002091114542256876
via_sprintf: 2002091114542256876
Benchmark: timing 200000 iterations of via_anonsub, via_concat, via_po
+six, via_sprintf...
via_anonsub: 7 wallclock secs ( 5.60 usr + 0.28 sys = 5.88 CPU)
via_concat: 21 wallclock secs (18.71 usr + 0.55 sys = 19.27 CPU)
via_posix: 13 wallclock secs ( 9.59 usr + 0.62 sys = 10.20 CPU)
via_sprintf: 10 wallclock secs ( 6.67 usr + 0.39 sys = 7.06 CPU)
But just for kicks, I thought I'd take it for a spin on a new machine running 5.8.0 and see what change, if any, appeared. I changed the code (the benchmark code, not the underlying snippets) a bit to use cmpthese instead, and this gives:
via_concat: 2002091114275977636
via_anonsub: 2002091114275977636
via_posix: 2002091114275977636
via_sprintf: 2002091114275977636
Benchmark: running via_anonsub, via_concat, via_posix, via_sprintf for
+ at least 10 CPU seconds...
via_anonsub: 12 wallclock secs (10.12 usr + 0.51 sys = 10.62 CPU) @ 5
+3156.42/s (n=564787)
via_concat: 10 wallclock secs (10.20 usr + 0.24 sys = 10.44 CPU) @ 3
+0435.83/s (n=317674)
via_posix: 10 wallclock secs ( 9.57 usr + 0.93 sys = 10.50 CPU) @ 6
+0954.29/s (n=640020)
via_sprintf: 10 wallclock secs (10.20 usr + 0.33 sys = 10.53 CPU) @ 4
+4919.45/s (n=473058)
Rate via_concat via_sprintf via_anonsub via_posix
via_concat 30436/s -- -32% -43% -50%
via_sprintf 44919/s 48% -- -15% -26%
via_anonsub 53156/s 75% 18% -- -13%
via_posix 60954/s 100% 36% 15% --
I'm bummed. Somewhere along the line of perl's evolution, the performance of POSIX got better and/or the handling of subroutine calls got worse. Oh well, you can't always use POSIX, and this anonymous subroutine approach works well for other system calls that return multiple values, such as stat and getpwnam. And I can live with 15% ineffeciency I guess. Such is life.
update: It's faster because it doesn't have to deal with building up and tearing down lexicals. And the sub is compiled at, ah, compile time. That's why it should be faster. Look more closely at the code; I'm not building closures.
I used to assume that using POSIX was slower because it was XS code and paying the XS/Perl boundary-crossing cost. Either calling XS is now cheaper, or creating lexicals is now more expensive, relatively speaking.
print@_{sort keys %_},$/if%_=split//,'= & *a?b:e\f/h^h!j+n,o@o;r$s-t%t#u' |