我可以告诉 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 中更新数据似乎是可能的(尽管有人告诉我它针对的是不可变数据......)

mysql limit 是不是能加快查询速度?

减少 perl 启动时间的最佳方法

matlab中,如何加快数据运算速度?

包含2个以上列表的列表在循环中运行缓慢我可以使用Thread来加快速度吗?

我想使用 java 集合来加快处理速度但同时避免内存堆异常?