代码之家  ›  专栏  ›  技术社区  ›  David McLaughlin

使用mod_perl时,如何仅在编译时执行低效的代码?

  •  9
  • David McLaughlin  · 技术社区  · 16 年前

    我一直在对我用Perl编写的框架的性能进行基准测试,与我们现有的代码库相比,每秒的请求减少了50%(一些打击是可以理解的,因为我们正在从过程式意大利面条代码转向面向对象的MVC框架)。

    应用程序在mod_perl下运行,我添加了 Moose 并将我的所有框架代码放入 startup.pl script ,这本身使我每秒的请求量翻了一番。我希望进一步提高这个数字,使其尽可能接近现有金额。有人认为这是过早的优化,但我想解决几个明显的低效问题,看看它对性能的影响。

    与大多数框架一样,我有一个配置文件和一个调度器。配置部分由处理 Config::General ,因此需要一些IO和解析才能将我的配置文件加载到应用程序中。我在这里看到的最大问题是,我对收到的每个请求都这样做!

    在我的应用程序上运行Devel::Dprof指出Config::General::BEGIN和一堆相关的IO模块是不是Moose的主要慢点之一。所以我想做的,事后看来更有意义的是利用mod_perl的持久性和startup.pl编译的东西,只在服务器启动时在配置文件中加载一次。

    问题是,我不太熟悉这将如何工作。

    目前,每个项目都有一个PerlHandler引导类,它非常精简,看起来像这样:

    use MyApp; 
    MyApp->new(config_file => '/path/to/site.config')->run();
    

    MyApp.pm继承自框架Project模块,其代码如下:

    my $config = Config::General->new(
                    -ConfigFile => $self->config_file,
                    -InterPolateVars => 1,
                 );    
    
    $self->config({$config->getall});
    

    为了只在编译时做到这一点,我的引导和项目基础模块都必须更改(我认为),但我不确定要做什么更改,同时保持代码的美观和简洁。有人能给我指出正确的方向吗?

    更新

    我在每个项目模块中尝试了ysth在他的回答中描述的BEGIN BLOCK方法。所以我现在有:

    package MyApp::bootstrap;
    use MyApp;
    
    my $config;
    BEGIN
    {
        $config = {Config::General->new(...)->getall};        
    }
    
    sub handler { ..etc.
        MyApp->new(config => $config)->run();
    

    仅仅这一快速变化就给了我一个 50% 每秒请求数的增加,证实了我的想法,即配置文件是一个值得修复的主要瓶颈。我们破旧的开发机器上的基准数字是60rps,仅凭这一变化,我的框架就从30rps变成了45rps。对于那些说Moose速度慢并且编译时受到影响的人来说。。在启动时编译所有Moose代码时,我获得了与预编译配置文件时相同的(50%)增长。

    我现在唯一的问题是,这违反了DRY原则,因为相同的Config::General->每个BEGIN块中都有新代码,只有配置文件的路径不同。我有一些不同的策略来限制这一点,但我只是想发布这一更改的结果。

    6 回复  |  直到 16 年前
        1
  •  10
  •   ysth    16 年前

    假设您的应用程序根本不更改配置,请将其移动到start块中:

    # this code goes at file scope
    my $config;
    BEGIN {
        $config = { Config::General->new( ... )->getall }
    }
    
    # when creating a new instance
    $self->config( $config );
    

    并确保所有模块都在startup.pl中编译。

    你可以变得更花哨,让一个单例类提供配置哈希, 但你不需要。

        2
  •  4
  •   yhw42 menu_on_top    14 年前

    如果你能上你的驼鹿课 immutable ,这可能会给你另一个减速带。

        3
  •  3
  •   Community CDub    8 年前

    模块 import sub 在编译时执行,因此我们可以使用它来减少/消除DRY ysth's answer .

    在下面的示例中,我们使用import方法读取具有给定参数的配置文件,然后将该配置推送到调用包中。

    警告是任何 $config 调用包中的变量将被清除。

    package Foo_Config;
    use English qw(-no_match_vars);
    sub import {
       my ($self, @cfg) = @ARG;
       my $call_pkg     = caller;
       my $config       = {Config::General->new(@cfg)->getall};
       do{ # this will create the $config variable in the calling package.
           no strict 'refs';
           ${$call_pkg . '::config'} = $config;
       };
       return;
    }
    
    package MyApp;
    # will execute Foo_Config->import('/path/to/site.config') at compile time.
    use Foo_Config '/path/to/site.config'; 
    
        4
  •  1
  •   Jack M.    16 年前

    我在安装HTML::Mason框架时也遇到了同样的问题,发现这个方法相当有效: 在httpd.conf中:

    PerlRequire handler.pl
    <FilesMatch "\.mhtml$">
      SetHandler perl-script
      PerlHandler YourModule::Mason
    </FilesMatch>
    

    在handler.pl文件中,您定义了所有静态项,如配置、数据库句柄等。这在YourModule::Mason的范围内定义了它们,该范围在apache线程启动时编译(新线程显然会有固有的开销)。你的模块:梅森有一个 handler 处理请求的方法。

    我承认,HTML::Mason中可能有一些神奇的东西在帮助我,但它对我有效,也许对你有效?

        5
  •  0
  •   mjy    16 年前

    在几乎不做任何更改的情况下加速此类事情的一种常见方法是在调用同一Apache进程之间简单地使用全局变量和缓存状态:

    use vars qw ($config);
    # ...
    $config = Config::General->new( ... )->getall
        unless blessed($config); # add more suitable test here
    

    它不是很干净,可能会导致模糊的错误(尽管根据我的经验,“my$var”会导致更多错误),有时会占用大量内存,但通过这种方式可以避免许多(重复的)昂贵的初始化语句。比使用BEGIN{}的优势;唯一的代码是,您也可以根据其他事件重新初始化,而无需重新启动apache或终止进程(例如,通过在上述测试中包含磁盘上文件的时间戳)。

    不过要注意陷阱: an easy way to break in

        6
  •  -2
  •   Len Jaffe    16 年前

    JackM的想法是正确的。

    通过加载所有类并在“ 母亲 “Apache进程,你不必每次生成新的worker时都编译它们,因为它们已经可用并且在内存中。我们中间非常细致的人为他们的应用程序经常使用的每个模块添加一个“use”行。如果你不在母舰上加载包和模块,每个worker不仅会受到加载模块的性能影响,而且不会获得现代操作系统提供的内存共享的好处。

    这实际上是mod_perl和CGI之间差异的另一半。前半部分是mod_perl的持久perl引擎与CGI为每次调用重新生成perl。