2009-06-03 25 views
24

非常にCPU集約的な文字列操作を行うコードがあり、パフォーマンスを向上させる方法を探していました。Cythonでの文字列操作

(EDIT:私はより良いHTML、そのようなものからのコメントを剥ぎ取り、Cでステートマシンとして表現されるかもしれない正規表現の多くを実行し、最長共通部分を見つけるようなものをやっている)

私は現在、コードの一部をCythonに移植することを検討しています。しかし、Cythonの主な焦点は数値計算であり、文字列での作業はほとんど文書化されていないようです。

Unicodeも大きな問題になる可能性があります。

私の質問は以下のとおりです。

  1. 私も、文字列のもののためCythonを気にする必要がありますか?サイホンでこのタイプの処理を経験した人はいますか?
  2. 私はCythonのドキュメントに何か不足していますか?誰でもチュートリアル/リファレンス/ Cythonの文字列での作業についてのドキュメントを知っていますか?
+1

+1:...私はそれを聞いたことがあり、興味深い:-) –

+0

どのような文字列操作ですか? – Miles

答えて

10

私は 'profile it'という回答に投票しましたが、これを追加したいと考えました。可能な限り最適な最適化は、Python標準ライブラリまたは組み込み関数を使用して、必要なタスクを実行することです。これらは通常C言語で実装されており、Cythonで書かれた拡張機能を含むあらゆる拡張機能とほぼ同等のパフォーマンスを提供します。あなたのアルゴリズムがPythonの文字ループで文字を実行している場合は、できるだけ最初のものにする必要があります。

しかし、組み込み関数や他の既存の標準ライブラリでは修正できないアルゴリズムがある場合、Cythonは合理的なアプローチのようです。擬似Pythonをネイティブコードにコンパイルするだけで、他の操作と同じように文字列操作に適しています。しかし、慣用的なPythonコードを手に入れるだけでCythonを使うと大きな利点があるとは思えません。 Cの各アルゴリズムの一部または全部を書き直して、低レベルの操作がPython/Cのバリア全体で変数を絶えず変換しないようにすると、最大の利点が得られます。

最後に、Unicode - 「大きな問題」かもしれないが、使用方法を指定していないことを暗示しています。 Cythonはおそらく、Unicodeを扱う関連するPython APIを呼び出すCコードを生成するので、機能は限定されそうにありません。しかし、C言語でのUnicode文字列の扱いは簡単ではなく、パフォーマンス向上のためにC言語でアルゴリズムの一部を書き直すことは、それほど価値がないということです。古典的な文字列アルゴリズムの多くは、文字単位で記憶単位が1つの従来の意味では「文字列」ではない多くのUnicodeエンコーディングでは機能しません。

+0

+1:標準ライブラリの使用上の良い点。 –

+0

あなたの最後の段落について:これは私がそれが大きな問題だと言っていたことを意味するものです。 – itsadok

+0

まあ、あなたはそれが大きな問題になるかもしれないと言いました。 ;)しかし、いくつかのアルゴリズムはうまく動作することに注意するのが潜在的に重要です。ほとんどの読み取り専用パターン、または直接パターンマッチングとスワップがあります。また、データが許す限り、最適化されたASCIIスタイルのアルゴリズムに落ちるいくつかのユーティリティがあります。多くは細部に依存します。 – Kylotan

4

これは非常に興味深い問題です。 Cythonは、PythonをCのデータ型と統合するためのツールです。特定のNumpy機能のために必要とされるほどの要求がないため、文字列を扱うための機能を提供していません。

あなたが説明した種類の問題を処理するために設計された既存のC/C++ライブラリとのインターフェイスにCythonをよく使うことができます。 HTML/XMLを処理するには、たとえばlibxmlを調べるとよいでしょう。しかし、それだけで利用できる(もちろん)ready-made python bindingsがあります。私はHTMLを処理するためにlxmlを広範囲に使用しました。必要なのはすべて実行しています。fast、それにunicodeをうまく処理します。

あなたのケースでは、lxmlとカスタムC関数の組み合わせが最適だろうと思います。たとえば、Cでの最長部分文字列を見つけるための高速関数を簡単に作ることができます。これは、バイトレベルで行うことができます(Cの文字列はchar *だけで、バイトの配列です)。その後、それらをPythonにマップすることができます(Cythonは本当に簡単になります)。確かに自明ではありませんが、アプリのパフォーマンスがそれに依存している場合は、その努力をする価値があります。

次に、C/C++でユニコードを使って作業するのはいいと思います。 Evan JonesのThis articleは、努力する価値があるかどうかを判断するのに役立ちます。 C.

+0

lxmlはCythonで書かれています。文字列処理に煩わされることはないようです。 –

7

ただ、完全を期すために、私がやってしまったことがちょうど(の一部)を書き込むされた文字列操作のコード結局のところ、それは、PythonのC拡張を書き始めるためridiculously easyです。 Unicode文字列はPy_UNICODEの配列です。これはPythonビルドに応じてintまたはshortです。

IはCに

s = re.sub(r' +', ' ', s) 

ようなコード変換X20改善を得ました。より複雑な正規表現でも同様の改善が得られましたが、Cコードはとても素早くクレイジーになりました。

全体的に、書き換え後のスループットは20%アップしました。私は今、書き直すためにもっと多くのものを探しています...

7

"ばかげて簡単"は非常に相対的な用語です。 「はじめに」はそれだけです。 Cで堅牢な拡張を書くには、参照カウント、メモリ割り当て/解放、エラー処理などに注意する必要があります。 Cythonはあなたのためにその多くを行います。

Cythonの非ユニコード文字列は、Pythonのstrオブジェクトか、Cのようにcharの配列です。必要と思われるCython固有のドキュメントは何ですか?

自分で試してみることをおすすめします。しかしそれを行う前に、Pythonコードの非効率性を調べることを強くお勧めします。ときには、あなたは非常に簡単に大きなスピードアップを得ることができます。例えば

re.sub(' +', ' ', s) # one space in pattern 

を使用して...スペース文字の実行を圧縮すると、実行が1の長さを有し、おそらく珍しくない場合には、それはスペースでスペースを置き換えることを意味します。すべてのランの長さが1の場合、入力文字列の参照カウントを簡単にインクリメント(または減少させずに)して戻すことができるときに、新しい置換文字列を作成します。

re.sub(' +', ' ', s) # two spaces in pattern 

がまったく同じ結果を生成し、より速く実行することができる...見てみましょう:

すべては長さ1を実行します。これは、3.4倍の速度で動作します。図示されていません。入力文字列が長ければ長いほど、より良い結果が得られます。

\python26\python -mtimeit -s"s='now is the winter of our discontent'; import re; x = re.compile(' +').sub" "x(' ', s)" 
100000 loops, best of 3: 8.26 usec per loop 

\python26\python -mtimeit -s"s='now is the winter of our discontent'; import re; x = re.compile(' +').sub" "x(' ', s)" 
100000 loops, best of 3: 2.41 usec per loop 

1ランの長さが2の場合、速度比は2.5です。すべての走行距離が2の場合、速度比は1.2です。すべてのことが考慮され、1回のキーストロークの投資に対する悪いリターンではありません。

+0

アドバイスをいただきありがとうございます!それは正規表現についての本当に良い点でした。私がCythonでヒットした他の障害についてのアドバイスを得ました - http://stackoverflow.com/questions/943658? – itsadok

3

Cythonは実際にはCPythonのPy_UNICODE型をサポートしているので、たとえば、Unicode文字列を直接反復したり、Cの速度で文字を比較することができます。私は最近、Cythonに導入されてきたし、重要なプロジェクトで使用するための大規模なCとC++のライブラリを包む大きな成功を収めている

http://docs.cython.org/src/tutorial/strings.html

4

参照してください。生成されたPython拡張のいくつかは、実際には本番環境ですでに実行されています。だから、まず、Cythonは間違いなく良い選択です。

Cythonですべてのコードを本当に書きたいのか、C/C++コードを記述してCythonからこれらの関数にアクセスできるようにするかを検討する必要があります。明らかに、これは部分的にCおよび/またはC++でのあなたの快適度に依存します。

文字列を扱う場合、おそらくchar*ではなく、C++のstd::stringを使用すると簡単に人生を変えることができます。それはfrom libcpp.string cimport stringで非常に簡単にcythonにインポートすることができます標準的なcythonを介して変数は文字列型で宣言することができますcdef string ...