【问题标题】:Perl: Speeding up evalPerl:加速评估
【发布时间】:2021-04-30 01:17:27
【问题描述】:

eval 处理字符串时速度很慢:必须先解析字符串才能执行。

我正在寻找一种缓存解析的方法,以便我可以将解析的字符串重用于另一个 eval。下一个 eval 将是相同的代码,但不会 eval 为相同的值,所以我不能简单地缓存结果。

从描述中我正在寻找来自Eval::Compileceval

但我不能使用 Eval::Compile,因为这需要平台的 C 编译器,而且用户没有 C 编译器。

那么我可以在纯 Perl 中做类似于 ceval 的事情吗?

背景

GNU Parallel 允许用户给出将在每个参数上进行评估的 Perl 表达式。目前,Perl 表达式由用户以字符串形式给出,并对每个参数进行评估。 Perl 表达式对于每个参数保持不变。因此重新编译表达式是一种浪费,因为重新编译不会改变任何东西。

代码分析表明eval 是瓶颈之一。

示例

用户输入:$_ .= "foo" and s/a/b/g

用户的脚本存储在$usereval1$usereval2

用户给出存储在@arguments中的10000个随机参数(字符串)。

sub replace {
  my ($script, $arg) = @_;
   local $_;
   $_ = $arg;
   # This is where I would like to cache the parsed $script.
   eval $script;
   return $_;
}

for my $arg (@arguments) {
   # Loads of indirect code (in the order of 1000 lines) that
   # call subs calling subs calling subs that eventually does:
   $replaced1 = replace($usereval1, $arg);
   $replaced2 = replace($usereval2, $arg);
   # Yet more code that does stuff with $replaced1 $replaced2
}

【问题讨论】:

  • 每次调用到底有什么不同?
  • 你可以像这样存储一个子程序引用:perl -lwe 'my $x = eval(q( sub { my $foo = shift; $foo*2; } )); print $x->(12);' 打印24。但是这就引出了一个问题,写一个子程序不是更简单吗?你确定你需要动态代码,还是这只是一个蛮力解决方案?如果您想要更好的答案,您需要分享有关您的解决方案的更多详细信息。
  • 用户提供要评估的表达式,所以是的,我需要动态代码。制作匿名潜艇并不是一个坏主意。还是更快吗?你量过吗?如果是这样,你能公布你的数字吗?
  • @TLP 我做了一个最小的测试。它看起来好像快了 40 倍。太好了!
  • @OleTange 允许用户向eval 提供代码听起来像是一个非常危险的想法。即使假设人们没有积极尝试恶意,你也知道有人最终会破坏某些东西。如果我是你,我会花更多的时间来找出一个安全的方法来做到这一点。

标签: perl eval


【解决方案1】:

你可以像这样存储一个子程序引用:

perl -lwe 'my $x = eval(q( sub { my $foo = shift; $foo*2; } )); print $x->(12);'

这会打印出24。您可以重复使用代码而无需重新编译。

【讨论】:

  • 在实际程序中测得的加速(包括所有周围的代码):1.5x。对于 5 行补丁来说还不错。
  • @OleTange 查看您的代码,如果您想要适当的加速,您需要在 for 循环外编译 $usereval,例如 my $usereval1 = eval("sub { $string }") 并跳过 @ 内的 eval 987654325@子程序。
  • 1.5x 的加速只编译一次 eval。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-01
  • 1970-01-01
  • 2011-06-15
  • 2011-03-29
  • 2013-01-27
  • 1970-01-01
相关资源
最近更新 更多