Как последовательный цикл работает быстрее, чем параллельный цикл в C#?

я попробовал очень минимальный пример:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Collections.Concurrent;
using System.Diagnostics;

namespace TPLExample {
    class Program {
        static void Main(string[] args) {
            int[] dataItems = new int[100];
            double[] resultItems = new double[100];

            for (int i = 0; i < dataItems.Length; ++i) {
                dataItems[i] = i;
            }

            Stopwatch stopwatch = new Stopwatch();

            stopwatch.Reset();
            stopwatch.Start();
            Parallel.For(0, dataItems.Length, (index) => {
                resultItems[index] = Math.Pow(dataItems[index], 2);
            });
            stopwatch.Stop();
            Console.WriteLine("TPL Time elapsed: {0}", stopwatch.Elapsed);

            stopwatch.Reset();
            stopwatch.Start();
            for (int i = 0; i < dataItems.Length; ++i) {
                resultItems[i] = Math.Pow(dataItems[i], 2);
            }
            stopwatch.Stop();
            Console.WriteLine("Sequential Time elapsed: {0}", stopwatch.Elapsed);

            WaitForEnterKey();
        }

        public static void WaitForEnterKey() {
            Console.WriteLine("Press enter to finish");
            Console.ReadLine();
        }

        public static void PrintMessage() {
            Console.WriteLine("Message printed");
        }
    }
}

вывод:

TPL Time elapsed: 00:00:00.0010670
Sequential Time elapsed: 00:00:00.0000178
Press enter to finish

последовательный цикл намного быстрее, чем TPL! Как это возможно? Из моего понимания, расчет в пределах Parallel.For будет выполняться параллельно, поэтому должно быть быстрее?

4 ответов


проще говоря: только для итерации более ста элементов и выполнения небольшой математической операции, создание новых потоков и ожидание их завершения создает больше накладных расходов, чем просто выполнение цикла.

из моего понимания, расчет внутри параллели.Ибо будет выполняться параллельно, так Должно ли это быть быстрее?

как обычно бывает, когда люди делают широкие заявления о производительности компьютера, есть гораздо больше переменные в игре здесь, и вы не можете действительно сделать такое предположение. Например, внутри вашего for цикл, вы не делаете ничего больше, чем Math.Pow, который процессор может выполнять очень быстро. Если бы это была интенсивная операция ввода-вывода, требующая, чтобы каждый поток ждал долгое время, или даже если бы это была серия интенсивных операций с процессором, вы бы получили больше от параллельной обработки (если у вас есть многопоточный процессор). Но как бы то ни было, накладные расходы на создание и синхронизацию этих потоки намного больше, чем любое преимущество, которое может дать вам параллелизм.


параллельная обработка цикла полезна, когда операция, выполняемая в цикле, относительно дорогостоящая. Все, что вы делаете в своем примере, это вычисление показателя, который тривиален. Накладные расходы многопоточности намного перевешивают выгоды, которые вы получаете в этом случае.


этот пример кода является практическим доказательством действительно хороших ответов выше.

я моделировал интенсивную работу процессора, просто блокируя поток с помощью Thead.Спать.

вывод:

  • Последовательный Цикл - 00:00:09.9995500
  • Параллельная Петля-00:00: 03.0347901

_

class Program
{
    static void Main(string[] args)
    {
        const int a = 10;

        Stopwatch sw = new Stopwatch();
        sw.Start();

        //for (long i = 0; i < a; i++)
        //{
        //    Thread.Sleep(1000);
        //}

        Parallel.For(0, a, i =>
        {
            Thread.Sleep(1000);
        });

        sw.Stop();

        Console.WriteLine(sw.Elapsed);

        Console.ReadLine();
    }
}

накладные расходы на распараллеливание намного больше, чем просто выполнение математики.Pow 100 раз последовательно. Об этом говорили другие.

Что еще более важно, хотя доступ к памяти тривиален в последовательной версии, но с параллельной версией потоки должны делиться памятью (resultItems), и это будет действительно убить вас, даже если у вас есть миллион предметов.

см. стр. 44 этой отлично Microsoft whitepaper on параллельное программирование: http://www.microsoft.com/en-us/download/details.aspx?id=19222. Вот статья журнала MSDN на эту тему:http://msdn.microsoft.com/en-us/magazine/cc872851.aspx