比eval更快的替代方案?

时间:2011-12-28 05:27:41

标签: performance perl eval

我正在处理一个使用本土模板系统的网络应用程序,该系统允许将Perl代码嵌入到HTML中。这些语句由模板解析器在运行时使用eval EXPR执行。

这非常灵活,但这些陈述分散在各处,并执行 lot eval EXPR(而不是eval BLOCK)要求Perl每次启动解释器,我的分析显示它们是减速的合理重要来源。

许多嵌入式Perl语句非常简单。例如,模板可能有这样的行:

<p>Welcome, <!--E: $user->query('name') -->.

或者:

<p>Ticket number <!--E: $user->generate_ticket_number() --> has been generated.

也就是说,他们只是调用对象方法。但是,也有更复杂的问题。

我希望能够优化这一点,到目前为止有两个想法,这两个想法都很糟糕。第一种是重写所有模板,用USER:NAMEUSER:GENERATETICKETNUMBER之类的标记替换简单调用,然后解析器可以扫描并调用适当的对象方法。但是,我没有处理混合HTML和Perl的模板,而是使用混合HTML,Perl和令牌的模板。

第二个想法是尝试解析嵌入的Perl,弄清楚语句想要做什么,如果它足够简单,通过符号引用调用适当的对象方法。这显然是疯了。

我有一些合理的解决方案吗?

4 个答案:

答案 0 :(得分:9)

尝试采用类似于mod_perl用于编译CGI的方法:

  1. 将模板转换为Perl代码。例如,您的第一个示例可能会转换为:

    print "<p>Welcome, ";
    print $user->query('name');
    print ".\n";
    
  2. 围绕该代码包裹sub { ... },以及一些解包参数的代码(例如,样本中的$user之类的内容)。

  3. eval该代码。请注意,它返回一个coderef。

  4. 重复调用该coderef。 :)

答案 1 :(得分:3)

您可以查看Mojolicious。它有templating engine,允许语法接近你正在使用的语法。您可以切换到使用它或查看其来源(单击上一个链接左侧的源)以查看是否可以绘制一些想法。

仅供参考Mojolcious模板引擎的语法允许以下格式与HTML混合使用

<% Perl code %>
<%= Perl expression, replaced with result %>
<%== Perl expression, replaced with XML escaped result %>
<%# Comment, useful for debugging %>
<%% Replaced with "<%", useful for generating templates %>
% Perl code line, treated as "<% line =%>"
%= Perl expression line, treated as "<%= line %>"
%== Perl expression line, treated as "<%== line %>"
%# Comment line, treated as "<%# line =%>"
%% Replaced with "%", useful for generating templates

答案 2 :(得分:2)

你可能想看看Text::MicroTemplate的胆量。实际上,您可能希望使用 Text :: MicroTemplate,因为它可能符合您的需求。它构建了一个子程序,可以根据需要连接字符串,就像duskwuff建议的那样。以下是build_mt('hello, <?= $_[0] ?>')re.pl的结果:

$CODE1 = sub {
       package Devel::REPL::Plugin::Packages::DefaultScratchpad;
       use warnings;
       use strict 'refs';
       local $SIG{'__WARN__'} = sub {
         print STDERR $_mt->_error(shift(), 4, $_from);
       }
       ;
       Text::MicroTemplate::encoded_string(sub {
         my $_mt = '';
         local $_MTREF = \$_mt;
         my $_from = '';
         $_mt .= 'hello, ';
         $_from = $_[0];
         $_mt .= ref $_from eq 'Text::MicroTemplate::EncodedString' ? $$_from : do {
           $_from =~ s/([&><"'])/$Text::MicroTemplate::_escape_table{$1};/eg;
           $_from
         };
         return $_mt;
       }
       ->(@_));
     };

答案 3 :(得分:0)

您不应该使用'eval'来调用模板中的方法。抱歉听起来很苛刻,但分离视图的目的是从视图层中删除处理代码。上面描述的模板系统和Template Toolkit只是传入一个对象/哈希,因此您可以访问它。

为什么不将$ user作为hashref传递:

$user = {
        'name' => 'John',
        'id' => '3454'
      };

这将允许您通过以下方式访问“名称”:

$user->{'name'};

否则,您可能正在执行以下操作:

  1. 模板调用$ user-&gt; query();
  2. 方法调用DB获取值
  3. 方法返回值
  4. 制作数据库查询比将哈希/对象引用传递给模板要昂贵得多。您可能需要查看一些代码分析工具,如Devel :: NYTProf,以查看代码执行的哪些部分真正减慢了您的速度。我怀疑eval是否会让你的程序陷入困境,以至于你需要优化eval。听起来像eval中的代码正在减慢你的速度。