外出先で文字列をさまざまなデータ型に変換するための最速の(一般的なアプローチ)を探しています。
何かによって生成された大きなテキストデータファイルを解析しています(ファイルのサイズは数メガバイトです)。この特定の関数は、テキストファイルの行を読み取り、各行を区切り文字に基づいて列に解析し、解析された値を.NETDataTableに配置します。これは後でデータベースに挿入されます。FARによる私のボトルネックは、文字列変換(ConvertおよびTypeConverter)です。
ファイルにどのタイプが含まれるかわからないため、動的な方法(つまり、「Convert.ToInt32」などから離れる)を使用する必要があります。タイプは、実行時の以前の構成によって決定されます。
これまで私は次のことを試しましたが、どちらもファイルの解析に数分かかります。この1行をコメントアウトすると、数百ミリ秒で実行されることに注意してください。
row[i] = Convert.ChangeType(columnString, dataType);
と
TypeConverter typeConverter = TypeDescriptor.GetConverter(type);
row[i] = typeConverter.ConvertFromString(null, cultureInfo, columnString);
誰かがこのような一般的なより速い方法を知っているなら、私はそれについて知りたいです。または、私のアプローチ全体が何らかの理由でうまくいかない場合は、提案を受け入れます。ただし、ハードコードされた型を使用する一般的でないアプローチを指摘しないでください。それはここでは単にオプションではありません。
更新-パフォーマンステストを改善するためのマルチスレッド
パフォーマンスを向上させるために、解析タスクを複数のスレッドに分割することを検討しました。速度は多少向上しましたが、思ったほどではありませんでした。しかし、これが興味のある人のための私の結果です。
システム:
Intelキセノン3.3GHzクアッドコアE3-1245
メモリ:12.0 GB
Windows 7 Enterprise x64
テスト:
テスト機能は次のとおりです。
(1)文字列の配列を受け取ります。(2)文字列を区切り文字で分割します。(3)文字列をデータ型に解析し、行に格納します。(4)データテーブルに行を追加します。(5)終了するまで(2)-(4)を繰り返します。
テストには1000個の文字列が含まれ、各文字列は16列に解析されるため、合計で16000個の文字列変換が行われます。シングルスレッド、4スレッド(クアッドコアのため)、および8スレッド(ハイパースレッディングのため)をテストしました。ここではデータを処理しているだけなので、これよりも多くのスレッドを追加しても効果があるとは思えません。したがって、シングルスレッドの場合は1000文字列を解析し、4スレッドはそれぞれ250文字列を解析し、8スレッドはそれぞれ125文字列を解析します。また、スレッドの使用方法をいくつかテストしました。スレッドの作成、スレッドプール、タスク、関数オブジェクトです。
結果: 結果時間はミリ秒単位です。
シングルスレッド:
- メソッド呼び出し:17720
4スレッド
- パラメータ化されたスレッドの開始:13836
- ThreadPool.QueueUserWorkItem:14075
- Task.Factory.StartNew:16798
- Func BeginInvoke EndInvoke:16733
8スレッド
- パラメータ化されたスレッドの開始:12591
- ThreadPool.QueueUserWorkItem:13832
- Task.Factory.StartNew:15877
- Func BeginInvoke EndInvoke:16395
ご覧のとおり、最速はパラメーター化されたスレッドを使用することです。8スレッド(私の論理コアの数)から始めます。ただし、4スレッドを使用した場合に勝るものはなく、シングルコアを使用した場合よりも約29%高速です。もちろん、結果はマシンによって異なります。また、私は
Dictionary<Type, TypeConverter>
型コンバーターの配列を使用するための文字列解析用のキャッシュでは、パフォーマンスが大幅に向上することはなく、必要なときに配列を作成するよりも、1つの共有キャッシュ型コンバーターを使用する方が保守性が高くなります。
別の更新:
さて、私はさらにいくつかのテストを実行して、パフォーマンスをさらに絞り込めるかどうかを確認しました。興味深いことがいくつか見つかりました。私は8つのスレッドを使用することにしました。これらはすべて、Parameterized Thread Startメソッドから開始されました(これは以前のテストの中で最速でした)。上記と同じテストが実行されましたが、解析アルゴリズムが異なります。きがついた
Convert.ChangeType and TypeConverter
ほぼ同じ時間がかかります。次のようなタイプ固有のコンバーター
int.TryParse
私のタイプは動的であるため、少し高速ですが、オプションではありません。ricovoxは、例外処理についていくつかの良いアドバイスをしました。私のデータには確かに無効なデータがあり、一部の整数列は空の数値にダッシュ'-'を付けるので、型コンバーターはその時点で爆発します。つまり、解析するすべての行に少なくとも1つの例外、つまり1000の例外があります。非常に時間がかかります。
ところで、これは私がTypeConverterで変換を行う方法です。拡張機能は単なる静的クラスであり、GetTypeConverterはcahcedTypeConverterを返すだけです。変換中に例外がスローされた場合、デフォルト値が使用されます。
public static Object ConvertTo(this String arg, CultureInfo cultureInfo, Type type, Object defaultValue)
{
Object value;
TypeConverter typeConverter = Extensions.GetTypeConverter(type);
try
{
// Try converting the string.
value = typeConverter.ConvertFromString(null, cultureInfo, arg);
}
catch
{
// If the conversion fails then use the default value.
value = defaultValue;
}
return value;
}
結果:
8スレッドで同じテスト-1000行、各16列、スレッドあたり250行を解析します。
だから私は3つの新しいことをしました。
1-テストを実行します。例外を最小限に抑えるために、解析する前に既知の無効なタイプを確認します。つまり、if(!Char.IsDigit(c))value = 0; またはcolumnString.Contains('-')など..
ランタイム:29ms
2-テストを実行します。trycatchブロックを持つカスタム解析アルゴリズムを使用します。
ランタイム:12424ms
3-テストを実行します。例外を最小限に抑えるために、解析する前に無効な型をチェックするカスタム解析アルゴリズムを使用します。
ランタイム15ms
わお!ご覧のとおり、例外を排除することで世界に違いが生まれました。例外が本当にどれほど高価なのか、私は気づきませんでした。したがって、例外を本当に未知のケースに最小化すると、解析アルゴリズムは3桁速く実行されます。私はこれが完全に解決されたと考えています。TypeConverterを使用して動的型変換を維持すると思いますが、数ミリ秒遅くなります。変換する前に既知の無効な型をチェックすると、例外が回避され、処理が大幅に高速化されます。これをさらにテストするように指摘してくれたricovoxに感謝します。