Orcish Maneuver:让Perl排序程序加速的方法
时间:2014-08-13 14:49 来源: 我爱IT技术网 作者:山风
Orcish Maneuver 是一个可以让 Perl 排序程序加速的操作方式,这个方法在法则的排序问题上非常有用。

在 Perl 程序中,如果遇到比较复杂的排序问题时,一般的程序设计师会会使用 sort 配合一个自己撰写的排序函数来处理。
举例来说,假设我们有一个 @file 数组,里面储存了许多文件名称,如果我们想要依照文件的更改时间来排序,通常会这样写:
- my @sortsorted = sort { -M $a <=> -M $b } @files;
其中 -M 会传回文件的相对更改时间(请参考 perldoc),然后 sort 再根据这个数值来进行排序。
由于 -M 这个运算符会需要去查询文件的更改时间,这个动作是比较费时的,如果文件数量很多的时候,就容易造成执行速度下降的问题,这时候就可以改用 Orcish Maneuver 的写法:
- my @sortsorted =
- sort { ( $m{$b} ||= -M $b ) <=> ( $m{$b} ||= -M $b ) }
- @files;
这里的 ||= 是一个比较少见的运算符,他只是将 == 与 || 合起来写而已,也就是说
$m{$a} ||= -M $a
就等同于
$m{$a} = $m{$a} || -M $a
而这样的写法在 sort 第一次遇到某个文件名称 $a 时,$m{$a} 会传回 undef,这时候 || 右方的 -M $a 就会被执行。
如果在 C 语言中,|| 只会传回 0 或 1(false 或 true),然而在 Perl 中的 || 运算符会传回实际执行的结果,所以这时候 -M $a 的执行结果就会储存至 $m{$a}。
如此一来,当下一次在遇到相同的文件名称 $a 时,他就可以直接使用 $m{$a} 里面所储存的结果,不用再执行一次 -M $a,这样就可以节省许多不必要的动作。
而这里的 %m 是一个暂时性的杂凑(hash),在使用前应该要确保它是空的,而且在排序完成之后就没有用了,所以建议可以这样写:
- {
- my %m;
- @sortsorted = sort ...
- }
让 %m 限制在这个范围(scope)之内。
以下是一个小测试程序,它会将目前目录中所有的文件名称读进来,然后使用 Perl 的 Benchmark 模块进行两种写法的排序测试:
- #!/usr/bin/perl
- use Benchmark;
- @files = <*>;
- timethese(0, {
- Ori => sub {
- my @sortsorted = sort { -M $a <=> -M $b } @files;
- },
- OM => sub {
- my @sortsorted =
- sort { ( $m{$b} ||= -M $b ) <=> ( $m{$b} ||= -M $b ) }
- @files;
- }
- }
- );
我拿了两万多个文件来测试,结果如下:
- Benchmark: running OM, Ori for at least 3 CPU seconds...
- OM: 3 wallclock secs ( 3.07 usr + 0.00 sys = 3.07 CPU) @ 43.65/s (n=134)
- Ori: 3 wallclock secs ( 0.85 usr + 2.31 sys = 3.16 CPU) @ 11.08/s (n=35)
执行效率在这个例子来看是相差 3.8 倍,但理论上文件数量越多,差异会越大。
本文来源 我爱IT技术网 http://www.52ij.com/jishu/12412.html 转载请保留链接。
- 评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)
-
