Почему я не могу достичь 100% загрузки ЦП с помощью кода параллельных задач? - PullRequest
1 голос
/ 30 мая 2020
private static async Task<int> SumParallel()
        {
            var intList = Enumerable.Range(1, 1000_000_000);
            int count = intList.Count();
            int total = 0;
            for(int i = 1; i < 5; i++)
            {
                int skip = ((i - 1) * count) / 4;
                int take = count / 4;
                Interlocked.Add(ref total,
                    await GetSum(intList, skip, take));
            }
            return total;
        }

private static async Task<int> GetSum(IEnumerable<int> list, int skip, int take)
        {
            return await Task.Run(() =>
             {
                 int temp = 0;
                 foreach(int n in list.Skip(skip).Take(take))
                 {
                     if (n % 2 == 0)
                         temp -= n;
                     else
                         temp += n;
                 }
                 return temp;
             });
        }

Я пытаюсь выполнить задачу с интенсивными вычислениями, просто чтобы попрактиковаться в параллельной библиотеке задач. Итак, я написал этот код. Если я попытаюсь вычислить ту же сумму с помощью Parallel Linq, я увижу, что загрузка ЦП достигнет 100% следующим образом:

int sum = Enumerable.Range(1, 1000_000_000)
                .AsParallel()
                .Select(i => i % 2 == 0 ? -i : i).Sum();

Код ручных параллельных задач = время 10 секунд, cpu = только 25%

Параллельный код Linq = 18 секунд, cpu = 100% (и все еще занимает 18 секунд)

Linq-код без параллелизма: 14 секунд, только cpu = 25%

Почему это так? Когда я также запускаю 4 потока параллельно, почему загрузка моего процессора не достигает go 100%? Это всего 25%, как в непараллельном коде (всего с одним потоком). Я действительно запускаю 4 потока параллельно или нет?

Мой ноутбук Core i3 - 2 ядра = 4 логических процессора

1 Ответ

2 голосов
/ 30 мая 2020

SumParallel не выполняет параллельную работу, потому что он создает и ожидает каждую задачу последовательно:

for(int i = 1; i < 5; i++) {
    //...
    await GetSum(intList, skip, take)

Чтобы сделать ее параллельной, вы должны запустить все пять задач, а затем await все из них для завершения с помощью await Task.WhenAll(tasks);.

Что касается Parallel Linq, который работает медленнее, чем стандартный Linq, это связано с тем, что ваша рабочая нагрузка слишком гранулярна. В результате накладные расходы на параллелизм сводят на нет любые преимущества параллельного выполнения. Другими словами, распределить элементы по потокам и собрать результаты обратно - это больше работы, чем фактический расчет (n % 2). Чтобы максимально использовать параллелизм, ваша рабочая нагрузка должна быть небольшой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...