[英]Is there a faster way to get floating point bits than pack/unpack in Perl?
[英]Is Perl's unpack() ever faster than substr()?
有几次我读过unpack()
比substr()
快,特别是随着子串数量的增加。 但是,这个基准建议不然。 我的基准测试是否存在缺陷,或者unpack()
所谓的性能优势是旧版本Perl的延续?
use strict;
use warnings;
use Benchmark;
my ($data, $format_string, $n_substrings);
my %methods = (
unpack => sub { return unpack $format_string, $data },
substr => sub { return map {substr $data, $_, 1} 0 .. $n_substrings - 1 },
);
for my $exp (1 .. 5){
$n_substrings = 10 ** $exp;
print $n_substrings, "\n";
$format_string = 'a1' x $n_substrings;
$data = 9 x $n_substrings;
Benchmark::cmpthese -2, \%methods;
}
输出(在Windows上):
10
Rate unpack substr
unpack 131588/s -- -52%
substr 276802/s 110% --
100
Rate unpack substr
unpack 13660/s -- -57%
substr 31636/s 132% --
1000
Rate unpack substr
unpack 1027/s -- -68%
substr 3166/s 208% --
10000
Rate unpack substr
unpack 84.4/s -- -74%
substr 322/s 281% --
100000
Rate unpack substr
unpack 5.46/s -- -82%
substr 30.1/s 452% --
正如在一些答案中所指出的, unpack()
在Windows上表现不佳。 这是solaris机器上的输出 - 不是那么具有决定性,但是substr()
仍然赢得了比赛:
10
Rate unpack substr
unpack 202274/s -- -4%
substr 210818/s 4% --
100
Rate unpack substr
unpack 22015/s -- -9%
substr 24322/s 10% --
1000
Rate unpack substr
unpack 2259/s -- -9%
substr 2481/s 10% --
10000
Rate unpack substr
unpack 225/s -- -9%
substr 247/s 9% --
100000
Rate unpack substr
unpack 22.0/s -- -10%
substr 24.4/s 11% --
事实上,你的基准是有缺陷的,真的,非常有趣的方式,但它归结为你真正比较的是解包与地图可以扔掉一个列表的相对效率,因为Benchmark :: cmpthese()正在执行void上下文中的函数。
你的substr出现在顶部的原因是pp_ctl.c pp_mapwhile()中的这行代码:
if (items && gimme != G_VOID) {
即perl的地图神奇地跳过一堆工作(即为地图的结果分配存储),如果它知道它是在虚拟上下文中被调用的话!
(我对Windows的预感与上面提到的其他内容相比,基于Windows的perl内存分配很糟糕,所以跳过分配是一个更大的节省 - 只是预感,但是,我没有一个Windows框可以玩但实际的解压缩实现是直接的C代码,并且不应该与其他窗口大不相同。)
我有三种不同的解决方案来解决这个问题并产生更公平的比较:
这是我的%方法版本,包含所有三个版本:
my %methods = (
unpack_assign => sub { my @foo = unpack $format_string, $data; return },
unpack_loop => sub { for my $foo (unpack $format_string, $data) { } },
unpack_return_ref => sub { return [ unpack $format_string, $data ] },
unpack_return_array => sub { return unpack $format_string, $data },
substr_assign => sub { my @foo = map {substr $data, $_, 1} 0 .. ($n_substrings - 1) },
substr_loop => sub { for my $foo ( map {substr $data, $_, 1} 0 .. ($n_substrings - 1)) { } },
substr_return_ref => sub { return [ map {substr $data, $_, 1} 0 .. ($n_substrings - 1) ] },
substr_return_array => sub { return map { substr $data, $_, 1} 0 .. ($n_substrings - 1) },
);
我的结果是:
$ perl -v
This is perl, v5.10.0 built for x86_64-linux-gnu-thread-multi
$ perl foo.pl
10
Rate substr_assign substr_return_ref substr_loop unpack_assign unpack_return_ref unpack_loop unpack_return_array substr_return_array
substr_assign 101915/s -- -20% -21% -28% -51% -51% -65% -69%
substr_return_ref 127224/s 25% -- -1% -10% -39% -39% -57% -62%
substr_loop 128484/s 26% 1% -- -9% -38% -39% -56% -61%
unpack_assign 141499/s 39% 11% 10% -- -32% -32% -52% -57%
unpack_return_ref 207144/s 103% 63% 61% 46% -- -1% -29% -37%
unpack_loop 209520/s 106% 65% 63% 48% 1% -- -28% -37%
unpack_return_array 292713/s 187% 130% 128% 107% 41% 40% -- -12%
substr_return_array 330827/s 225% 160% 157% 134% 60% 58% 13% --
100
Rate substr_assign substr_loop substr_return_ref unpack_assign unpack_return_ref unpack_loop unpack_return_array substr_return_array
substr_assign 11818/s -- -25% -25% -26% -53% -55% -63% -70%
substr_loop 15677/s 33% -- -0% -2% -38% -40% -51% -60%
substr_return_ref 15752/s 33% 0% -- -2% -37% -40% -51% -60%
unpack_assign 16061/s 36% 2% 2% -- -36% -39% -50% -59%
unpack_return_ref 25121/s 113% 60% 59% 56% -- -4% -22% -35%
unpack_loop 26188/s 122% 67% 66% 63% 4% -- -19% -33%
unpack_return_array 32310/s 173% 106% 105% 101% 29% 23% -- -17%
substr_return_array 38910/s 229% 148% 147% 142% 55% 49% 20% --
1000
Rate substr_assign substr_return_ref substr_loop unpack_assign unpack_return_ref unpack_loop unpack_return_array substr_return_array
substr_assign 1309/s -- -23% -25% -28% -52% -54% -62% -67%
substr_return_ref 1709/s 31% -- -3% -6% -38% -41% -51% -57%
substr_loop 1756/s 34% 3% -- -3% -36% -39% -49% -56%
unpack_assign 1815/s 39% 6% 3% -- -34% -37% -48% -55%
unpack_return_ref 2738/s 109% 60% 56% 51% -- -5% -21% -32%
unpack_loop 2873/s 120% 68% 64% 58% 5% -- -17% -28%
unpack_return_array 3470/s 165% 103% 98% 91% 27% 21% -- -14%
substr_return_array 4015/s 207% 135% 129% 121% 47% 40% 16% --
10000
Rate substr_assign substr_return_ref substr_loop unpack_assign unpack_return_ref unpack_loop unpack_return_array substr_return_array
substr_assign 131/s -- -23% -27% -28% -52% -55% -63% -67%
substr_return_ref 171/s 30% -- -5% -6% -38% -42% -52% -57%
substr_loop 179/s 37% 5% -- -1% -35% -39% -50% -55%
unpack_assign 181/s 38% 6% 1% -- -34% -38% -49% -55%
unpack_return_ref 274/s 109% 60% 53% 51% -- -6% -23% -32%
unpack_loop 293/s 123% 71% 63% 62% 7% -- -18% -27%
unpack_return_array 356/s 171% 108% 98% 96% 30% 21% -- -11%
substr_return_array 400/s 205% 134% 123% 121% 46% 37% 13% --
100000
Rate substr_assign substr_return_ref substr_loop unpack_assign unpack_return_ref unpack_loop unpack_return_array substr_return_array
substr_assign 13.0/s -- -22% -26% -29% -51% -55% -63% -67%
substr_return_ref 16.7/s 29% -- -5% -8% -37% -43% -52% -58%
substr_loop 17.6/s 36% 5% -- -3% -33% -40% -50% -56%
unpack_assign 18.2/s 40% 9% 3% -- -31% -37% -48% -54%
unpack_return_ref 26.4/s 103% 58% 50% 45% -- -9% -25% -34%
unpack_loop 29.1/s 124% 74% 65% 60% 10% -- -17% -27%
unpack_return_array 35.1/s 170% 110% 99% 93% 33% 20% -- -12%
substr_return_array 39.7/s 206% 137% 125% 118% 50% 36% 13% --
回到原来的问题:“unpack()比substr()更快吗?” 答:总是,对于这种类型的应用 - 除非你不关心返回值;)
测试没有缺陷,但它是偏斜的。 如果您需要做的只是从字符串中提取一个相当简单的子字符串,那么substr会更好,但那就是它。 例如,使用substr即使这个简单的任务也不容易完成:
$foo = '123foo456789bar89012';
my ($t1,$t2,$t3,$t4,$t5) = unpack("A3A3A6A3A5",$foo);
那就是你应该看到substr和unpack之间的巨大差异。
我在Ubuntu 9下得到了与提问者相似的结果:
This is perl, v5.10.0 built for i486-linux-gnu-thread-multi
10
Rate unpack substr
unpack 535925/s -- -3%
substr 552749/s 3% --
100
Rate unpack substr
unpack 57957/s -- -5%
substr 61264/s 6% --
1000
Rate unpack substr
unpack 4716/s -- -22%
substr 6075/s 29% --
10000
Rate unpack substr
unpack 466/s -- -24%
substr 609/s 31% --
100000
Rate unpack substr
unpack 46.3/s -- -23%
substr 60.5/s 31% --
但我不确定这是否相关。 我不倾向于使用unpack进行简单的字符串提取,因为它的字符串格式不正确:-)
我认为它会闪耀的是提取编码整数和各种其他二进制信息,这是我将使用它。
你应该从马修和我的(以及你的)基准测试中得到的一点是,它将在很大程度上取决于环境因素。 请记住,速度虽然好,但不是最重要的 - 最终 - 我不认为我编写的代码会受到严重影响,因为每秒只能执行460万次提取而不是600万:-)您可能需要额外的性能,但我怀疑它是用Perl编写的大多数应用程序。
自从提出这个问题以来,我已经在各种条件下对substr
打了几次unpack
。 以下是我学到的一些内容:
不要以在void上下文中调用Perl函数的方式设置基准(正如我在原始问题中所做的那样;请参阅dlowe的有用响应)。 一些Perl函数在void上下文中调用时会有优化(并且这些优化似乎因操作系统而异),可能会使基准测试结果出现偏差。
如果您对substr
的使用涉及循环(例如,迭代列位置列表),则unpack
总是更快。 但是,在这种情况下, substr
的明显缓慢是由于循环的开销,而不是substr
本身。
如果只需要几个字段,则substr
通常比unpack
更快或更快。
如果需要多个字段,则unpack
和相同数量的substr
调用之间的头对头比较随着字段数量的增加而变化不大:两种方法在相同速率下变慢。
结果可能因操作系统而异。 在我的Windows XP计算机上,只要需要多个字段, unpack
就会略有优势。 在我工作场所的Solaris机器上, substr
总是更快,甚至在数百个领域。
结论 :无论字段数量多少, unpack
与substr
的性能都不是一个很大的问题。 使用最清晰的代码中的任何一种方法。 但是,如果您发现自己在循环结构中使用substr
,切换到unpack
将导致值得注意的速度提升。
不是说我不相信你的结果,但你运行这个系统是什么类型的系统? 我在Ubuntu 8.10(perl 5.10)上运行了你的脚本,结果如下:
mscharley@S04:~$ perl -v
This is perl, v5.10.0 built for x86_64-linux-gnu-thread-multi
mscharley@S04:~$ ./test.pl
10
Rate substr unpack
substr 587390/s -- -10%
unpack 650343/s 11% --
100
Rate substr unpack
substr 66060/s -- -5%
unpack 69433/s 5% --
1000
Rate substr unpack
substr 6847/s -- -2%
unpack 6977/s 2% --
10000
Rate substr unpack
substr 683/s -- -1%
unpack 693/s 1% --
100000
Rate substr unpack
substr 68.3/s -- -0%
unpack 68.4/s 0% --
我的本地Windows机器的结果(根据我的结果,我猜你正在使用它):
>perl -v
This is perl, v5.10.0 built for MSWin32-x86-multi-thread
>perl test.pl
10
Rate unpack substr
unpack 125210/s -- -50%
substr 252878/s 102% --
100
Rate unpack substr
unpack 12677/s -- -56%
substr 28854/s 128% --
1000
Rate unpack substr
unpack 963/s -- -66%
substr 2846/s 196% --
10000
Rate unpack substr
unpack 78.8/s -- -73%
substr 291/s 269% --
100000
Rate unpack substr
unpack 4.88/s -- -82%
substr 27.2/s 457% --
如果我不得不对差异做出很好的猜测,我会猜测并且说Windows没有本机打包/解包功能,因此Perl必须以某种方式模拟它。 反正我的2c。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.