并行编程之PLINQ

并行 LINQ (PLINQ) 是 LINQ 模式的并行实现。PLINQ 的主要用途是通过在多核计算机上以并行方式执行查询委托来加快 LINQ to Objects 查询的执行速度。与顺序 LINQ 查询一样,PLINQ 查询对任何内存中 IEnumerable 或 IEnumerable<(Of <(T>)>) 数据源进行操作,并推迟执行,这意味着在枚举查询之前不会开始执行这些操作。主要区别是 PLINQ 尝试充分利用系统中的所有处理器。它利用所有处理器的方法是,将数据源分成片段,然后在多个处理器上对单独工作线程上的每个片段并行执行查询。在许多情况下,并行执行意味着查询运行速度显著提高。

通过并行执行,PLINQ 通常只需向数据源添加 AsParallel 查询操作,即可在某些查询类型的旧版代码上获得显著的性能改进。但是,并行可能引入其自己的复杂性,因此并非所有查询操作在 PLINQ 中都运行得更快。事实上,并行降低了某些查询的速度。

System.Linq..::.ParallelEnumerable 类公开 PLINQ 的几乎所有功能。此类和 System.Linq 命名空间类型的其余部分一起编译到 System.Core.dll 程序集中。

1、选择使用模型

当您编写查询时,通过在数据源上调用 ParallelEnumerableAsParallel()()() 扩展方法来选择使用 PLINQ,如下面的示例所示。

var source = Enumerable.Range(1, 10000);

// Opt-in to PLINQ with AsParallel

var evenNums = from num in source.AsParallel()

where Compute(num) > 0

select num;

2、并行度

默认情况下,PLINQ 使用主机上的所有处理器,这些处理器的数量最多可达 64 个。通过使用 WithDegreeOfParallelism()()() 方法,可以指示 PLINQ 使用不多于指定数量的处理器。这在当您要确保计算机上运行的其他进程收到一定的 CPU 时间量时非常有用。下面的代码段将查询限制为最多使用两个处理器。

var query = from item in source.AsParallel().WithDegreeOfParallelism(2)

where Compute(item) > 42

select item;

3、总体工作的计算开销

为了实现加速,PLINQ 查询必须具有足够的适合并行工作来弥补开销。工作可表示为每个委托的计算开销与源集合中元素数量的乘积。假定某个操作可并行化,则它的计算开销越高,加速的可能性就越大。例如,如果某个函数执行花费的时间为 1 毫秒,则针对 1000 个元素进行的顺序查询将花费 1 秒来执行该操作,而在四核计算机上进行的并行查询可能只花费 250 毫秒。这样就产生了 750 毫秒的加速。如果该函数对于每个元素需要花费 1 秒来执行,则加速将为 750 秒。如果委托的开销很大,则对于源集合中的很少几个项,PLINQ 可能会提供明显的加速。相反,包含无关紧要委托的小型源集合通常不适合于 PLINQ。

在下面的示例中,queryA 可能适合于 PLINQ(假定其 Select 函数涉及大量工作)。queryB 可能不适合,原因是 Select 语句中没有足够的工作,并且并行化的开销将抵销大部分或全部加速。

var queryA = from num in source.AsParallel()

select ExpensiveFunction(num); //good for PLINQ

var queryB = from num in source.AsParallel()

where num % 2 > 0

select num; //not as good for PLINQ

4、PLINQ 何时选择顺序模式

PLINQ 将始终尝试至少按与查询以顺序方式运行同样的速度来执行查询。尽管 PLINQ 不会查看用户委托的计算开销有多高或输入源有多大,但它却会查找某些查询"形状"。具体而言,它将查找通常会导致查询在并行模式下运行更慢的查询运算符或运算符组合。如果找到此类形状,PLINQ 默认情况下会转而使用顺序模式。

但是,在衡量特定查询的性能后,您可能会确定该查询在并行模式下实际运行更快。在这些情况下,您可以通过 ParallelEnumerableWithExecutionMode()()() 方法使用 ParallelExecutionMode..::.ForceParallelism 标志,以指示 PLINQ 对查询进行并行化。有关更多信息,请参见如何:在 PLINQ 中指定执行模式。

下面的列表介绍了 PLINQ 默认情况下将按顺序模式执行的查询形状:

  • 包含 Select 子句、已建立索引的 Where 子句、已建立索引的 SelectMany 子句或 ElementAt 子句的查询(在排序或筛选运算符移除或重新排列了索引后)。
  • 包含 Take、TakeWhile、Skip、SkipWhile 运算符并且源序列中的索引未采用原始顺序的查询。
  • 包含 Zip 或 SequenceEquals 的查询,除非其中一个数据源具有按原始顺序排列的索引,并且另一个数据源可建立索引(即,数组或 IList(T))。
  • 包含 Concat 的查询,除非将其应用到可建立索引的数据源。
  • 包含 Reverse 的查询,除非应用到可建立索引的数据源。

5、PLINQ 中的顺序保留

下面的示例演示一个未排序的并行查询,该查询筛选与某个条件匹配的所有元素,而不会尝试以任何方式对结果进行排序。此查询不一定会生成源序列中满足条件的前1000 个城市,而是会生成满足条件的某一组 1000 个城市。您可以通过对源序列使用 AsOrdered()()() 运算符来启用顺序保留。然后,您可以稍后通过使用 AsUnOrdered()()() 方法在查询中禁用顺序保留。

var cityQuery = (from city in cities.AsParallel()

where city.Population > 10000

select city)

.Take(1000);

查询运算符和排序

下面的查询运算符将顺序保留引入查询的所有后续运算中,或直至调用 AsUnordered()()() 为止:

  • OrderBy()()()
  • OrderByDescending()()()
  • ThenBy()()()
  • ThenByDescending()()()

下面的 PLINQ 查询运算符在某些情况下可能要求经过排序的源序列生成正确的结果:

  • Reverse()()()
  • SequenceEquals()()()
  • TakeWhile()()()
  • SkipWhile()()()
  • Zip()()()

并行编程之PLINQ的更多相关文章

  1. .Net并行编程之二:并行循环

    本篇内容主要包括: 1.能够转化为并行循环的条件 2.并行For循环的用法:Parallel.For 3.并行ForEach的用法Parallel.ForEach 4.并行LINQ(PLINQ)的用法 ...

  2. Pthreads并行编程之spin lock与mutex性能对比分析(转)

    POSIX threads(简称Pthreads)是在多核平台上进行并行编程的一套常用的API.线程同步(Thread Synchronization)是并行编程中非常重要的通讯手段,其中最典型的应用 ...

  3. 并行编程之CountdownEvent的用法

    教程:http://blog.gkarch.com/threading/part5.html#the-parallel-class http://www.cnblogs.com/huangxinche ...

  4. 异步编程之Promise(3):拓展进阶

    异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...

  5. iOS 多线程编程之Grand Central Dispatch(GCD)

    介绍: Grand Central Dispatch 简称(GCD)是苹果公司开发的技术,以优化的应用程序支持多核心处理器和其它的对称多处理系统的系统.这建立在任务并行运行的线程池模式的基础上的. 它 ...

  6. 深入浅出Cocoa多线程编程之 block 与 dispatch quene

    深入浅出 Cocoa 多线程编程之 block 与 dispatch quene 罗朝辉(http://www.cppblog.com/kesalin CC 许可,转载请注明出处 block 是 Ap ...

  7. python并发编程之multiprocessing进程(二)

    python的multiprocessing模块是用来创建多进程的,下面对multiprocessing总结一下使用记录. 系列文章 python并发编程之threading线程(一) python并 ...

  8. Python函数式编程之map()

    Python函数式编程之map() Python中map().filter().reduce()这三个都是应用于序列的内置函数. 格式: map(func, seq1[, seq2,…]) 第一个参数 ...

  9. [Cocoa]深入浅出Cocoa多线程编程之 block 与 dispatch quene

    深入浅出 Cocoa 多线程编程之 block 与 dispatch quene 罗朝辉(http://www.cppblog.com/kesalin CC 许可,转载请注明出处 block 是 Ap ...

随机推荐

  1. ABP展现层——Javascript函数库

    ABP展现层——Javascript函数库 点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之21.ABP展现层——Javascript函数库 ABP是“ASP.N ...

  2. Matlab与.NET基于类型安全的接口混合编程入门

    原文:[原创]Matlab与.NET基于类型安全的接口混合编程入门 如果这些文章对你有用,有帮助,期待更多开源组件介绍,请不要吝啬手中的鼠标. [原创分享]Matlab.NET混编调用Figure窗体 ...

  3. IT互联网行业中相关职能的缩写

    RD – Research & Develop 研发FE – Front End 前端QA – Quality Assurance 测试DBA – Database Administrator ...

  4. 深入了解Libgdx中间Skin分类

    文不是直接翻译.. . 本文在Libgdx的官方wiki的基础上,加上一些自己的理解.所以,难免会一些甚至是非常多的理解非常片面的东西.写的不好,还请见谅.... 事实上 事实上.在LibGDX的官方 ...

  5. Blocks and Variables

    Blocks and Variables https://developer.apple.com/library/ios/documentation/cocoa/conceptual/Blocks/A ...

  6. Javascript 继承 call与prototype

    function Parent(hello){ this.hello = hello; this.sayHello = function(){ alert(this.hello); } } Paren ...

  7. js 正则学习小记之匹配字符串优化篇

    原文:js 正则学习小记之匹配字符串优化篇 昨天在<js 正则学习小记之匹配字符串>谈到 个字符,除了第一个 个,只有 个转义( 个字符),所以 次,只有 次成功.这 次匹配失败,需要回溯 ...

  8. linux高级技巧:rsync同步(一个)

    1.rsync基本介绍         rsync这是Unix下的一款应用软件,它能同步更新两处计算机的文件与文件夹,并适当利用差分编码以降低数据传输.rsync中一项与其它大部分类似程序或协议中所未 ...

  9. P31RestKit.dll 2.0 Unity3d json

    using System.Collections.Generic; using UnityEngine; using System.Collections; using Prime31; public ...

  10. 【C语言的日常实践(十六)】字符串输出功能puts、fputs和printf

    C有三个标准库函数的输出字符串puts().fputs()和printf(). 1.puts()函数仅仅须要给出字符串參数的地址. #include <stdio.h> int puts( ...