以下は、それぞれ約300 KBから1 MB(最大)x 5回繰り返される約21個のログファイルから簡単なテキスト処理を行うPerlおよびPythonスクリプトです(合計125ファイル、 log 5回繰り返されます)。
Pythonコード(コンパイルされたre
を使用し、re.I
を使用するように変更されたコード)
#!/usr/bin/python
import re
import fileinput
exists_re = re.compile(r'^(.*?) INFO.*Such a record already exists', re.I)
location_re = re.compile(r'^AwbLocation (.*?) insert into', re.I)
for line in fileinput.input():
fn = fileinput.filename()
currline = line.rstrip()
mprev = exists_re.search(currline)
if(mprev):
xlogtime = mprev.group(1)
mcurr = location_re.search(currline)
if(mcurr):
print fn, xlogtime, mcurr.group(1)
Perlコード
#!/usr/bin/Perl
while (<>) {
chomp;
if (m/^(.*?) INFO.*Such a record already exists/i) {
$xlogtime = $1;
}
if (m/^AwbLocation (.*?) insert into/i) {
print "$ARGV $xlogtime $1\n";
}
}
そして、私のPCでは、両方のコードが10,790行のまったく同じ結果ファイルを生成します。そして、CygwinのPerlおよびPython実装で行われたタイミングは次のとおりです。
User@UserHP /cygdrive/d/tmp/Clipboard
# time /tmp/scripts/python/afs/process_file.py *log* *log* *log* *log* *log* >
summarypy.log
real 0m8.185s
user 0m8.018s
sys 0m0.092s
User@UserHP /cygdrive/d/tmp/Clipboard
# time /tmp/scripts/python/afs/process_file.pl *log* *log* *log* *log* *log* >
summarypl.log
real 0m1.481s
user 0m1.294s
sys 0m0.124s
もともと、Pythonを使用すると10.2秒かかり、この単純なテキスト処理にPerlを使用すると1.9秒しかかかりませんでした。
(UPDATE)しかし、コンパイルされたre
バージョンのPythonの後、Pythonでは8.2秒、Perlでは1.5秒かかります。それでもPerlはずっと高速です。
Pythonの速度を改善する方法はありますかOR Perlが単純なテキスト処理の高速なものになることは明らかです。
ちなみに、これは私が単純なテキスト処理のために行った唯一のテストではありませんでした...そして、ソースコードを作成するそれぞれ異なる方法で、Perlは常に大きなマージンで勝ちます。また、Pythonは、単純なm/regex/
の一致と印刷の場合のパフォーマンスが向上しませんでした。
C、C++、アセンブリ、その他のPythonのフレーバーなどの使用を提案しないでください。
私は、標準Pythonを使用し、標準Perlと比較したビルトインモジュールを使用したソリューションを探しています(モジュールを使用していません)。少年、読みやすさのためにすべてのタスクにPythonを使用したいと思いますが、速度をあきらめるために、そうは思いません。
したがって、Perlと同等の結果を得るためにコードを改善する方法を提案してください。
更新:2012-10-18
他のユーザーが示唆したように、Perlにはその場所があり、Pythonにはある。
したがって、この質問については、数百または数千のテキストファイルの各行で単純な正規表現が一致し、結果をファイルに書き込む(または画面に印刷する)、Perlは常に、このジョブのパフォーマンスで常に勝ちます。それと同じくらい簡単です。
Perlがパフォーマンスで勝ったと言うとき...標準PerlとPythonのみが比較されます...いくつかのあいまいなモジュール(私のような普通のユーザーにとってはあいまい)に頼らず、Cを呼び出さないことに注意してください、 C++、PythonまたはPerlのアセンブリライブラリ。単純なテキストマッチングジョブのこれらの追加手順とインストールをすべて学ぶ時間はありません。
そのため、Perlはテキスト処理と正規表現を好んでいます。
Pythonには他の場所で揺れる場所があります。
Update 2013-05-29:同様の比較を行う優れた記事 こちら 。 Perlは単純なテキストマッチングで再び勝ちます...詳細については、記事を参照してください。
これはまさに、Perlが行うように設計されたものなので、高速であることは驚くことではありません。
Pythonコードの簡単な最適化は、これらの正規表現をプリコンパイルすることです。そのため、毎回再コンパイルされません。
exists_re = re.compile(r'^(.*?) INFO.*Such a record already exists')
location_re = re.compile(r'^AwbLocation (.*?) insert into')
そして、あなたのループで:
mprev = exists_re.search(currline)
そして
mcurr = location_re.search(currline)
それだけでは、PythonスクリプトをPerlスクリプトと一致させることはできませんが、最初にコンパイルせずにループでreを繰り返し呼び出すことは、Pythonの悪い習慣です。
仮説:Perlは、Pythonがしない最適化のために、一致しない行でバックトラックする時間を短縮します。
交換すると何が得られますか
^(.*?) INFO.*Such a record already exists
と
^((?:(?! INFO).)*?) INFO.*Such a record already
または
^(?>(.*?) INFO).*Such a record already exists
Pythonでは、関数呼び出しは時間の面で少し高価です。さらに、ループ内でファイル名を取得するためのループ不変関数呼び出しがあります。
fn = fileinput.filename()
この行をfor
ループの上に移動すると、Pythonタイミング。Perlを打ち負かすのに十分ではないかもしれません。
Perlの方が速くなると思います。興味があるだけで、次のことを試すことができますか?
_#!/usr/bin/python
import re
import glob
import sys
import os
exists_re = re.compile(r'^(.*?) INFO.*Such a record already exists', re.I)
location_re = re.compile(r'^AwbLocation (.*?) insert into', re.I)
for mask in sys.argv[1:]:
for fname in glob.glob(mask):
if os.path.isfile(fname):
f = open(fname)
for line in f:
mex = exists_re.search(line)
if mex:
xlogtime = mex.group(1)
mloc = location_re.search(line)
if mloc:
print fname, xlogtime, mloc.group(1)
f.close()
_
更新に対する反応として「複雑すぎます」。
もちろん、Perlバージョンよりも複雑に見えます。 Perlは正規表現を中心に構築されました。この方法では、正規表現でより高速なインタープリター言語を見つけることはほとんどできません。 Perl構文...
_while (<>) {
...
}
_
...また、より一般的な言語で何とかしなければならない多くのことを隠します。一方、読み取り不可能な部分を外に移動すると、Pythonコードをより読みやすくすることが非常に簡単です。
_#!/usr/bin/python
import re
import glob
import sys
import os
def input_files():
'''The generator loops through the files defined by masks from cmd.'''
for mask in sys.argv[1:]:
for fname in glob.glob(mask):
if os.path.isfile(fname):
yield fname
exists_re = re.compile(r'^(.*?) INFO.*Such a record already exists', re.I)
location_re = re.compile(r'^AwbLocation (.*?) insert into', re.I)
for fname in input_files():
with open(fname) as f: # Now the f.close() is done automatically
for line in f:
mex = exists_re.search(line)
if mex:
xlogtime = mex.group(1)
mloc = location_re.search(line)
if mloc:
print fname, xlogtime, mloc.group(1)
_
ここでdef input_files()
は他の場所(別のモジュールなど)に配置するか、再利用できます。構文的には同じ方法ではありませんが、Perlのwhile (<>) {...}
でさえ簡単に模倣することができます。
_#!/usr/bin/python
import re
import glob
import sys
import os
def input_lines():
'''The generator loops through the lines of the files defined by masks from cmd.'''
for mask in sys.argv[1:]:
for fname in glob.glob(mask):
if os.path.isfile(fname):
with open(fname) as f: # now the f.close() is done automatically
for line in f:
yield fname, line
exists_re = re.compile(r'^(.*?) INFO.*Such a record already exists', re.I)
location_re = re.compile(r'^AwbLocation (.*?) insert into', re.I)
for fname, line in input_lines():
mex = exists_re.search(line)
if mex:
xlogtime = mex.group(1)
mloc = location_re.search(line)
if mloc:
print fname, xlogtime, mloc.group(1)
_
その場合、最後のfor
は、Perlのwhile (<>) {...}
と同じように(原則的に)簡単に見えるかもしれません。このような可読性の向上はPerlではより困難です。
とにかく、それはPythonプログラムを高速にしません。Perlはここで再び高速になります。Perlisはファイル/しかし、私の意見では、Pythonはより汎用的なプログラミング言語です。
一般的に、すべての人工ベンチマークは悪です。ただし、他のすべてが等しい場合(アルゴリズム的アプローチ)、相対的な改善を行うことができます。ただし、Perlを使用していないことに注意してください。したがって、Perlを支持することはできません。そうは言っても、Pythonを使用すると、パフォーマンスを改善するために Pyrex または Cython を使用できます。または、冒険好きなら、 ShedSkin (これはほとんどのコア言語で動作し、一部ではなくすべてのコアモジュールで動作します)を介してPythonコードをC++に変換してみることができます。 )。
それでも、ここに投稿されたヒントのいくつかに従うことができます。