11

私は System.Threading.Parallel と Threading のパフォーマンスをテストしてきましたが、Parallel がスレッド化よりもタスクの完了に時間がかかっていることに驚いています。それは、私が読み始めたばかりの Parallel に関する私の限られた知識によるものだと確信しています。

いくつかのスニペットを共有すると思いました。誰かが私に指摘できれば、並列コードはスレッド化されたコードよりも遅く実行されています。また、素数を見つけるために同じ比較を実行しようとしたところ、スレッド化されたコードよりもはるかに遅く終了する並列コードが見つかりました。

public class ThreadFactory
{
    int workersCount;
    private List<Thread> threads = new List<Thread>();

    public ThreadFactory(int threadCount, int workCount, Action<int, int, string> action)
    {
        workersCount = threadCount;

        int totalWorkLoad = workCount;
        int workLoad = totalWorkLoad / workersCount;
        int extraLoad = totalWorkLoad % workersCount;

        for (int i = 0; i < workersCount; i++)
        {
            int min, max;
            if (i < (workersCount - 1))
            {
                min = (i * workLoad);
                max = ((i * workLoad) + workLoad - 1);
            }
            else
            {
                min = (i * workLoad);
                max = (i * workLoad) + (workLoad - 1 + extraLoad);
            }
            string name = "Working Thread#" + i; 

            Thread worker = new Thread(() => { action(min, max, name); });
            worker.Name = name;
            threads.Add(worker);
        }
    }

    public void StartWorking()
    {
        foreach (Thread thread in threads)
        {
            thread.Start();
        }

        foreach (Thread thread in threads)
        {
            thread.Join();
        }
    }
}

プログラムは次のとおりです。

Stopwatch watch = new Stopwatch();
watch.Start();
int path = 1;

List<int> numbers = new List<int>(Enumerable.Range(0, 10000));

if (path == 1)
{
    Parallel.ForEach(numbers, x =>
    {
        Console.WriteLine(x);
        Thread.Sleep(1);

    });
}
else
{
    ThreadFactory workers = new ThreadFactory(10, numbers.Count, (min, max, text) => {

        for (int i = min; i <= max; i++)
        {
            Console.WriteLine(numbers[i]);
            Thread.Sleep(1);
        }
    });

    workers.StartWorking();
}

watch.Stop();
Console.WriteLine(watch.Elapsed.TotalSeconds.ToString());

Console.ReadLine();

アップデート:

ロックを考慮に入れる: 次のスニペットを試しました。再び同じ結果が得られますが、Parallel はかなり遅く終了するようです。

パス = 1; cieling = 10000000;

    List<int> numbers = new List<int>();

    if (path == 1)
    {
        Parallel.For(0, cieling, x =>
        {
            lock (numbers)
            {
                numbers.Add(x);    
            }

        });
    }

    else
    {
        ThreadFactory workers = new ThreadFactory(10, cieling, (min, max, text) =>
        {

            for (int i = min; i <= max; i++)
            {
                lock (numbers)
                {
                    numbers.Add(i);    
                }                       

            }
        });

        workers.StartWorking();
    }

更新 2: 私のマシンにクアッド コア プロセッサが搭載されていることを簡単に更新します。したがって、Parallel には 4 つのコアが使用可能です。

4

4 に答える 4

4

Reed Copsey Jrのブログ投稿を参照すると、次のようになります。

ただし、Parallel.ForEach はもう少し複雑です。汎用の IEnumerable を使用する場合、処理に必要なアイテムの数は事前にわからないため、実行時に検出する必要があります。さらに、各要素に直接アクセスできないため、スケジューラはコレクションを列挙して処理する必要があります。 IEnumerable はスレッド セーフではないため、列挙時に要素をロックし、処理するチャンクごとに一時的なコレクションを作成し、これをスケジュールする必要があります。

ロックとコピーにより、Parallel.ForEach の時間が長くなる可能性があります。また、パーティショニングと ForEach のスケジューラが影響し、オーバーヘッドが発生する可能性があります。コードをテストし、各タスクのスリープを増やしたところ、結果は近くなりましたが、それでも ForEach は遅くなりました。

[編集 - より多くの研究]

実行ループに以下を追加しました。

if (Thread.CurrentThread.ManagedThreadId > maxThreadId)
   maxThreadId = Thread.CurrentThread.ManagedThreadId;

これが私のマシンで示しているのは、現在の設定の他のスレッドと比較して、ForEach で使用するスレッドが 10 少ないことです。ForEach からより多くのスレッドが必要な場合は、ParallelOptions とスケジューラをいじる必要があります。

Parallel.ForEach はアクティブなスレッドの数を制限しますか?を参照してください。

于 2010-08-26T07:00:22.587 に答える
3

ご質問にお答えできると思います。まず第一に、システムに搭載されているコアの数を書きませんでした。デュアルコアを実行している場合Parallel.For、例で 10 のスレッドを使用しているときに を使用して動作するスレッドは 4 つだけThreadです。実行中のタスク (印刷 + ショート スリープ) はスレッド化の非常に短いタスクであり、スレッドのオーバーヘッドはタスクに比べて非常に大きいため、より多くのスレッドがより適切に機能します。スレッドなしで同じコードを記述した場合、ほぼ確実にそれはより速く動作します。

どちらの方法もほとんど同じように機能しますが、事前にすべてのスレッドを作成すると、Parallel.For移動のオーバーヘッドが追加されるタスク プールを使用するため、大幅に節約できます。

于 2010-08-26T07:46:47.977 に答える
0

それは論理的です:-)

コードの1つ(または2つ)の層を追加することでパフォーマンスが向上したのは、歴史上初めてのことです。コンビニエンスライブラリーを使用する場合は、代金を支払うことを期待する必要があります。ところで、あなたは数字を投稿していません。結果を公開しました:-)

Parallel-sで物事をもう少し失敗させる(またはバイアスをかける:-)には、リストを配列に変換します。

次に、それらを完全に不公平にするために、自分で作業を分割し、わずか10個のアイテムの配列を作成し、Parallelに完全にスプーンフィードアクションを実行します。もちろん、あなたはParallel-sがこの時点であなたのために行うと約束した仕事をしていますが、それは興味深い数字になるはずです:-)

ところで、私はちょうどそのリードのブログを読みました。この質問で使用されるパーティショニングは、彼が最も単純で素朴なパーティショニングと呼んでいるものです。それは確かにそれを非常に良い除去テストにします。それが完全にホースで固定されているかどうかを知るためだけに、ゼロワークケースをチェックする必要があります。

于 2010-08-26T12:12:49.377 に答える
0

Threading.Parallel に関しては、比較はあまり公平ではありません。カスタム スレッド プールに 10 個のスレッドが必要であることを伝えます。Threading.Parallel は必要なスレッド数がわからないため、現在の CPU 負荷などを考慮して実行時に適応しようとします。テストの反復回数は十分に少ないため、この数のスレッドに適応ペナルティを課すことができます。Threading.Parallel に同じヒントを提供すると、はるかに高速に実行されます。


int workerThreads;
int completionPortThreads;
ThreadPool.GetMinThreads(out workerThreads, out completionPortThreads);
ThreadPool.SetMinThreads(10, completionPortThreads);

于 2010-08-26T09:14:50.670 に答える