



1、并行网关
假设现在我们想在旁边放一份沙拉。无论如何,如果你想要沙拉,你可以像我们在图1.1中所做的那样建模。
图1.1:准备沙拉和主菜。
在这里,我们介绍了另一个符号:(文本)注释;这是一个您可以与任何流对象(在本例中是任务)关联的工件。你可以输入任何文本;在我们的示例中,我们输入了执行相关任务的平均时间。任务时间的总和等于流程的运行时间,意大利面的运行时间为48分钟,牛排的运行时间为43分钟。祝贺您:您已经基于关键数据分析了您的第一个流程!
不过,这意味着要等23分钟甚至28分钟才能开始吃东西。难以忍受的!你真的很饿,但你能做什么?也许你不会先准备沙拉,然后再做意大利面或牛排,但你会同时做这两件事——同时做。适当的符号是并行网关,或简称和网关,如图1.2所示。
图1.2:同时准备沙拉和主菜。
将任务画成并行并不意味着必须同时处理。与图1.1所示的示例相反,在开始其他任务之前也不是必须准备沙拉。然而,并行准备确实使我们的总时间减少了10分钟。使任务尽可能并行化是经典的流程优化。
如示例所示,该进程不仅是并行的(它使用了and split),而且路径稍后还会同步(an and merge)。原因很容易理解:你只能在主菜和配菜都准备好之后才能开始吃。
令牌的概念如何应用于此流程的实例?令牌在开始事件时产生,它运行choose recipe任务,然后插入and split。每个路径都从网关中出现一个令牌。这意味着在本例中有两个令牌:第一个令牌进入xor分割,其传出路径取决于所选的菜谱。
假设我们想做意大利面。令牌进入任务并停留15分钟。同时,第二个令牌进入第二个,准备沙拉任务,它只停留10分钟。10分钟后,它移到合并处。传入路径的数量决定了网关正在等待的相关令牌的数量,因此在这里,它等待相同流程实例的两个令牌。
在我们的场景中,第二个令牌在10分钟后到达and merge,而第一个令牌在cook pasta中总共停留了15分钟。这意味着and合并将等待到第一个令牌到达—额外的5分钟。此时,令牌愉快地合并为单个令牌,该令牌将继续沿着传出路径运行。
这听起来是不是太抽象或太专业了?它不是,这和你自己的做法是一样的:沙拉准备好了,但意大利面还没有,所以你等着。当意大利面最终做好时,你就可以吃了。
那么,为什么使用这个看似复杂的符号概念呢?例如,每年信用机构创建9000万个流程实例,这些并不是按照严格的顺序执行的,他们有重叠,为了每天正确地定义和执行这种复杂的流程及其各种并行操作、分支、合并和同步,令牌方法就对对概念设计和实现是非常有帮助,而且非常必要。我们希望到目前为止,流程实例与令牌并不完全相同:许多令牌可以在单个流程实例的范围内运行。
用下面的问题检查你的理解:
问题:图1.3显示了相同的过程,但是由于缺少空间,省略了and merge,并且从prepare沙拉任务的路径直接通向xor合并。如果我们实例化这个过程,并决定支持意大利面,会发生什么?
图1.3:在这个过程中发生了什么?
答:在and分割时生成令牌,然后像往常一样进行克隆。当我们完成准备沙拉,令牌通过xor合并和吃饭执行,五分钟后,意大利面也做好了,它的令牌通过xor合并,并再次执行!这不是我们想要的行为。
问题:图1.4显示了一个只包含两个任务的流程。实例化后,流程实例存活多久?
图1.4:流程实例存在多长时间?
答:它存在45天,这与进程的运行时间相对应。即使在and split中生成的令牌在30天后通过task 1,然后被上端事件使用,第二个令牌仍然在task 2中驻留15天,流程实例继续存在,直到低端事件使用第二个令牌为止。
注意:只要流程中有一个令牌存在,流程实例就存在!在使用所有生成的令牌之前,该实例无法完成。
本文会持续更新,欢迎关注,技术支持:盘古BPM
一、并行计算
简单地说,并行计算就是在并行计算机上所做的计算。从普通意义上讲,它和常说的高性能计算、超级计算等是同义词。并行计算的初衷是为了努力仿真自然世界中一个序列中含有众多同时发生的、复杂且相关事件的事务状态。
为了利用并行计算求解一个计算问题,通常基于以下考虑:1.将计算任务分解成多个子任务,有助于同时解决;2.在同一时间,由不同的执行部件可同时执行多个子任务;3.多计算资源下解决问题的耗时要少于单个计算资源下的耗时。
并行计算可分为:1.计算密集型:如大型科学工程计算与数值模拟等;2.数据密集型:如数字图书馆、数据仓库、数据挖掘和计算可视化等;3.网络密集型:如协同计算和远程诊断等。二、并行计算机体系结构
1.并行计算机结构模型
(1)结构类型
(2)几种MIMD
注:对称指所有处理器都能同等地访问I/O很同样的运行程序(如OS和I/O服务程序),而非对称主从式是仅有主处理器运行OS和控制访问I/O并监控从处理器执行
UMA(Uniform Memory Access)均匀存储访问:物理存储器被所有处理器均匀共享,所有处理器对所有SM访存时间相同,每台处理器可带有高速私有缓存,外围设备共享。
- NUMA非均匀存储访问:共享的SM是由物理分布式的LM逻辑构成,处理器访存时间不一样,访问LM或CSM(群内共享存储器)内存储器比访问GSM(群间共享存储器)快
- COMA(Cache-Only MA)全高速缓存存储访问:NUMA的特例、全高速缓存实现
- CC-NUMA(Coherent-Cache NUMA)高速缓存一致性NUMA:NUMA+高速缓存一致性协议
- NORMA(No-Remote MA)非远程存储访问:无SM,所有LM私有,通过消息传递通信
衡量并行计算机性能单位:
TOP500前500名超级计算机排名指标(GFLOPS):
Rmax:Maximal LINPACK(Linear system package) performance achieved
Rpeak:Theoretical peak performance
主要包括CPU和存储器的某些基本性能指标,并行通信开销以及机器的成本、价格和性能价格比等。
(1)CPU和存储器的某些基本性能指标
并行执行时间:。其中 为计算时间, 为并行开销时间,包括进程管理(生成、切换和结束等)、组操作(进程组的生成与消亡等)时间,和进程查询(询问进程的标志、等级和组大小等)时间; 为相互通信时间,包括同步(如路障、临界区、事件等)、通信(如点到点通信、整体通信、读/写共享变量等)时间和聚操作(如归约和前缀运算等)时间。
存储器的层次结构:各层存储器的容量、延迟和带宽。
(2)通信开销
(3)机器的成本、价格和性能价格比
包括加速、效率和可扩放性等。
(1)并行系统的加速(比)
指对于一个给定的应用,并行算法(或并行程序)的执行速度相对于串行算法(或串行程序)的执行速度加快了多少倍。
加速比性能定律:
Amdahl加速定律(固定计算负载)
Gustafson定律(适用于可扩放问题)
Sun&Ni定律(受限于存储器)
约定:
(2)扩放性
最朴素的含义是在确定的应用背景下,计算机系统(或算法或编程等)性能随处理器数的增加而按比例提高的能力。被广泛用来描述并行算法能否有效利用可扩充的处理器的能力。
基本测试程序、数学库测试程序和并行测试程序等
并行算法是一些可同时执行的诸进行的集合,这些进程互相作用和协调动作从而实现给定问题的求解。
从不同角度分类成数值计算和非数值计算的并行算法;同步、异步和分布的并行算法;共享存储和分布存储的并行算法;确定的和随机的并行算法等。
(1)par-do
n个节点并行完成for循环(每个节点不同,和i相关):
for i = 1 to n par-do
...
endfor
(2)for all
所有节点都执行相同语句:
for all Pi, where 0 <= i <= k do
...
endfor
通常分析以下指标:
并行算法的WT表示——Brent定理:
令W(n)是并行算法A在运行时间T(n)内所执行的运算量,则A使用p台处理器可在t(n) = O(W(n)/p + T(n))时间内执行完毕
1.PRAM模型(SIMD-SM)
PRAM(Parallel Random Access Machine)并行随机存取机器,是一种抽象并行计算模型,它假设:
根据并发访问机制,又分为:
PRAM-CRCW又分为:
PRAM优点:
PRAM缺点:
2.异步APRAM模型(MIMD-SM)
异步APRAM模型假设:
指令类型有:
APRAM比PRAM更加接近实际并行机
3.BSP模型(MIMD-DM)
BSP(Bulk Synchronous Parallel)大同步并行机(APRAM算作轻量)是一个分布式存储的MIMD模型,它的计算由若干全局同步分开的、周期为L的超级步组成,各超级步中处理器做LM操作并通过选路器接收和发送消息;然后做一次全局检查,以确定该超级步是否已经完成(块内异步并行,块间显式同步)
参数:处理器数p、选路器吞吐率g、全局同步间隔L、一个超级步中一个处理器至多发送或接收h条消息
4.LogP模型:MIMD-DM,点到点通讯
LogP模型是分布式存储、点到点通信的MIMD模型
LogP采取隐式同步,而不显式同步障
1 、串改并
发掘和利用现有串行算法中的并行性,直接将串行算法改造为并行算法
最常用的设计思路但并不普适,好的串行算法一般无法并行化(数值串行算法可以)
快速排序的串改并
SISD上串行执行,最坏情况下O(n^2),理想情况下O(nlogn)
思路:将O(n)的划分(Partition)并行化是关键,算法:
在CRCW模型上,用伪代码描述如下:
//A[1...n]排序用n个处理器,处理器i中存有A[i]
//f[i]中存有其元素是主元素的处理器号
//LC[1...n]和RC[1...n]分别记录给定主元素的左儿子和右儿子
for each processor i par-do
root = i //所有处理器竞争,只有一个写入root
f[i] = root //所有处理器都把root写入自己的f[i]
LC[i] = RC[i] = n + 1 //初始值
endfor
repeat for each processor i != root do
if (A[i] < A[f[i]]) || (A[i] == A[f[i]] && i < f[i])
LC[f[i]] = i //并发写,所有满足条件的i只有一个写入LC[f[i]]作为左子树的根,也就是下一次循环的主元素
if i == LC[f[i]] then
exit //若当前处理器写入则什么也不做
else
f[i] = LC[f[i]] //若当前处理器没有写入,那么它只能当LC[f[i]]的字节点了
endif
else
RC[f[i]] = i //并发写,所有满足条件的i只有一个写入RC[f[i]]作为右子树的根,也就是下一次循环的主元素
if i == RC[f[i]] then
exit //若当前处理器写入则什么也不做
else
f[i] = RC[f[i]] //若当前处理器没有写入,那么它只能当RC[f[i]]的字节点了
endif
endif
endrepeat
每次迭代构造一层排序二叉树花费O(1)时间,在基本平衡的情况下树高O(logn),则算法复杂度为O(logn)
2 、全新设计
从问题本身描述出发,不考虑相应的串行算法,设计 一个全新的并行算法
有向环K着色算法的并行化设计
问题:有向环顶点着色,一共K种颜色,相邻顶点不允许同色
串行算法(3着色):交替2种颜色,若顶点是奇数则需要用到第3种颜色(难以并行化)
SIMD-EREW上的并行K着色算法:
//初始随机着色为c[i],每个顶点着色不同
//输出着色方案为nc[i]
for i = 1 to n par-do
k = c[i]和c[i的后继]的最低的不同二进制位
nc[i] = 2 * k + c[i]的二进制第k位
endfor
O(1)时间完成,需要算法正确性证明
3、借用法
找出求解问题和某个已解决问题之间的联系,改造或利用已知算法应用到求解问题上
利用矩阵乘法求所有点对间最短路径
d[k][i][j]表示从vi到vj**至多**经过k-1个中间顶点时的最短路径,d[k][i][j] = min{d[k/2][i][l]+d[k/2][l][j]}
那么可以用矩阵乘法的改进(乘变加,求和变min)做logn次矩阵乘法即可
思路是这样,具体伪代码略,需要O(n^3)个节点,时间复杂度为O(log^2(n))
1、划分设计技术
使用划分法把问题求解分成两步:
(1)均匀划分(PSRS排序)
长度为n的待处理序列均匀划分给p个处理器,每个处理器处理n/p个元素
MIMD机器上的PSRS排序算法:
(1)均匀划分:将n个元素A[1..n]均匀划分成p段,分配给p个处理器
(2)局部排序:pi调用串行排序算法对A[(i-1)n/p+1..in/p]排序
(3)选取样本:pi从其有序子序列A[(i-1)n/p+1..in/p]中选取p个样本元素
(4)样本排序:用一台处理器对p2个样本元素进行串行排序
(5)选择主元:用一台处理器从排好序的样本序列中选取p-1个主元,并播送给其他pi
(6)主元划分:pi按主元将有序段A[(i-1)n/p+1..in/p]划分成p段
(7)全局交换:各处理器将其有序段按段号交换到对应的处理器中(一定保证均匀划分??)
(8)归并排序:各处理器对接收到的元素进行归并排序
(2)方根划分(Valiant归并排序)
长度为n的待处理序列,取i*sqrt(n)为划分元,将元素划分成若干段交给处理器处理
SIMD-CREW机器上的Valiant归并排序:
(1)方根划分:把A和B(长n有序段)的第i*sqrt(n)元素作为划分元,把A和B分成若干段
(2)段间比较:A中的所有划分元和B中的所有划分元比较,确定A中划分元应插入B中的哪一段
(3)段内比较:A中的划分元和B中相应段比较并确定插入位置,这些插入位置又将B重新划分成了若干段
(4)段组合并:插入A划分元后,又得到若干有序段组需要归并,递归直到有一组(A)的长度为0
使用n个处理器可以在O(loglogn)内完成
(3)对数划分(并行归并排序)
取i*logn作为划分元划分
定义位序rank(x,X)为X中小于等于x的元素个数,则有PRAM-CREW上的对数划分:
//非降有序的A={a[1],...,a[n]}和B={b[1],...,b[m]}归并
//假设log(m)和k=m/log(m)均为整数
j[0]=0
j[k]=n
for i = 1 to k - 1 par-do
j[i] = rank(b[ilogm], A)
endfor
for i = 1 to k - 1 par-do
Bi = {b[ilogm+1], ..., b[(i+1)logm]}
Ai = {a[j[i]+1], ..., a[j[i+1]]}
endfor
//将原问题转化为子序列组Bi和Ai的归并,那么同样可以递归调用完成整个序列的归并
//对数划分保证Bi和Ai中的元素均大于Bi-1和Ai-1中的元素
(4)功能划分( (m,n)-选择)
功能划分是根据特定问题而把序列分成p个等长组,每组符合问题特性的一种划分方法
(m,n)-选择问题是将长为n的序列中选取前m个较小的元素,利用功能划分来实现并行化的(m,n)-选择问题求解:
(1)功能划分:将A划分成g=n/m组,每组含m个元素
(2)局部排序:使用Batcher排序网络将各组并行排序
(3)两两比较:将所排序的各组两两比较,从而形成MIN序列
(4)排序-比较:对各个MIN序列,重复执行第(2)和第(3)步,直至选出m个最小者
2、分治设计技术
分治将复杂问题划分成较小规模特性相同的子问题,且子问题类型和原问题类型相同,通常用递归完成分治算法
3、平衡树设计技术
以树的叶结点为输入,中间结点为处理结点,由叶向根或由根向叶逐层进行并行处理
4、倍增设计技术
递归调用时,所要处理数据之间的距离逐步加倍, 经过k步后即可完成距离为2^k的所有数据的计算
5、流水线技术
将算法路程分成p个前后衔接的任务片段,一个任务片段完成之后其后继任务片段可以立即开始,那么久可以引入流水线的思想来处理多条数据
1.1 并行语言构造方法
1.2 并行性问题
可利用SPMD来伪造MPMD
需要运行MPMD:parbegin S1 S2 S3 parend
可以改造成SPMD:
for i = 1 to 3 par-do
if i == 1 then S1
else if i == 2 then S2
else if i == 3 then S3
endfor
那么并行扩展至需要支持SPMD即可
1.3 交互/通信
(1)交互类型
(2)交互方式
(3)交互模式
按编译时是否能确定交互模式可分为静态的、动态的
按多少发送者和接收者:
1.4 并行编程风范
1.5 并行程序设计模型
(1)隐式并行
用串行语言编程,编译器货操作系统自动转化成并行代码
特点:语义简单、可以执行好、易调试易验证、but效率低
(2)数据并行
SIMD模型,包括数据选路和局部计算,特点:但现场、松散同步、常用聚合操作
数据并行计算π:
long i,j,t,N=100000;
double local[N], temp[N], pi, w;
w = 1.0/N;
forall (i = 0; i < N; i++) {
local[i] = (i + 0.5) * w;
temp[i] = 4.0 / (1.0 + local[i] * local[i]);
}
pi = reduce(temp, +);
(3)消息传递
MPP、COW自然模型,MPI广泛应用:多线程异步并行、地址空间分开、常用SPMD形式编码
MPI消息传递计算π:
#define N 100000
main(){
double local=0.0,pi,w,temp=0.0;
long i,taskid,numtask;
w=1.0/N;
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&taskid);
MPI_Comm_Size(MPI_COMM_WORLD,&numtask);
for (i = taskid; i < N; i=i + numtask){
temp = (i+0.5)*w;
local = 4.0/(1.0+temp*temp)+local;
}
MPI_Reduce(&local,&pi,1,MPI_Double,MPI_MAX,0, MPI_COMM_WORLD);
if (taskid == 0) printf(“pi is %f \n”,pi*w);
MPI_Finalize() ;
}
(4)共享变量
PVP、SMP、DSM自然模型,多线程异步,显示同步而隐式通信
OpenMP使用共享变量计算π:
#define N 100000
main(){
double local,pi=0.0,w;
long i;
w=1.0/N;
#pragma parallel
#pragma shared(pi, w)
#pragma local(i, local)
{
#pragma parallel for (i = 0; i < N; i++)
{
local = (i + 0.5) * w;
local = 4.0 / (1.0 + local * local);
}
#pragma critical
{
pi = pi + local
}
}
}
OpenMP是FORK-JOIN模型,主线程串行执行,直到编译制导并行域出现
MPI是一种消息传递接口的标准,适用于分布式存储系统的编程模型
与OpenMP不同的是,MPI是多进程的并行模式,运行时需要在外部指定开启进程数,并且是用SPMD的编程风格去模拟MPMD的编程风格(用进程号区别),不会FORK-JOIN而是通过消息传递同步
C#并行编程(3):并行循环
初识并行循环
并行循环主要用来处理数据并行的,如,同时对数组或列表中的多个数据执行相同的操作。
在C#编程中,我们使用并行类
System.Threading.Tasks.Parallel
提供的静态方法Parallel.For
和Parallel.ForEach
来实现并行循环。从方法名可以看出,这两个方法是对常规循环for
和foreach
的并行化。简单用法
使用并行循环时需要传入循环范围(集合)和操作数据的委托
Action<T>
:Parallel.For(0, 100, i => { Console.WriteLine(i); }); Parallel.ForEach(Enumerable.Range(0, 100), i => { Console.WriteLine(i); });
使用场景
对于数据的处理需要耗费较长时间的循环适宜使用并行循环,利用多线程加快执行速度。
对于简单的迭代操作,且迭代范围较小,使用常规循环更好好,因为并行循环涉及到线程的创建、上下文切换和销毁,使用并行循环反而影响执行效率。
对于迭代操作简单但迭代范围很大的情况,我们可以对数据进行分区,再执行并行循环,减少线程数量。
循环结果
Parallel.For
和Parallel.ForEach
方法的所有重载有着同样的返回值类型ParallelLoopResult
,并行循环结果包含循环是否完成以及最低迭代次数两项信息。下面的例子使用
Parallel.ForEach
展示了并行循环的结果。ParallelLoopResult result = Parallel.ForEach(Enumerable.Range(0, 100), (i,loop) => {// 委托传入ParallelLoopState,用来控制循环执行 Console.WriteLine(i + 1); Thread.Sleep(100); if (i == 30) // 此处设置循环停止的确切条件 { loop.Break(); //loop.Stop(); } }); Console.WriteLine($"{result.IsCompleted}-{result.LowestBreakIteration}");
值得一提的是,循环的
Break()
和Stop()
只能尽早地跳出或者停止循环,而不能立即停止。取消循环操作
有时候,我们需要在中途取消循环操作,但又不知道确切条件是什么,比如用户触发的取消。这时候,可以利用循环的
ParallelOptions
传入一个CancellationToken
,同时使用异常处理捕获OperationCanceledException
以进行取消后的处理。下面是一个简单的例子。/// <summary> /// 取消通知者 /// </summary> public static CancellationTokenSource CTSource { get; set; } = new CancellationTokenSource(); /// <summary> /// 取消并行循环 /// </summary> public static void CancelParallelLoop() { Task.Factory.StartNew(() => { try { Parallel.ForEach(Enumerable.Range(0, 100), new ParallelOptions { CancellationToken = CTSource.Token }, i => { Console.WriteLine(i + 1); Thread.Sleep(1000); }); } catch (OperationCanceledException oce) { Console.WriteLine(oce.Message); } }); }
static void Main(string[] args) { ParallelDemo.CancelParallelLoop(); Thread.Sleep(3000); ParallelDemo.CTSource.Cancel(); Console.ReadKey(); }
循环异常收集
并行循环执行过程中,可以捕获并收集迭代操作引发的异常,循环结束时抛出一个
AggregateException
异常,并将收集到的异常赋给它的内部异常集合InnerExceptions
。外部使用时,捕获AggregateException
,即可进行并行循环的异常处理。下面的例子模拟了并行循环的异常抛出、收集及处理的过程。
/// <summary> /// 捕获循环异常 /// </summary> public static void CaptureTheLoopExceptions() { ConcurrentQueue<Exception> exceptions = new ConcurrentQueue<Exception>(); Parallel.ForEach(Enumerable.Range(0, 100), i => { try { if (i % 10 == 0) {//模拟抛出异常 throw new Exception($"{DateTime.Now}=> Thread-[{Thread.CurrentThread.ManagedThreadId}] had thrown a exception. [{i}]"); } Console.WriteLine(i + 1); Thread.Sleep(100); } catch (Exception ex) {//捕获并收集异常 exceptions.Enqueue(ex); } }); if (!exceptions.IsEmpty) {// 方法内部可直接进行异常处理,若需外部处理,将收集到的循环异常抛出 throw new AggregateException(exceptions); } }
外部处理方式
try { ParallelDemo.CaptureTheLoopExceptions(); } catch (AggregateException aex) { foreach (Exception ex in aex.InnerExceptions) {// 模拟异常处理 Console.WriteLine(ex.Message); } }
分区并行处理
当循环操作很简单,迭代范围很大的时候,ParallelLoop提供一种分区的方式来优化循环性能。下面的例子展示了分区循环的使用,同时也能比较几种循环方式的执行效率。
/// <summary> /// 分区并行处理,顺便比较各种循环的效率 /// </summary> /// <param name="rangeSize">迭代范围</param> /// <param name="opDuration">操作耗时</param> public static void PartationParallelLoop(int rangeSize = 10000, int opDuration = 1) { //PartationParallelLoopWithBuffer Stopwatch watch0 = Stopwatch.StartNew(); Parallel.ForEach(Partitioner.Create(Enumerable.Range(0, rangeSize), EnumerablePartitionerOptions.None), i => {//模拟操作 Console.WriteLine($"{DateTime.Now}=> Thread-[{Thread.CurrentThread.ManagedThreadId}] was running. [{i}]"); Thread.Sleep(opDuration); }); watch0.Stop(); //PartationParallelLoopWithoutBuffer Stopwatch watch1 = Stopwatch.StartNew(); Parallel.ForEach(Partitioner.Create(Enumerable.Range(0, rangeSize),EnumerablePartitionerOptions.NoBuffering), i => {//模拟操作 Console.WriteLine($"{DateTime.Now}=> Thread-[{Thread.CurrentThread.ManagedThreadId}] was running. [{i}]"); Thread.Sleep(opDuration); }); watch1.Stop(); //NormalParallelLoop Stopwatch watch2 = Stopwatch.StartNew(); Parallel.ForEach(Enumerable.Range(0, rangeSize), i => {//模拟操作 Console.WriteLine($"{DateTime.Now}=> Thread-[{Thread.CurrentThread.ManagedThreadId}] was running. [{i}]"); Thread.Sleep(opDuration); }); watch2.Stop(); //NormalLoop Stopwatch watch3 = Stopwatch.StartNew(); foreach (int i in Enumerable.Range(0, rangeSize)) {//模拟操作 Console.WriteLine($"{DateTime.Now}=> Thread-[{Thread.CurrentThread.ManagedThreadId}] was running. [{i}]"); Thread.Sleep(opDuration); } watch2.Stop(); Console.WriteLine(); Console.WriteLine($"PartationParallelLoopWithBuffer => {watch0.ElapsedMilliseconds}ms"); Console.WriteLine($"PartationParallelLoopWithoutBuffer => {watch1.ElapsedMilliseconds}ms"); Console.WriteLine($"NormalParallelLoop => {watch2.ElapsedMilliseconds}ms"); Console.WriteLine($"NormalLoop => {watch3.ElapsedMilliseconds}ms"); }
在 I7-7700HQ + 16GB 配置 VS调试模式下得到下面一组测试结果。
Loop Condition | PartationParallelLoop WithBuffer | PartationParallelLoop WithoutBuffer | Normal ParallelLoop | Normal Loop |
---|---|---|---|---|
10000,1 | 10527 | 11799 | 11155 | 19434 |
10000,1 | 9513 | 11442 | 11048 | 19354 |
10000,1 | 9871 | 11391 | 14782 | 19154 |
100,1000 | 9107 | 5951 | 5081 | 100363 |
100,1000 | 9086 | 5974 | 5187 | 100162 |
100,1000 | 9208 | 5125 | 5255 | 100239 |
100,1 | 350 | 439 | 243 | 200 |
100,1 | 390 | 227 | 166 | 198 |
100,1 | 466 | 225 | 84 | 197 |
应该根据不同的应用场景选择合适的循环策略,具体如何选择,朋友们可自行体会~
作 者:LayShun。
出 处:http://cnblogs.com/chenbaoshun/ 。
版权声明:本文原创发表于 博客园,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则视为侵权。
分类: [01]技术沉淀
标签: 并行编程
并行计算中,有两种并行的方法:任务并行(task-parallelism)和数据并行(data-parallelism)。任务并行:将许多可以解决问题的任务分割,然后分布在一个或者多个核上进行程序的执行。数据并行:将可以解决问题的数据进行分割,将分割好的数据放在一个或者多个核上进行执行;每一个核对这些数据都进行类似的操作。例:有一个professor P,他在给本科生上一门课,一共有300个本科生,考试的试卷上有15到题目。有三个助教来帮助Professor P修改试卷。那么,这三个助教可以采用数据并行或者任务并行的方法来完成任务。数据并行:每个人平均看100份试卷,然后分别对自己的试卷的15到题目打分。任务并行:每个人分别负责5个题目。转载于:https://www.cnblogs.com/SevenwindMa/p/4366218.html