
       📝个人主页:五敷有你      
  🔥系列专栏:并发编程
 ⛺️稳重求进,晒太阳

指令集并行原理
名词:
Clock Cycle Time
CPU的Clock Cycle Time(时钟周期时间),等于主频的倒数。意思是CPU能识别的最小时间单位。
比如4G的CPU的CLockCycle Time就是0.25ns ,作为对比,我们挂钟的Cycle Time是1s
CPI
有的指令需要更多的时钟周期,所以引出了CPI ,指令平均时钟周期
IPC
IPC是CPI 的倒数,表示每个时钟周期能运行的指令数
CPU执行时间
程序的CPU执行时间 = 指令数 * CPI * Clokc Cycle Time
鱼罐头的故事
加工一条鱼需要50分钟,只能是一条鱼,一条鱼加工。。。

可以将每个鱼罐头的加工流程细分为 5 个步骤:
- 去鳞清洗 10分钟
 - 蒸煮沥水 10分钟
 - 加注汤料 10分钟
 - 杀菌出锅 10分钟
 - 真空封罐 10分钟
 

即使只有一个工人,最理想的清空是:他能够在 10 分钟内同时做好这 5 件事,因为对第一条鱼的真空装罐,不会影响对第二条鱼的杀菌出锅...
指令重排序优化
事实上,现代处理器会设计为一个时钟周期完成一条执行时间最长的 CPU 指令。为什么这么做呢?可以想到指令还可以再划分成一个个更小的阶段,例如,每条指令都可以分为: 取指令 - 指令译码 - 执行指令 - 内存访问 - 数据写回 这 5 个阶段

术语参考:
- instruction fetch (IF)
 - nstruction decode (ID)
 - execute (EX)
 - memory access (MEM)
 - register write back (WB)
 
在不改变程序结果的前提下,这些指令的各个阶段可以通过重排序和组合来实现指令级并行,这一技术在 80's 中叶到 90's 中叶占据了计算架构的重要地位。
提示:分阶段,分工是提升效率的关键!
指令重排的前提是,重排指令不能影响结果,例如
// 可以重排的例子 int a = 10; // 指令1 int b = 20; // 指令2 System.out.println( a + b ); // 不能重排的例子 int a = 10; // 指令1 int b = a - 5; // 指令2
支持流水线的处理器
现代 CPU 支持多级指令流水线,例如支持同时执行 取指令 - 指令译码 - 执行指令 - 内存访问 - 数据写回 的处理器,就可以称之为五级指令流水线。
这时 CPU 可以在一个时钟周期内,同时运行五条指令的不同阶段(相当于一条执行时间最长的复杂指令),IPC = 1,本质上,流水线技术并不能缩短单条指令的执行时间,但它变相地提高了指令地吞吐率。

SuperScalar 处理器
大多数处理器包含多个执行单元,并不是所有计算功能都集中在一起,可以再细分为整数运算单元、浮点数运算单元等,这样可以把多条指令也可以做到并行获取、译码等,CPU 可以在一个时钟周期内,执行多于一条指令,IPC> 1


诡异的结果
问题产生
int num=0;
boolean ready = false;
// 线程1 执行此方法
public void actor1(I_Result r) {
        if(ready) {
        r.r1 = num + num;
        } else {
        r.r1 = 1;
        }
        }
// 线程2 执行此方法
public void actor2(I_Result r) {
        num = 2;
        ready = true;
        } 
I_Result 是一个对象,有一个属性 r1 用来保存结果,问,可能的结果有几种?
情况1:线程1 先执行,这时 ready = false,所以进入 else 分支结果为 1
情况2:线程2 先执行 num = 2,但没来得及执行 ready = true,线程1 执行,还是进入 else 分支,结果为1
情况3:线程2 执行到 ready = true,线程1 执行,这回进入 if 分支,结果为 4(因为 num 已经执行过了)
情况4:这种情况下是:线程2 执行 ready = true,切换到线程1,进入 if 分支,相加为 0,再切回线程2 执行 num = 2
这种现象叫做指令重排,是 JIT 编译器在运行时的一些优化。
解决方法
volatile 修饰的变量,可以禁止指令重排序
@JCStressTest
@Outcome(id = {"1", "4"}, expect = Expect.ACCEPTABLE, desc = "ok")
@Outcome(id = "0", expect = Expect.ACCEPTABLE_INTERESTING, desc = "!!!!")
@State
public class ConcurrencyTest {
    int num = 0;
    volatile boolean ready = false;
    @Actor
    public void actor1(I_Result r) {
        if(ready) {
            r.r1 = num + num;
        } else {
            r.r1 = 1;
        }
    }
    @Actor
    public void actor2(I_Result r) {
        num = 2;
        ready = true;
    }
} 
结果为




















