C#のMIME & mboxパーサーでは、MimeKitと呼ばれています。
私が書いた以前のMIME &のmboxパーサー(例えば、GMime)は非常に高速でした(1.2GBのmboxファイル内のすべてのメッセージを約1秒で解析できる)。
私はMimeKitのパフォーマンスをまだテストしていませんが、私がCで使ったのと同じテクニックをC#で使っています。私のC実装よりも遅くなるだろうと思っていますが、ボトルネックはI/OとMimeKitはGMimeのように最適な(4k)読み込みを行うように書かれていますが、かなり近いはずです。あなたは(StreamReader.ReadLine()、SharpMimeToolsにそれを渡した後、テキストを組み合わせて)遅くなるためにあなたの現在のアプローチを見つけている
の理由は、以下の理由により、次のとおりです。
StreamReader.ReadLine ()は、ファイルからデータを読み込むのに最適な方法ではありません。
A)ファイルから読み込んだバイトのブロックをユニコードに変換します(これは、バイト[]の読み込みでバイトを反復処理する必要があります)ストリームから読み取られたバイトをユニコードchar []に変換するためにディスクから取得します。
B)それから、内部のchar []を反復処理して、それぞれの文字を '\ n'が見つかるまでStringBuilderにコピーする必要があります。
ここで、読んだ行だけで、あなたのmbox入力ストリームに少なくとも2回のパスがあります。すべてのメモリ割り当てについては言及しません...
次に、読み込んだすべての行を1つのメガストリングに結合します。これは、あなたの入力を別のパスに渡す必要があります(ReadLine()からStringBuilderに読み込まれた各文字列からすべての文字をコピーしますか?)。
入力テキストに対して最大3回反復されており、解析はまだ行われていません。
SharpMimeMessageStreamを使用するSharpMimeToolsに、メガストリングを渡します。これは...(/ facepalm)は、文字セット変換を行う別のStreamReaderの上にあるReadLine()ベースのパーサーです。つまり、何かが解析される前に5回反復されます。 SharpMimeMessageStreamには、ReadLine()が読み込みに失敗したことを検出した場合に、そのReadLine()を元に戻す方法もあります。そのため、彼は以上の行のうち少なくとも一部をでスキャンしていると仮定することは合理的です。すべての文字列割り当てが行われているわけではありません。
SharpMimeToolsにラインバッファが設定されると、それはフィールド&の値に分割されます。それは別のパスです。私たちはこれまでに6回までのパスです。
SharpMimeToolsは、 '、'とパラメータ化されたヘッダー(Content-TypeやContentなど)で分割してアドレスヘッダーをトークン化するために、string.Split()(このMIMEパーサーが標準に準拠していないことを示しています) - 配置)を ';'で分割します。それは別のパスです。
これを分割すると、string.Split()から返された各文字列に対して正規表現の一致が実行され、さらに正規表現がrfc2047の符号化ワードトークンを通過してから別の正規表現を作成しますエンコードされた単語のcharsetとペイロードのコンポーネントを渡します。この時点までに、入力の大部分で少なくとも9回または10回のパスを話しています。
GMimeとMimeKitなど、多くのパスが必要と私は私のパーサは、彼らが行うよりも少なくとも1少ないパスを作成するように最適化することができ知っとして、それはすでに2倍以上ですので、私は私の検査でいずれかの遠くに行くあきらめます。
また、サイドノートとして、byte [](またはsbyte [])の代わりに文字列を解析するMIMEパーサーは、決してうまくいかないでしょう。電子メールの問題は、ワイルドの非常に多くのメールクライアント/スクリプト/ etcがヘッダーとメッセージ本文に宣言されていない8bitテキストを送信することです。どのようにユニコード文字列パーサーを処理することができますか?ヒント:それはできません。
2013-09-18更新: Mboxファイルの解析に使用できるようになり、問題の解決に成功しましたが、私のCライブラリほど速くはありません。
[[email protected] MimeKit]$ mono ./mbox-parser.exe larger.mbox
Parsed 14896 messages in 6.16 seconds.
[[email protected] MimeKit]$ ./gmime-mbox-parser larger.mbox
Parsed 14896 messages in 3.78 seconds.
[[email protected] MimeKit]$ ls -l larger.mbox
-rw-r--r-- 1 fejj staff 1032555628 Sep 18 12:43 larger.mbox
として:これは(GMimeが〜1秒で同様のサイズのmbox形式のファイルを解析することが可能であるところである)ので、I/O性能が、それは私の古いLinuxマシン上になるほど良好ではないのiMac上でテストされましたGMimeはまだかなり高速ですが、MimeKitのパーサーのパフォーマンスを向上させる方法についてのアイデアがあります。 C#のfixed
ステートメントは非常に高価であることが判明したので、私はそれらの使い方を改訂する必要があります。たとえば、a simple optimization私は昨日、(私が正しく覚えていれば)全体の時間から約2-3秒を削った。
最適化のアップデート:だけで置き換えることによって、他の20%の性能改善:
while (*inptr != (byte) '\n')
inptr++;
で:
do {
mask = *dword++^0x0A0A0A0A;
mask = ((mask - 0x01010101) & (~mask & 0x80808080));
} while (mask == 0);
inptr = (byte*) (dword - 1);
while (*inptr != (byte) '\n')
inptr++;
最適化のアップデート:私は最終的に同じ速さなどMimeKitを作ることができました私のEnum.HasFlag()の使用から切り離し、ダイレクトビットマスキングを代わりに使用して、GMimeを使用してください。
MimeKitは、同じMboxストリームを3.78秒で解析できるようになりました。
比較のため、SharpMimeToolsは20を超えます分(これをテストするには、SharpMimeToolsがmboxファイルを解析できないため、電子メールを別々のファイルに分割する必要があります)。
別のアップデート:コード全体でさまざまな調整をして、3.00sのフラットにしました。
うわー、あなたたちは速いです!返信いただきありがとうございます。今、私は、 'From'のライン・バイ・ルックアップを行っています。見つけたら、そこからFROMの次の発生に行き、SharpMimeTools SharpMimeMessageハンドラに渡します。 StreamReaderと.ReadLineをmboxファイルとして使用して死んで遅くなるのは20Mb以上です(そして、かなり多くのものが1.2Gbのmboxファイルになります)。 確かに、 'が発生し、セグメントを取得する(正規表現かもしれない?) –