CPU的乱序执行和合并写技术

CPU的乱序执行:

  CPU在进行读等待的同时执行指令,是CPU乱序的根源,这其实不是乱,而是提高效率。例如指令1去内存读数据,因为CPU与内存访问速度相差100倍,如果指令2的执行过程不需要依赖指令1,那么指令2可以先执行,乱序执行的本质是同时执行。Java对象的创建过程不是一个原子操作,极有可能出现指令重排序,下面通过Java对象创建的汇编码讲解。

// 源码:
class T { int num = 8;}
T t = new T();

// 汇编码:
0 new #2 <T>                    ---> new了一块内存,对象属性num赋初始值(03 dup                           ---> 复制栈中的引用,供下面的invokespecial消耗
4 invokespecial #3 <T.<init>>   ---> 执行构造方法,对象属性num赋默认值(8),将堆中的对象地址与栈中的引用建立关联
7 astore_1                      ---> 将栈中的引用弹出赋值局部变量表的第一个位置,第0个位置是this
8 return

在这里插入图片描述

DCL指令重排序

  这也是DCL(Double Check Lock)单例必须要加上volatile关键字的原因,CPU层面使用内存屏障禁止指令重排序,通过在指令1和指令2之间插入内存屏障来禁止指令重排序,Inter通过原语lfence(load), sfence(save), mfence(mixed)实现内存屏障,当然也可以使用总线锁来解决。

  1. sfence:在sfence指令前的写操作必须在sfence指令后的写操作前完成;
  2. lfence:在lfence指令前的读操作必须在lfence指令后的读操作前完成;
  3. mfence:在mfence指令前的读写操作必须在mfence指令后的读写操作前完成;
  4. lock:原子指令,如x86上的lock...指令是一个Full Barrier,执行时会锁住内存子系统来确保执行顺序,甚至跨越多个CPU,这是硬件层次.
  5. volatile:locl addl 0x0(exp),向exp寄存器中加0,主要是执行lock指令;
  6. sychronized:lock comxchg,通过自旋获得锁才能执行后面的操作;
// DCL单例
public class DCLInstance{
    private static volatile DCLInstance instance = null;
    private DCLInstance(){}
    
    public static DCLInstance getInstance(){
        if(instance==null){
            sychronized(DCLINstance.class){
                if(instance==null){
                    instance = new DCLINstance();
                }
            }
        }
        return instance;
    }
}
Write Combining 合并写技术:

  Write Combining Buffer一般是4个字节,由于ALU速度太快,为了提高写效率,CPU在写入L1时,写入一个WC Buffer,当WC Buffer满了之后,直接用WC写入L2。
在这里插入图片描述

Writer Combing

  可以通过程序对合并写技术进行验证,如下所示程序,runCaseOne中将7次写入操作一次性执行,runCaseTwo中将写操作分为两组,每组4次写操作,一共8次写操作,但是runCaseTwo的执行耗时却比runCaseOne要少。

public class WriteCombining {
    private static final int ITERATIONS = Integer.MAX_VALUE;
    private static final int ITEMS = 1 << 24;
    private static final int MASK = ITEMS - 1;
    private static final byte[] arrayA = new byte[ITEMS];
    private static final byte[] arrayB = new byte[ITEMS];
    private static final byte[] arrayC = new byte[ITEMS];
    private static final byte[] arrayD = new byte[ITEMS];
    private static final byte[] arrayE = new byte[ITEMS];
    private static final byte[] arrayF = new byte[ITEMS];

    public static long runCaseOne() {
        long start = System.nanoTime();
        int i = ITERATIONS;
        while (--i != 0) {
            int slot = i & MASK;
            byte b = (byte) i;
            arrayA[slot] = b;
            arrayB[slot] = b;
            arrayC[slot] = b;
            arrayD[slot] = b;
            arrayE[slot] = b;
            arrayF[slot] = b;
        }
        return System.nanoTime() - start;
    }

    public static long runCaseTwo() {
        long start = System.nanoTime();
        int i = ITERATIONS;
        while (--i != 0) {
            int slot = i & MASK;
            byte b = (byte) i;
            arrayA[slot] = b;
            arrayB[slot] = b;
            arrayC[slot] = b;
        }
        i = ITERATIONS;
        while (--i != 0) {
            int slot = i & MASK;
            byte b = (byte) i;
            arrayD[slot] = b;
            arrayE[slot] = b;
            arrayF[slot] = b;
        }
        return System.nanoTime() - start;
    }
    
    public static void main(final String[] args) {
        System.out.println("单次执行 (ms) = " + runCaseOne()/100_0000);
        System.out.println("拆分两次执行 (ms) = " + runCaseTwo()/100_0000);
    }
}

// 输出:
单次执行 (ms) = 4682
拆分两次执行 (ms) = 4462
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值