ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Cache写策略(Cache一致性问题与骚操作)

2020-07-04 16:35:28  阅读:297  来源: 互联网

标签:主存 策略 Cache long static 一致性 new public


写命中

写直达(Write Through)

信息会被同时写到cache的块和主存中。这样做虽然比较慢,但缺少代价小,不需要把整个块都写回主存。也不会发生一致性问题。

对于写直达,多出来%10向主存写入的存储指令使得其比其单纯向Cache写入的速度慢上将近10倍。这种速度不一致的问题,不管是在硬件结构还是软件,有着一条“不管怎么样,先试试这样行不行”的办法:并行加缓冲。

Write Buffer

我们使用写缓冲(Write Buffer)来解决这个问题,CPU写入Cache的同时会写入Write Buffer。缓冲中的内容什么时候写入主存交给存控(Memory Controller)来控制,CPU将省下的时间去处理其他事情。

Write Buffer是一个FIFO队列,一般只有4字节。

然而当写操作频繁的时候,这点容量就不够用了,容易饱和发生阻塞,就相当于没加一样...

解决的办法可以是再加一级Cache,变成二级缓存。什么,你问为什么不把两级Cache和一起,搞那么多干什么?这个二级当然是慢一点的便宜货,咱们弄这些东西,不就是因为越快东西越贵,买不起嘛土豪!

对于缓冲的问题,还有个关于合并写对程序效率的影响, 具体可以参考这篇博文

第二种办法就是改变策略使用写回,也就是下面介绍的方法。

写回(Write Back)

信息仅仅写到Cache中的块。当其被替换时,信息才会被写回到主存中。虚拟存储器系统通常采用写回策略,因为写到磁盘的延迟代价太大。

写回的速度要更快一些,因为不必每次写操作都访问主存。但这样我们如何保证一致性问题呢?我们可以给每行添加一个脏位(dirty bit),这样我们替换这块Cache时就可以根据脏位来判断是否需要写回主存。如果没有被“弄脏过”,那么就不需要写回主存。

不过对于同一块Cache中的变量X,他不是太喜欢这个机制。因为它的邻居Y老是被修改,导致X这个只被读取的变量老得往内存跑,它不想跟Y待在一起了,太累人了。

聆听了X的心声,我们有什么办法可以帮助它吗?办法当然是有的,让Y这个烦人的家伙单独待着就行。下面分别运行两个程序,排除首次装入的影响(其实写一块也行,对齐的技巧源自Disruptor)

public class Padding {

    private static class X {
        public long p1,p2,p3,p4,p5,p6,p7;//cache padding
        public volatile long x=0L;
        public long p9,p10,p11,p12,p13,p14,p15;
        //8*8刚好占满Cache一行,p9...p15只是为了确保x单独在一行中,不与其他频繁修改的变量在一起
    }
    public static X[] arrX=new X[2];
    static {
        arrX[0]=new X();
        arrX[1]=new X();
    }
    public static void main(String[] args) throws InterruptedException {
        Thread thread1=new Thread(()->{
            for(long i=0;i<100_000_000L;i++)
                arrX[0].x=i;
        });

        Thread thread2=new Thread(()->{
            for(long i=0;i<100_000_000L;i++)
                arrX[0].x=i;
        });
        final long start=System.nanoTime();
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println((System.nanoTime()-start)/1_000_000);
    }
}
package mytask;

public class NoPadding {
    private static class X {
        public volatile long x=0L;
    }

    public static X[] arrX=new X[2];
    static {
        arrX[0]=new X();
        arrX[1]=new X();
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread1=new Thread(()->{
            for(long i=0;i<100_000_000L;i++)
                arrX[0].x=i;
        });

        Thread thread2=new Thread(()->{
            for(long i=0;i<100_000_000L;i++)
                arrX[0].x=i;
        });
        final long start=System.nanoTime();
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println((System.nanoTime()-start)/1_000_000);
    }
}

写不命中

对于写不命中,有两者方法:写分配与非写分配。前者利用空间局部性,每次都从主存中读取一个块装入Cache更新相应单元。后者则是直接写主存单元,不将主存块装入Cache。

标签:主存,策略,Cache,long,static,一致性,new,public
来源: https://www.cnblogs.com/AD-milk/p/13235380.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有