2008-12-04 66 views
9

我一直在基準框架,我在Perl編寫的表現和我得到的比我們現有的代碼庫每秒請求數減少了50%(有的命中是可以理解的,因爲我們是從程序麪條代碼會到一個OOP MVC框架)。如何在編譯時使用mod_perl執行低效的代碼?

該應用程序在mod_perl下運行,並且我已將Moose和我的所有框架代碼添加到startup.pl script中,該函數本身每秒會將我的請求數量加倍。我希望進一步提高這個數字,以儘可能接近現有金額。有人認爲這是過早的優化,但是我想解決一些明顯的低效問題,並且看看它是如何影響性能的。

最喜歡的框架,我有一個配置文件和調度。配置部分由Config::General處理,因此需要一點IO和解析來將我的配置文件加載到應用程序中。我在這裏看到的最大問題是,我正在爲每個請求都這樣做!

我的應用程序運行點傑韋利:: Dprof到配置::一般BEGIN和未駝鹿的主要慢點之一一堆相關的IO模塊。所以我想做什麼以及事後更加明智的做法是利用mod_perl的持久性和startup.pl編譯的特性,只做一次加載配置文件的工作 - 當服務器啓動時。

的問題是,我不是太熟悉,這將如何工作。

目前每個項目都有的PerlHandler引導類,這是很瘦,看起來像這樣:

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

MyApp.pm從框架項目模塊,其中有這樣的代碼繼承:

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

$self->config({$config->getall}); 

爲了只在編譯時做到這一點,我的引導程序和項目基本模塊將不得不改變(我認爲),但我很不確定要做出什麼改變,並且仍然保持代碼的漂亮和精益。任何人都可以在這裏指出我正確的方向嗎?

UPDATE

我試着在他的答案通過YSTH描述了每個項目模塊的方法BEGIN塊。所以,我現在有:

package MyApp::bootstrap; 
use MyApp; 

my $config; 
BEGIN 
{ 
    $config = {Config::General->new(...)->getall};   
} 

sub handler { ..etc. 
    MyApp->new(config => $config)->run(); 

這個快速變化的單獨給我以每秒請求50%增加,證實了我的想法,該配置文件是一個重大的瓶頸值得固定。我們的crotchety old dev機器上的基準數字是60rps,而我的框架已經從30rps變成了45rps,而這個變化本身就是如此。對於那些認爲Moose速度很慢並且編譯時間受到打擊的人來說。我在編譯我的配置文件時,在啓動時編譯了所有Moose代碼後,得到了同樣的(50%)增加。

我現在唯一的問題是,由於同樣的配置::常規 - >新的代碼是在每一個BEGIN塊只與路徑的配置文件不同這違反了DRY原則。我有幾個不同的策略來限制這一點,但我只是想公佈這個變化的結果。

回答

10

假設你的應用程序不更改配置的話,那移動到開始塊:

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

# when creating a new instance 
$self->config($config); 

,並確保所有的模塊都在startup.pl編譯。

你可能會更加奇特,並且有一個singleton類提供配置散列, 但你不需要。

+1

該解決方案的問題在於,您必須爲每個項目模塊(每個項目都有自己的配置文件)使用該代碼創建此BEGIN塊。 我確實很快把這個放在了一邊,而且我的請求每秒增加了50%,所以我無論如何都滿口答覆,因爲它確實回答了我的問題 – 2008-12-05 10:53:10

+1

要解決「每個項目都需要它自己的配置」,你可以 1)將所有文件合併成不同的部分(可以使用INI文件或Config :: ApacheFormat)。 2)有一個配置類,它將每個配置文件保存在一個散列中,並根據一些$ ENV變量提取正確的配置文件。 – mpeters 2008-12-05 14:11:10

+1

我從來沒有真正考慮過擁有一個巨大的配置文件,因爲當您擁有數百個項目時,維護會有多困難......但實際上有很多好處,因爲我們在所有項目之間共享數據庫連接,除此之外在少數情況下。謝謝。 – 2008-12-05 15:04:39

1

我有一個HTML ::梅森框架安裝同樣的問題,發現這個工作相當好: 在httpd.conf:

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

在你handler.pl文件中,定義所有的你的靜態項目,比​​如你的配置,數據庫句柄等等。這些定義在YourModule :: Mason的範圍內,它是在apache線程啓動時編譯的(新線程顯然會有內在的開銷)。 YourModule :: Mason然後有一個處理請求的方法handler

我承認在HTML :: Mason中可能會有一些魔法幫助我,但它適用於我,也許對您有用?

+0

請參閱下面的答案。它不僅僅是讓你的石匠對象實例化,因爲那時你已經加載了一大塊泥工,不需要重新加載,並且可以在工人之間透明地共享。 – 2008-12-05 13:27:24

4

如果你可以讓你的駝鹿類immutable,這可能會給你另一個減速帶。

+1

當然。這是您使用Moose時學會做的第一件事情之一。 – 2008-12-05 13:51:20

-2

JackM有正確的想法。

通過加載所有類,並在「母親」 Apache進程實例化您的應用程序級的對象(在你的情況下,配置),您不必每次新員工產卵編譯它們,因爲它們已經可用並且在內存中。我們之間非常細緻的爲他們的應用程序定期使用的每個模塊添加一條「使用」線。如果您沒有在母船中加載您的軟件包和模塊,那麼每個工作人員不僅要承擔加載模塊的性能問題,還不能從現代操作系統提供的內存共享中受益。

它實際上是mod_perl和CGI之間差異的另一半。對於每個調用,前半部分是mod_perl的持久perl-engine與CGI的重新生成的perl。

0

,有少量變化加快這樣的事情的一種常見方式是簡單地使用全局變量和緩存狀態他們同樣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

3

模塊的import sub在編譯時執行的,所以我們可以用它來降低/消除ysth's answer晾乾。

在下面的例子中,我們使用一個導入方法來讀取配置文件,並給出給我們的參數,然後將該配置推送到調用包中。

在調用包中的任何$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';