为什么并行任务代码无法达到 100% 的 CPU 利用率?


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;
});
}

我正在尝试执行计算密集型任务,只是为了练习任务并行库。所以,我写了这段代码。如果我尝试使用并行 Linq 计算相同的总和,我可以看到 CPU 利用率达到 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 个线程时,为什么我的 CPU 利用率没有达到 100%?它只有 25%,就像在非并行代码中一样(只有一个线程(。我真的并行运行 4 个线程吗?

我的笔记本电脑是酷睿i3 - 2核= 4个逻辑处理器

SumParallel不执行并行作业,因为它按顺序创建并等待每个任务:

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

要使其并行,您必须启动所有五个任务,然后await所有任务以完成await Task.WhenAll(tasks);

至于并行 Linq 比标准 Linq 慢,那是因为您的工作负载太精细了。因此,并行性的开销抵消了并行执行的任何好处。换句话说,将项目分发到线程并收集结果比实际计算(n % 2(要多得多。为了充分利用并行性,您的工作负载必须是块状的。

最新更新