我可以告诉 Perl 一些数据是不可变的以加快速度吗?
Posted
技术标签:
【中文标题】我可以告诉 Perl 一些数据是不可变的以加快速度吗?【英文标题】:Can I tell Perl that some data are immutable to speed things up? 【发布时间】:2009-05-18 21:43:40 【问题描述】:Perl 非常适合编写我通常需要做的那种字符串/文件解析程序。与 C/C++/JAVA 相比,我真正喜欢的是编写快速脚本和一次性代码所花费的时间微不足道。但是,我想学习如何加快速度。
例如,我想学习如何向 Perl 提供提示,以便它可以更好地做出一些决定——尤其是与字符串相关的事情。在我看来,无论您以后是否真的修改了副本,每当您执行任何操作时,Perl 都会复制一个字符串。这是设计使然(我可以用一些魔法将其关闭吗?)还是我在咆哮?
我真的很想把一些字符串当作(const char *
)。我确信我们总是不需要所有东西都是 std::string 涉及所有的包袱(让我们假设 std::string 类似于 Perl 字符串)。我可以提示 Perl 在某些字符串上执行此操作吗?
我记得在某篇文章中读到(如果可以的话,请发表评论)你可以向 Perl 暗示你不会修改某些变量,因此它消除了如果你要修改它等所需的额外包袱.
我相信 Perl 变量有两个指向同一个 Perl 变量的内部指针——一个可以存储一个数字,另一个可以存储一个字符串(字符数组)。我可以一直告诉 Perl 从头到尾选择一个吗?我可以让 Perl 将某些字符串视为 (const char *)
,这样它们就不会标记修改它们所需的功能吗?
例如,我在某处(可能是同一篇文章?)读到 unpack() 比 substr() 更快,因为 substr() 返回一个左值,因此您也可以对其进行操作。例如,如果我想用 'ef' 替换字符串的前两个字符,我可以这样写:
substr(string, 0, 2) = 'ef'; # string now begins with 'ef'
因此,除非我使用 substr() 的这个特殊功能,否则我最好使用 substr 吗?
我是不是一直在咆哮?
【问题讨论】:
你的意思是 substr(string, 0, 2) = 'ef';在你的例子中。 是的,丹尼尔!但是由于某种原因,我无法再进行修复了-它一直说找不到页面:-( 我个人从未见过使用字符串成为性能瓶颈的案例。他们只是使用更多的内存,而我们现在往往有大量的内存。你需要做一些 seriously 繁重的字符串工作才能出现内存/性能问题,如果你遇到了这个问题,你可能只是做错了什么。 【参考方案1】:您可以在带有Readonly::XS
的变量上设置SvREADONLY
标志,但这并不能提高效率。效率来自选择正确的算法,而不是通过编译器提示。如果您希望您的代码更快/使用更少的内存,请对其进行分析(请参阅Devel::NYTProf
)。当您发现瓶颈时,要么在那里使用不同的算法,要么改用XS
。
此外,如果您要尝试优化某些内容,请确保结果确实更快,这里是 substr 与 unpack:
Rate unpack substr
unpack 2055647/s -- -74%
substr 7989875/s 289% --
这是基准代码。
#!/usr/bin/perl
use strict;
use warnings;
use Benchmark;
my %subs = (
unpack => sub return unpack "a3", "foobarbaz" ,
substr => sub return substr "foobarbaz", 0, 3
);
for my $sub (keys %subs)
print "$sub => ", $subs$sub(), "\n";
Benchmark::cmpthese -1, \%subs;
【讨论】:
【参考方案2】:一般:
使用好的算法,除非必要,否则不要优化。如果是,请分析您的代码并对您的更改进行基准测试。现在是根据需要考虑 XS 或 Inline::C 的好时机。
a (const *) char 等价:
use constant Foo => 'bar';
由 perl 编译器创建 a minimal subroutine that can be inlined。您还可以创建自己的可内联常量函数
避免额外的复制:
典型的 perl 习惯用法做了一些“额外”的复制:
sub foo
my $bar = shift;
..do stuff with $bar...
许多人没有意识到 Perl 通过引用将参数传递给子例程。 @_
包含子例程参数的别名。
因此,您可以通过直接使用@_
来避免复制您的论点:
foo( $big_scalar );
sub foo
..do stuff with $_[0]...
.. sneakily risk modifying $big_scalar ..
当然,这是有风险的,因为如果你修改了值,就会修改调用值。仅当您需要保存 BIG 文件副本时才使用此选项。 (或者您明确想要修改调用参数。)
如果我需要移动一大块数据,但不打算修改它,我通常通过引用显式传递它,而不是乱用@_
;
foo( \$big_scalar );
sub foo
my $bar = shift;
... do stuff with $$bar ...
... can modify $big_scalar, but the pass by ref is explicit ...
[P]过早的优化是万恶之源
至少唐纳德·高德纳 (Donald Knuth) 是这么说的。这句话有很多智慧。
不正确的优化(声称是优化但实际上不是的代码)也很糟糕。
首先要清楚代码。 请务必分析您的代码以找到瓶颈。 请务必对您的优化进行基准测试,以确保它们有效。 记录您的优化代码,随身携带一些基准代码——明天的编译器可能与今天的响应方式不同。
【讨论】:
一边做一边写测试。 “优化”代码并让它开始表现不同真的很糟糕。 实际上,Tony Hoare 是说过早优化的人。 Knuth 只是引用了他的话。 有趣。看起来 Knuth 说是 Hoare 说的。霍尔说他没有,这可能是“常见的民间传说”或由于 Dijkstra。【参考方案3】:我与 Chas 合作,首先对您的代码进行基准测试和分析。我真的怀疑字符串复制是您的瓶颈,您会浪费大量时间而收效甚微。即使字符串复制确实是瓶颈,也要先在代码中寻找有缺陷的算法。 Perl 相对于 C 和 Java 的巨大潜在性能提升之一是因为它编写代码的速度非常快,它让您有大量额外的时间来分析、优化和改进算法。
如果字符串复制确实是您的瓶颈,请考虑简单地将大字符串作为引用传递。 C 中字符串指针的道德等价物。这将防止复制。请记住在使用它们之前取消引用它们。
sub foo
my $ref = shift;
print $$ref;
$string = "Some string";
foo(\$string);
【讨论】:
【参考方案4】:我记得在某篇文章中读到(如果可以的话,请发表评论)你可以暗示 perl 你不会修改某些变量,因此它消除了如果你要修改它等需要额外的包袱?
假设您说的是“use constant...”,我是否正确?
【讨论】:
没有。事实上,我在“某处”读到不应再“使用”使用常量,因为它可能会爆炸并且有更好的替代品(我又忘记了它是什么) Readonly(及其配套的 Readonly::XS)是常量 pragma 的替代方案。常量 pragma 很好,因为如果它可以在编译时折叠该值(例如 sleep MINUTE*1; 变为 sleep 60;),但 Readonly 很好,因为它是一个正常的标量并且可以这样使用(插值、引用、 ETC。)。对于像 AoA 这样的恒定复杂结构,Readonly 也更好。以上是关于我可以告诉 Perl 一些数据是不可变的以加快速度吗?的主要内容,如果未能解决你的问题,请参考以下文章
在 SORM 中更新数据似乎是可能的(尽管有人告诉我它针对的是不可变数据......)