问题
既然LINQ可以对集合进行串行处理和并行处理,究竟哪种效率更高呢?目测是并行啊。
list是泛型集合。里面成装的是实体类。可以实例化。
dictionary的功能比较简单,只能算是功能强点的数组。里面储存的键值对。
那么对List和Dictionary两种集合的处理,效率是否一样?
下面分别对List和Dictionary展开并行和串行处理,进行效率比较。
测试
- 首先我们声明1个类,RichMan,不好意思,暴露自己想法了。
public class RichMan
{
public string name { get; set; }
public int money { get; set; }
public int Age { get; set; }
public DateTime CreateTime { get; set; }
}
- 分明产生1500w条list和Dictionary记录:
public static ConcurrentDictionary<int, RichMan> LoadData()
{
//这里采用并行字典,确保LINQ并行计算时不会报错
ConcurrentDictionary<int, RichMan> dic = new ConcurrentDictionary<int, RichMan>();
//预加载1500w条记录
Parallel.For(0, 15000000, (i) =>
{
var single = new RichMan()
{
money = i,
name = "qcj" + i,
Age = i % 200,
CreateTime = DateTime.Now.AddSeconds(i)
};
dic.TryAdd(i, single);
});
return dic;
}
public static List<RichMan> LoadListData()
{
List<RichMan> list = new List<RichMan>();
//预加载1500w条记录
for (int i = 0; i < 15000000; i++)
{
RichMan single = new RichMan()
{
name = "qcj" + i,
money = i,
Age = i % 200,
CreateTime = DateTime.Now.AddSeconds(i)
};
list.Add(single);
}
return list;
}
- 测试,4种方法依次来一遍:
static void Main(string[] args)
{
var dict = LoadData();
Stopwatch watch = new Stopwatch();
watch.Start();
//Dictionary串行执行
var query1 = (from n in dict.Values
where n.Age > 25 && n.Age < 30
select n).ToList();
watch.Stop();
Console.WriteLine("串行计算Dictionary耗费时间:{0}", watch.ElapsedMilliseconds);
watch.Restart();
//Dictionary并行执行
var query2 = (from n in dict.Values.AsParallel()
where n.Age > 25 && n.Age < 30
select n).ToList();
watch.Stop();
Console.WriteLine("并行Dictionary计算耗费时间:{0}", watch.ElapsedMilliseconds);
//查询list
var list = LoadListData();
watch.Restart();
//list串行执行
var query3 = (from n in list
where n.Age > 25 && n.Age < 30
select n).ToList();
watch.Stop();
Console.WriteLine("串行List计算耗费时间:{0}", watch.ElapsedMilliseconds);
watch.Restart();
//list并行执行
var query4 = (from n in list.AsParallel()
where n.Age > 25 && n.Age < 30
select n).ToList();
watch.Stop();
Console.WriteLine("并行List计算耗费时间:{0}", watch.ElapsedMilliseconds);
Console.Read();
}
- 结果如下
结果可以发现,linq并行计算效率明显高于串行计算,list并行计算最快。
问题剖析:
参考了 这篇文章.
同样是集合,为什么性能会有这样的差距。我们要从存储结构和操作系统的原理谈起。
首先我们清楚List是对数组做了一层包装,我们在数据结构上称之为线性表,而线性表的概念是,在内存中的连续区域,除了首节点和尾节点外,每个节点都有着其唯一的前驱结点和后续节点。我们在这里关注的是连续这个概念。
而HashTable或者Dictionary,他是根据Key而根据Hash算法分析产生的内存地址,因此在宏观上是不连续的,虽然微软对其算法也进行了很大的优化。
由于这样的不连续,在遍历时,Dictionary必然会产生大量的内存换页操作,而List只需要进行最少的内存换页即可,这就是List和Dictionary在遍历时效率差异的根本原因。
在这里我们除了刚才的遍历问题,还要提到Dictionary的存储空间问题,在Dictionary中,除了要存储我们实际需要的Value外,还需要一个辅助变量Key,这就造成了内存空间的双重浪费。
而且在尾部插入时,List只需要在其原有的地址基础上向后延续存储即可,而Dictionary却需要经过复杂的Hash计算,这也是性能损耗的地方。