java编程-硬件基础知识(缓存行,指令重排…)

很多情况下保持黑箱即可,因为打开这个黑箱,你会发现黑箱变成黑洞,吞噬你所有的时间和精力。有可能带你偏离原来的方向,陷入到不必要的细节中而无法自拔。 -- 适度打开即可

CPU的制作过程

Intel cpu的制作过程

https://haokan.baidu.com/v?vid=11928468945249380709&pd=bjh&fr=bjhauthor&type=video

CPU是如何制作的(文字描述)

https://www.sohu.com/a/255397866_468626

CPU的原理

计算机需要解决的最根本问题:如何代表数字

晶体管是如何工作的:

https://haokan.baidu.com/v?vid=16026741635006191272&pd=bjh&fr=bjhauthor&type=video

晶体管的工作原理:

https://www.bilibili.com/video/av47388949?p=2

汇编语言的执行过程

汇编语言的本质:机器语言的助记符 其实它就是机器语言

计算机通电 -> CPU读取内存中程序(电信号输入)

->时钟发生器不断震荡通断电(GHZ/s) ->推动CPU内部一步一步执行

(执行多少步取决于指令需要的时钟周期)

->计算完成->写回(电信号)->写给显卡输出(sout,或者图形)


输入处理输出流程.png

上述中:内存中数据不必写到cpu,而是直接由DMA处理(cpu告诉内存,把其某块的数据发送到显卡,DMA会通过数据总线将数据发送到显卡)

CPU的基本组成

  • PC -> Program Counter 程序计数器 (记录当前指令地址)
  • Registers -> 暂时存储CPU计算需要用到的数据
  • ALU -> Arithmetic & Logic Unit 运算单元
  • CU -> Control Unit 控制单元
  • MMU -> Memory Management Unit 内存管理单元
  • cache

缓存

一致性协议:https://www.cnblogs.com/z00377750/p/9180644.html

缓存行:折中都用64字节(程序的局部性原理,一次读取一块数据,而不是一个字节)

缓存行越大,局部性空间效率越高,但读取时间慢

缓存行越小,局部性空间效率越低,但读取时间快

cache一致性协议.png

英特尔采用的是以上缓存一致性协议
如果一个缓存行装载不下数据时,总线锁会介入,即一侧的核心处理完毕之后,另一侧才可以处理,这样效率会降低

cache line.png

上图中显示的是两个核心,xy处于一个cache line, 左边的核心要修改x,从L1读取,发现不存在,依次从L2,L3读取,直到main memory才找到,然后依次在L3,L2,L1都缓存一份,最后计算单元进行处理;而右边的核心要操作y,操作步骤与左边的核心相似,这样左右两边的核心各自持有一个cache line,但是操作的不同的数据,两个核心之间需要相互同步各自修改的,即伪共享

以下是模拟cache line 不是特别精确

public class T03_CacheLinePadding {
    public static volatile long[] arr = new long[2];
    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(()->{
            for (long i = 0; i < 10000_0000L; i++) {
                arr[0] = i;
            }
        });

        Thread t2 = new Thread(()->{
            for (long i = 0; i < 10000_0000L; i++) {
                arr[1] = i;
            }
        });

        final long start = System.nanoTime();
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println((System.nanoTime() - start)/100_0000);
    }
}
public class T04_CacheLinePadding {
    public static volatile long[] arr = new long[16];
    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(()->{
            for (long i = 0; i < 10000_0000L; i++) {
                arr[0] = i;
            }
        });
        Thread t2 = new Thread(()->{
            for (long i = 0; i < 10000_0000L; i++) {
                arr[8] = i;
            }
        });
        final long start = System.nanoTime();
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println((System.nanoTime() - start)/100_0000);
    }
}

缓存行对齐对于有些特别敏感的数字,会存在线程高竞争的访问,为了保证不发生伪共享,可以使用缓存行对齐的编程方式

  • JDK7中,很多采用long padding提高效率(让高竞争的数据永远处于一个缓存行且只有它自己,该方式适用于英特尔cpu)
    如下:无论如何截取cursor这个数据所处的缓存行只有它自己

    long padding.png

  • JDK8,加入了@Contended注解(实验)需要加上:JVM -XX:-RestrictContended

超线程

超线程.png

多组寄存器+PC。上图中为两组,减少了线程切换时,中间态数据的保存,这样同事状态两个线程的数据,ALU指向哪个执行就执行它

存储器的层次结构

存储器层次结构.png

越是往的成本越高,即塔尖的部分成本巨贵。至于CPU为什么设计成三层(L1,L2,L3)是由工业设计大量实践证明三层最合理

cpu到各层时间对比.png

寄存器与memor速度比=1:100

多核cpu.png

一颗物理cpu内部有两个核心,这两个核心各自独立的L1 & L2,共享L3
两颗物理cpu共享主内存。

乱序执行

https://preshing.com/20120515/memory-reordering-caught-in-the-act/

禁止乱序

CPU层面:Intel -> 原语(mfence lfence sfence) 或者锁总线
JVM层级:8个hanppens-before原则 4个内存屏障 (LL LS SL SS)
as-if-serial : 不管硬件什么顺序,单线程执行的结果不变,看上去像是serial

举例说明(乱序执行可能产生的问题)
DCL单利为什么加volatile?

对象创建的会编码.png

0 new - 这条指令开辟一块内存空间,并将m=0,并在本地方法栈内存放一个引用,指向给地址
3 dup - 将栈的引用复制一份
4 invokespecial #3 > - 从栈中弹出栈顶的那个引用指向那个对象执行构造方法,即 m = 8
7 astore_1 - 将弹出栈中最下面引用赋值给t

DCL-单利.png

发生了指令重排之后,t=null, 则此时Thread 2 恰巧进入if 判断分支成立,即使用了半初始化状态的对象
详看2020-设计模式->>单利模式->>懒汉模式(Mgr06)
https://www.jianshu.com/p/9baf3170bff7

合并写(了解)

Write Combining Buffer
一般是4个字节
由于ALU速度太快,所以在写入L1的同时,写入一个WC Buffer,满了之后再直接更新到L2


wc buffer.png

NUMA(了解)

Non Uniform Memory Access
ZGC - NUMA aware
分配内存会优先分配该线程所在CPU的最近内存

UMA.png

NUMA.png

从主板的结构来说,他会分为不同的插槽,每个插槽上有一组cpu和与之相邻的内存条,则cpu就近访问内存要比访问其他插槽的内存速度要快的多

————————————————————
坐标帝都,白天上班族,晚上是知识的分享者
如果读完觉得有收获的话,欢迎点赞加关注

你可能感兴趣的:(java编程-硬件基础知识(缓存行,指令重排…))