并行设计模式:并行流水线与并行查找

流水线与指令重排

我们知道进程切换开销是很大的,CPU首先保存当前被中断进程现场,然后把系统堆栈指针保存到PCB中,接着去处理中断,选择下一个占用CPU的进程。引入线程的目的就是减少了系统的时空开销,因为现在多核处理器已经十分普遍,设想如果一个程序它只有一个线程,那么在一个双核处理器上运行该程序,那么同一时刻只有一个核在运行,另一个核就处于空闲状态,导致系统资源浪费。程序使用多线程,可以充分利用多核处理器,提高系统吞吐量和并发性,因为多线程发挥了多核CPU的优势,实现了真正的并行操作。

多线程并行执行虽然能提高效率,但是需要认真考虑,解决数据一致性的问题,即围绕原子性、可见性和有序性来保证多线程之间的合作。在这里,并行流水线中,我们重点关注的是有序性问题,在我们写代码的过程中,内心可能感觉,代码的执行顺序和行数顺序基本一样,由上到下依次执行。不过,JVM可能会在程序执行时,进行指令重排,即对现有指令的执行顺序进行重新排序,导致代码的执行顺序可能并不如程序员所想的那样依次执行。进行指令重排的原因,是为了提高CPU的效率,为什么这么说?假设指令是串行执行的,指令A是去存储器中读值,花费的时间比较多,假设用了20毫秒,那么排在指令A后面的指令B必须等待着20毫秒后,才能执行。但如果指令B执行的动作与指令A访问的值无关,且指令B也不会去访问存储器,只是在自己CPU的高速缓存中操作,那么就可以让指令B在等待指令A执行后,立刻执行,不用等待指令A的执行完成。流水线技术就是这样来的,利用每条指令可能访问或使用不同的硬件,调整指令的执行顺序来提高效率。

再举个明白点的例子:假设有以下指令:

LOAD R1,A;

LOAD R2,B;

ADD R3,R2,R1;

LOAD R4,C;

LOAD R5,D;

ADD R6,R5,R5;

指令load R1,A;把A的值读入到寄存器R1中,另一条指令load R2,B;把B的值读入到寄存器R2中,第三条指令是ADD R3,R2,R1;把寄存器R2和寄存器R1的值相加,然后读入到寄存器R3中,下同。由于第三条指令ADD R3,R2,R1;要等待寄存器R1和寄存器R2的值,导致下面的指令也要跟着一起等待(假设我们的代码是串行执行的),整段代码就慢了下来。可是我们看,下面的指令把C的值读入寄存器R3和把D的值读入寄存器R4,都不会对上面的ADD有影响,因为它们不会访问到寄存器R1到R3,所以,如果把指令重排,在ADD R3,R2,R1;指令执行等待后,接着执行C,D的值写入寄存器的指令,就能在可能发生等待的时候,去做其他的事情,不浪费等待的时间。

当然,指令重排不会总是发生的,它是有规则遵循的,具体大家可以自行查看“Happen-Before”规则,这篇日志不详细列出。

并行流水线

上面讲了一堆流水线和指令重排,我想表达的是,串行执行有时可能会浪费很多不必要的等待时间,如果把串行改成并行执行,就能节省很多时间,毕竟多核CPU早已成为趋势,能够胜任并发程序的执行工作。并行流水线就是一种并行模式,借鉴了流水线的方式,分工合作提升整体的效率。

来看个例子,假如程序要执行计算(A+B)/C-D,这个式子计算至少要分为三步:

  1. A+B;
  2. (A+B)/C;
  3. (A+B)/C-D;

假设代码只能串行执行,把整个计算过程放到一个线程上运算,可以看到,第一步A+B未完成&#x

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值