私は、ファイルを読み取り、ファイル内の各単語の出現をカウントする非常に単純な「単語カウント」プログラムをコーディングしました。コードの一部は次のとおりです。
class Alaki
{
private static List<string> input = new List<string>();
private static void exec(int threadcount)
{
ParallelOptions options = new ParallelOptions();
options.MaxDegreeOfParallelism = threadcount;
Parallel.ForEach(Partitioner.Create(0, input.Count),options, (range) =>
{
var dic = new Dictionary<string, List<int>>();
for (int i = range.Item1; i < range.Item2; i++)
{
//make some delay!
//for (int x = 0; x < 400000; x++) ;
var tokens = input[i].Split();
foreach (var token in tokens)
{
if (!dic.ContainsKey(token))
dic[token] = new List<int>();
dic[token].Add(1);
}
}
});
}
public static void Main(String[] args)
{
StreamReader reader=new StreamReader((@"c:\txt-set\agg.txt"));
while(true)
{
var line=reader.ReadLine();
if(line==null)
break;
input.Add(line);
}
DateTime t0 = DateTime.Now;
exec(Environment.ProcessorCount);
Console.WriteLine("Parallel: " + (DateTime.Now - t0));
t0 = DateTime.Now;
exec(1);
Console.WriteLine("Serial: " + (DateTime.Now - t0));
}
}
シンプルでわかりやすいです。私は辞書を使って各単語の出現を数えます。スタイルは大まかにMapReduceプログラミングモデルに基づいています。ご覧のとおり、各タスクは独自のプライベート辞書を使用しています。したがって、共有変数はありません。単語を自分で数える一連のタスク。コードをクアッドコアi7CPUで実行した場合の出力は次のとおりです。
パラレル:00:00:01.6220927
シリアル:00:00:02.0471171
スピードアップは約1.25で、これは悲劇を意味します。しかし、各行を処理するときに遅延を追加すると、約4のスピードアップ値に達することができます。
遅延のない元の並列実行では、CPUの使用率が30%に達することはほとんどないため、高速化は期待できません。ただし、遅延を追加すると、CPUの使用率は97%に達します。
まず、原因はプログラムのIOバウンドの性質であると思いました(ただし、ディクショナリへの挿入はある程度CPUに負荷がかかると思います)。すべてのスレッドが共有メモリバスからデータを読み取っているため、論理的に思えます。ただし、驚くべき点は、シリアルプログラムの4つのインスタンスを(遅延なしで)同時に実行すると、CPUの使用率が約上昇し、4つのインスタンスすべてが約2.3秒で終了することです。
これは、コードがマルチプロセッシング構成で実行されている場合、約3.5のスピードアップ値に達しますが、マルチスレッド構成で実行されている場合、スピードアップは約1.25であることを意味します。
あなたの考えは?私のコードに何か問題がありますか?共有データはまったくないと思いますし、コードに競合は発生しないと思います。.NETの実行時に欠陥はありますか?
前もって感謝します。