PLINQ并行查询效率的简单分析
假设执行一次查询需要花费的时间比较大,那我们可以采用PLINQ并行查询来进行提速
并行查询,就是利用多核计算机并行执行查询语句来进行提速,这很好理解,一个工人工作慢,那就多叫几个工人同时进行工作,这样工作的进展不就快了。火神山医院不也是靠这样方式建出来的吗,不然工人不多的话,10天怎么可能建造出一个火神山医院
显然,工作进展的快慢和工人数是有关的,在计算机中,工人数对应着线程数
运行以下代码,
这段代码是假设一个查询任务耗时约1s,分析:随着查询任务数量增多,使用普通LINQ执行完所有任务总耗时的变化情况,
简单的说,就是看
如果总任务数为1,需要执行多久,
总任务为2又需要执行多久……
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
public static int ExpensiveFunction(int num)
{
Thread.Sleep(1000);
return 1;
}
static void Main(string[] args)
{
for(int TaskNum = 1; TaskNum <= 20; TaskNum++)
{
//记录开始时间
DateTime dateTime1 = DateTime.Now;
//创建List
int maxLength = TaskNum;
List<int> nums = new List<int>();
for (int i = 0; i < maxLength; i++)
{
nums.Add(i);
}
//普通LINQ
var result = from num in nums
select ExpensiveFunction(num);
//并行PLINQ
//var result = from num in nums.AsParallel()
// select ExpensiveFunction(num);
//使用查询结果,否则LINQ实际不执行
int sum = 0;
foreach (int i in result)
{
sum += i;
}
Console.WriteLine($"任务数量:{TaskNum}");
//记录结束时间
DateTime dateTime2 = DateTime.Now;
//输出所用时长
Console.WriteLine("耗时:{0}", dateTime2 - dateTime1);
}
}
}
}
运行结果:
可以发现,耗时几乎是随着任务数量的增加而线性增长的,这也好理解,同样是一个工人,任务越多,工作时间应该也是越多的,而且是一个线性的变化关系
然后再将以上代码的普通LINQ语句改为PLINQ语句,再次执行程序
运行结果:
可以发现,当任务数量小于等于16时,执行完所有任务的用时和任务数量为1个时的用时是差不多的
而当任务数量为17时,用时发生突变,变为之前的约两倍
这是因为我的电脑的8核16线程的,一次最多处理16个任务,当任务数增大到17时,17个任务不能同时并行执行,而是先并行执行16个任务,等有一个线程把当前任务做完了,它才会去做剩下的一个任务,因此花费时间相对于16个任务是两倍的关系
需要注意的是,PLINQ并不是在所有场景下都比LINQ的执行速度要快,因为PLINQ涉及大量的线程的创建和销毁操作,这也是需要耗时的,因此要强调当执行一次查询需要花费的时间比较大时,才推荐使用PLINQ来代替LINQ
再次进行测试,将之前代码进行一些修改,让每次的查询时间变小一点,我们只需要简单的把Thread.Sleep()注释掉即可
采用LINQ的执行结果:
采用PLINQ的执行结果
比较两者的执行时间,发现在一次查询任务所需代价较小时,PLINQ相对于LINQ并没有明显的优势,反而在很多情况下PLINQ的处理速度要慢于LINQ