2009-10-19 12 views
5

ファイルからデータベースにデータをロードする成熟したコードがあります。 いくつかのファイル形式があります。それらはすべて固定幅のフィールドです。Perlの固定幅データの処理を高速化するにはどうすればよいですか?

一部のコードでは、Perl unpack()関数を使用して、入力データのフィールドをパッケージ変数に読み込みます。 ビジネスロジックは、これらのフィールドを「人間が読める」方法で参照することができます。

ファイルの読み取りコードは、ファイルを読み取る前に、フォーマット記述から1回生成されます。スケッチ形式で

、生成されたコードは次のようになります。コードをプロファイリング

while (<>) { 

    # Start of generated code. 

    # Here we unpack 2 fields, real code does around 200. 
    ($FIELDS::transaction_date, $FIELDS::customer_id) = unpack q{A8 A20}; 

    # Some fields have leading space removed 
    # Generated code has one line like this per affected field. 
    $FIELDS::customer_id =~ s/^\s+//; 

    # End of generated code. 

    # Then we apply business logic to the data ... 
    if ($FIELDS::transaction_date eq $today) { 
     push @fields, q{something or other}; 
    } 

    # Write to standard format for bulk load to the database. 
    print $fh join('|', @fields) . q{\n} or die; 
} 

時間の約35%がアンパックとリーディングスペースストリップに費やされていることが明らかになりました。 残りの時間は、データの検証と変換、および出力ファイルへの書き込みに費やされます。

ランタイムの1〜2%を占めるビジネスロジックの部分がないようです。

質問は - 私たちは何とかアンパックとスペースストリッピングから少しスピードを出せますか?好ましくは、FIELDSパッケージ変数を参照するすべてのコードをリファクタリングする必要はありません。

EDIT:場合

それは違い

$ perl -v 
This is perl, v5.8.0 built for PA-RISC1.1 
+0

アンパックの左側のパッケージ変数のリストの使用が最適である可能性があるかどうかを知りたいと思うでしょうか。 –

答えて

1

はいになります。 substrを使用して抽出するのが最も速い方法です。つまり:

$FIELDS::transaction_date = substr $_, 0, 8; 
$FIELDS::customer_id  = substr $_, 8, 20; 

がより高速になる可能性があります。さて、もし私がこのコードを手書きしていたら、私はunpackをあきらめませんでしたが、あなたがコードを生成しているならば、それにショットと測定値を与えるかもしれません。

は先頭のスペースを剥離するとしてもIs Perl’s unpack() ever faster than substr()?

への回答を参照してください、s/^\s+//は最速の方法である可能性が高いです。

更新:ベンチマークを実行することができなくても、明確なことは言うまでもありません。しかし、方法について:任意のトリミングとトリミング必要です

my ($y) = substr($_, 8, 20) =~ /\A\s+(.+?)\s+\z/; 

を必要としないフィールドの

my $x = substr $_, 0, 8; 

+0

私は実験して報告します。 –

+3

注意すべき点の1つは、アンパック 'A'が空白の末尾の空白を取り除くことです。 –

+0

これまでのところ有望です。基本的なベンチマークでは、一連のフィールド単位のsubstrとstrip後続の正規表現が、1つの展開を使用するよりも約50%高速です。テストは合格ですが、私の画面はPerlishの警告でいっぱいです。 –

7

私は実際に何度もこの問題に取り組んできました。 Unpack is better than substr

ストリップスペースがある限り、あなたはかなり嫌になります。その正規表現のハックはそれを行う "公式の"方法です。あなたは解凍文を改良することで効率を上げることができるかもしれません(データが4桁より長くないと仮定して、フィールドの全12桁を解凍するのはなぜですか?)そうでなければ、解析は単なるp.i.t.aです。

フラットデータで幸運を祈る。私がそれを嫌う方法、古くからのジャンクを欺く。

3

このタスクにプロセッサがバインドされていますか?計算は、プロセス全体がI/O境界になる可能性があると考えられるほど単純です。このような場合には、より速くアンパックするように最適化すると時間がかかりません。

実際にプロセッサにバインドされている場合は、説明されている問題はかなり並列化可能ですが、悪魔はビジネス計算の詳細にあります。

+0

確かに、はい。実際のコードでは、上記のpottedの例とは異なり、I/Oとアンパックは分離されています。並列性は良い呼び出しです - 私たちはすでにそれを行います。しかし、まだ文字列を細かくするのに費やす時間が少なくて済みます。 –

+2

私の場合、最大のボトルネックはデータベースそのものです。フラットなデータを扱っているのであれば、恐ろしい古代のフラットなデータベースを扱っているかもしれません。私は現代のデータベースに間隔をあけてデータを引き出すことで問題を回避しますが、「リアルタイム」データの場合、ほとんどの場合、減速の原因となるデータベースです。 – Satanicpuppy

+0

同意する必要があります。全体として、私たちのプロセスは、大部分の時間をデータをデータベースに浪費しています。これをスピードアップするために、並列プロセスを使用することができます。 「Perl-time」のそのような大部分がコードのこのような小さな部分で費やされたので、ここでは簡単に最適化するための範囲があるかもしれないと私は思いました。 –

1

これはXSのためのものでもあります。そのため、C関数を使用してデータを変更します。データが実際にコピーされたときに手動で制御できるので、これは他のどの機能よりもはるかに高速です。
Cコンパイラに依存しているため、ビルドプロセスが難しくなり、追加の統合手順が必要になります。

+0

ありがとうございます。おそらく私のプロジェクトでは、その利益は余分な複雑さを正当化するものではないでしょう。 Satanicpuppyが述べたように、これよりも高価なプロセスの他の部分があります。しかし、同様の問題を抱えている誰かのために働くことができます。 –

+3

CPU時間の大部分が正規表現エンジンと組み込み関数で費やされている場合、大部分の時間はCの土地(つまり、optreeのウォークスルーやperlデータ構造の処理に費やされない)で費やされます。また、Perl VMを書いた人たちを打ち負かすには、良いプログラマが必要です。解凍は速い! – tsee

1

単にパラレルにしてください。それは些細なことですが、遠隔の現代的なマシンでさえ、より速くなります。

0

コードのsubstrベースバージョンのベンチマークは、既存のアンパックより約50%高速かもしれないことを示唆しています。 実際のアプリケーションのコードを比較すると、substrのバージョンでは実行時間が16%短縮されました。 これは、ベンチマークとその質問で参照されているプロファイリングに基づいて私たちが望んでいたものに近いです。

この最適化は役に立ちます。しかし、私たちは新しいOSへの移行を控えているので、作業を進める前にコードがそこでどのように実行されるのかを待っています。比較ベンチマークに注目するためのテストを追加しました。

我々が今持っているイディオムは次のとおりです。

$FIELDS::transaction_date = substr($_, 0, 8) || ''; 
$FIELDS::transaction_date =~ s/\s+\z//; 
$FIELDS::customer_id = substr($_, 8, 20) || ''; 
$FIELDS::customer_id =~ s/\s+\z//; 

は以前のような選択をリードするスペースを除去しました。

これまでの回答すべてに感謝します。 私はSinan'sを受け入れるでしょう。なぜならそれは私たちのために働いたからです。

関連する問題