web-dev-qa-db-ja.com

Perlプログラムを高速化するにはどうすればよいですか?

これは実際には2つの質問ですが、それらは非常に似ているので、簡単にするために、それらをまとめるだけだと思いました。

  • Firstly:確立されたPerlプロジェクトを考えると、単なるコード内最適化を超えてそれをスピードアップするためのいくつかの適切な方法は何ですか?

  • Secondly:Perlでプログラムを最初から作成する場合、パフォーマンスを大幅に向上させるための良い方法は何ですか?

最初の質問では、きちんと書かれたプロジェクトが渡され、パフォーマンスを改善する必要があると想像してください。しかし、リファクタリング/最適化によって多くの利益を得ることができないようです。この場合、Cのようなものに書き直す以外に、速度を上げるにはどうしますか?

Perl固有でない限り、一般的な最適化手法には近づかないでください。

私はこれについて Python 以前に尋ねましたが、他の言語でもそれを行うのが良いかもしれないと思いました( サイコ との結果があるかどうか特に興味があります) パイレックス Perlの場合)。

29
akdom

最適化クラブのルールを覚えておいてください:

  1. 最適化クラブの最初のルールは、最適化しないことです。
  2. 最適化クラブの2番目のルールは、測定せずに最適化しないことです。
  3. アプリが基盤となるトランスポートプロトコルよりも高速に実行されている場合、最適化は終了しています。
  4. 一度に1つの要因。
  5. マーケットロイドもマーケットロイドのスケジュールもありません。
  6. テストは必要な限り続けられます。
  7. これがOptimizationClubでの最初の夜である場合は、テストケースを作成する必要があります。

したがって、実際に機能するコードがあると仮定して、プログラムを Devel :: NYTProf で実行します。

ボトルネックを見つけます。次に、ここに戻って、それらが何であるかを教えてください。

動作するコードがない場合は、最初に動作させてください。これまでに行う最大の最適化は、機能しない状態から機能する状態への移行です。

155
Andy Lester

アンディはすでに言及しています Devel :: NYTProf 。それは素晴らしいです。本当に、本当に素晴らしい。これを使って。

何らかの理由でDevel::NYTProfを使用できない場合は、古き良き Devel :: DProf にフォールバックできます。これは、長い間Perlに標準装備されています。計算に時間がかかるtrue関数(数学的な意味で)がある場合(たとえば、フィボナッチ数)、次のようになります メモ化 速度が向上します。

パフォーマンスの低下の多くは、不適切なデータ構造とアルゴリズムが原因です。コンピュータサイエンスの優れたコースは、ここで非常に役立ちます。 2つの方法があり、それらのパフォーマンスを比較したい場合は、 Benchmark モジュールも役立ちます。

次の Perlのヒント もここで役立つかもしれません:

免責事項:私は上記のリソースのいくつかを書いたので、それらに偏っている可能性があります。

34
pjf

改善できる点はたくさんあるので、最初に何が遅いのかを理解する必要があります。他の人はすでにその質問に答えています。これについては Mastering Perl でも少し話します。

新しいコードを書いているときに考えるべきことの不完全なリスト:

  • Devel :: NYTProf のようなプロファイルを使用して、コード内でほとんどの時間を費やしている場所を確認します。時にはそれは驚くべきことであり、簡単に修正できます。 Perlの習得 それについて多くのアドバイスがあります。

  • Perlは毎回ソースをコンパイルする必要があり、コンパイルが遅くなる可能性があります。すべてのファイルなどを見つける必要があります。たとえば、Jean-Louis Leroyによる "A Timely Start" を参照してください。彼は、@INCのモジュールの場所を最適化するだけですべてを高速化します。起動コストが高くて避けられない場合は、pperl、mod_Perlなどの永続的なperlも検討してください。

  • 使用するモジュールのいくつかを見てください。単純なことをするためだけに、依存関係の長いチェーンがありますか?もちろん、再発明は好きではありませんが、車に取り付けたいホイールに3つのボート、5つのヤギ、チーズバーガーが付属している場合は、独自のホイールを作成する(または別のホイールを見つける)ことをお勧めします。 。

  • メソッド呼び出しは高額になる可能性があります。たとえば、Perl :: Criticalテストスイートでは、isaを呼び出すと処理速度が低下します。すべての場合に本当に回避できるものではありませんが、覚えておくべきことです。誰かが「2の因数をあきらめることを誰も気にしない。それが悪いのは10人の人がそれをしているときだ」というような素晴らしい引用をしました。 :) Perl v5.22には、このためのパフォーマンスの改善がいくつかあります。

  • 同じ高価なメソッドを何度も呼び出しても同じ答えが得られる場合は、 メモ化 のようなものが適している可能性があります。これは、メソッド呼び出しのプロキシです。それが本当に関数である場合(つまり、同じ入力が副作用なしで同じ出力を提供する)、実際に繰り返し呼び出す必要はありません。

  • Apache :: DBI などのモジュールは、データベースハンドルを再利用して、データベース接続のコストのかかるオープンを回避できます。これは本当に単純なコードなので、Apacheを使用していない場合でも、内部を見るとその方法がわかります。

  • Perlは末尾再帰の最適化を行わないので、これらの超高速再帰アルゴリズムを作成しようとしているとLISPから来ないでください。それらを簡単に反復解に変えることができます(そしてそれについては 中級Perl で話します。

  • 正規表現を見てください。多くのオープンエンドの数量詞(例:.*)は、多くのバックトラックにつながる可能性があります。 Jeffrey Freidlの 正規表現の習得 すべての厄介な詳細(およびいくつかの言語にわたる)を確認してください。 彼の正規表現のウェブサイト もチェックしてください。

  • Perlがどのようにコンパイルされるかを知ってください。本当にスレッドとDDEBUGGINGが必要ですか?それらはあなたを少し遅くします。 perlbenchユーティリティをチェックして、さまざまなPerlバイナリを比較してください。

  • さまざまなPerlに対してアプリケーションをベンチマークします。一部の新しいバージョンでは高速化されていますが、一部の古いバージョンでは、限られた操作セットでより高速になる場合があります。あなたが何をしているのかわからないので、特にアドバイスはありません。

  • 仕事を広げます。他のプロセスやリモートコンピューターで非同期作業を行うことはできますか?他の誰かがいくつかのサブ問題を理解するので、あなたのプログラムに他のことに取り組んでもらいましょう。 Perlには、いくつかの非同期モジュールと負荷シフトモジュールがあります。ただし、そのようなことをうまく行うための足場は、それを行うことの利点を失う可能性があることに注意してください。

31
brian d foy

大きなチャンクを書き直す必要なしに、 Inline :: C を使用して、単一の遅いサブルーチンをCに変換できます。またはXSを直接使用します。 XSを使用してサブを段階的に変換することも可能です。 PPI/PPI :: XS たとえば、これを行います。

しかし、別の言語への移行は常に最後の手段です。たぶん、あなたは専門のPerlプログラマーにあなたのコードを見てもらうべきですか?多くの場合、彼はあなたのパフォーマンスを深刻に傷つけているいくつかの特異性を発見するでしょう。それ以外は、コードのプロファイルを作成します。特効薬はないことを忘れないでください。

Psycoとpyrexに関して:いいえ、Perlに相当するものはありません。

14
tsee

アプリケーションのプロファイリング-たとえば、上記のプロファイラーを使用します。その後、時間がどこに向かっているのかがわかります

CPU使用率以外の作業に時間を費やしている場合は、最初にそれらを減らす必要があります。CPUは簡単に拡張できますが、他のことはそうではありません。

いくつかの操作は特に遅いです、私は見つけました:

  • 大きなハッシュのkeys()は非常に悪いです
  • デバッグにData::Dumperを使用します。大きな構造でこの関数を使用すると、非常に時間がかかります。できれば避けてください。次のようなコードを見てきました。

    use Data::Dumper; 
    $debugstr = Dumper(\%bighash); 
    if ($debugflag_mostlyoff) { log($debugstr); } 
    
  • ほとんどのモジュールには、パフォーマンス特性が異なる代替手段があります。文字通り、信じられないほどひどいものもあります。

  • 一部の正規表現は非常に遅くなる可能性があり(多くの。*など)、より高速な同等の式に置き換えることができます。正規表現は、単体テストとパフォーマンステストを非常に簡単に実行できます(大きなシミュレートされたデータセットに対してループで実行するプログラムを作成するだけです)。最良の正規表現は、リテラル文字列など、非常に迅速にテストできるものから始まります。最初に探しているものを最初に探すのではなく、「後ろを振り返って」、それが本当に探しているものであるかどうかを確認する方がよい場合があります。正規表現を最適化することは、私があまり得意ではないちょっとしたブラックアートです。

最後の手段を除いて、Cで何かを書き直すことを検討しないでください。 PerlからCを呼び出す(またはその逆)には、比較的大きなオーバーヘッドがあります。迅速なPerl実装を取得できるのであれば、それは良いことです。

Cで何かを書き直す場合は、呼び出しのオーバーヘッドを最小限に抑え、Perlランタイムを呼び出すようにしてください(たとえば、SV *関数は主に文字列をコピーします)。これを実現する1つの方法は、より多くのことを実行するC関数を作成し、それを呼び出す回数を減らすことです。メモリ内で文字列をコピーするのはクールではありません。

一方、Cで何かを書き直すと、新しい障害モードが導入される可能性があるため、大きなリスクが伴います。メモリリーク、クラッシュ、セキュリティの問題。

9
MarkR

このテーマについて読む価値のあるエッセイは、ニコラス・クラークの講演です Perlの速度が十分でない場合 (PDF)。 Devel :: DProfへの参照など、いくつかのポイントは少し古くなっていますが、2002年に書かれたことを覚えておいてください。

それにもかかわらず、カバーされている資料の多くは引き続き関連性があります。

9
dland

これはあなたの質問に関連しているのは半分だけですが、ドキュメントのためにここに投稿します。

最近の CentOS/Perlバグ修正 アプリケーションの速度が2倍以上に向上しました。これは、CentOS Perlを実行し、bless/overload関数を使用するすべての人にとって必須です。

9
leek

メソッドとサブルーチンの呼び出しは、Perlでは無料ではありません。それらは比較的高価です。したがって、プロファイリングで実行時間のかなりの部分を小さなアクセサメソッドに費やしていることが判明した場合、それは一見の価値のあるマイクロ最適化である可能性があります。

ただし、notにすべきでないことは、ここでget_color()などのアクセサーを置き換えることです。

package Car;
# sub new {...}

sub get_color {
   my $self = shift;
   return $self->{color};
}

package main;
#...
my $color = $car->get_color();

カプセル化を破る直接アクセス:

my $color = $car->{color};

これは言うまでもないことだと思うかもしれませんが、これは至る所で行われていることもわかります。 Class :: XSAccessor を使用して実行できることは次のとおりです。

package Car;
# sub new {...}
use Class::XSAccessor
  getters => {
    get_color => 'color',
  },
  setters => {
    set_color => 'color',
  };

これにより、XSに実装された新しいメソッドget-およびset_color()が作成されるため、手巻きバージョンの約2倍の速度になります。連鎖メソッドと同様に、ミューテーター(つまり、「$ car-> color( 'red')」)も使用できます。

アプリケーションによっては、これにより非常に小さな(ただし本質的に無料の)ブーストが得られる場合があります。何か特別なことをしているのでない限り、1〜2%を超えないようにしてください。

8
tsee

プログラムの実行速度を上げる最善の方法は、プログラムの作業を減らすことです。ジョブに適したアルゴリズムを選択してください。何百万回も呼び出されるコードの一部の領域でダムアルゴリズムを選択するため、低速のアプリケーションをたくさん見ました。 100万回の操作ではなく、100万回* 100万回の操作を実行している場合、プログラムの実行速度は100万倍遅くなります。文字通り。

たとえば、ソートされたリストに要素を挿入するコードを次に示します。

while(my $new_item = <>){
    Push @list, $new_item;
    @list = sort @list;
    ... use sorted list
}

ソートはO(n log n)です。ソートされたリストへの挿入はO(log n)です。

アルゴリズムを修正します。

6
jrockway

最も費用効果の高い方法は、より高速なハードウェア(=>適切なハードウェアアーキテクチャ)を検討することです。私はより高速なCPUについて話しているのではなく、より高速なディスク、より高速なネットワーク..実際にはI/Oを高速化するものを高速化しています。

私はこれを何年も前に経験しました。XML解析ベースのアプリケーション(当時のブリーディングエッジテクノロジー<g>)を(高速で信頼性の高い!)Windows Serverから、多少時代遅れではありますが、より高速な専用のSunプラットフォームに移行したときです。/Oすべての周り。

いつものように、考慮してください

  • 開発者のパフォーマンス(コーディングにかかる​​時間、問題の複雑さ、結果の維持可能性)、
  • ハードウェアパフォーマンス、
  • ソフトウェアのパフォーマンス

そして、目前の問題に対して最も(コスト!)効果的な場所を改善します...

2
lexu

コードを高速化する必要がある場合は、テストスイートも高速化する可能性があります。この講演では、重要なポイントに触れます。

ターボチャージャー付きテストスイート

1
EvdB