内存局部性原理-利用空间局部性优化程序

29 篇文章 0 订阅
3 篇文章 0 订阅

学习来自马士兵

什么是内存局部性原理?

见https://blog.csdn.net/xindoo/article/details/97525694 可以看下内存局部性这块的解释

先上代码

package com.example.mashibing;

import java.util.concurrent.CountDownLatch;

public class Demo1 {

    static CountDownLatch countDownLatch = new CountDownLatch(2);

    // 内存空间局部性原理 优化
    public static class T {
//        public long p1611 = 1;
//        public long p161 = 1;
//        public long p151 = 1;
//        public long p141 = 1;
//        public long p131 = 1;
//        public long p121 = 1;
//        public long p111 = 1;
        public volatile long p1 = 1;
    }

    public static void main(String[] args) throws InterruptedException {
        long l = System.currentTimeMillis();
        T t1 = new T();
        T t3 = new T();
        Thread t = new Thread(() -> {
            for (int i = 0; i < 1_0000_0000; i++) {
                t1.p1 ++;
            }
            countDownLatch.countDown();
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1_0000_0000; i++) {
                t3.p1 ++;
            }
            countDownLatch.countDown();
        });
        t.start();
        t2.start();
        countDownLatch.await();

        long l1 = System.currentTimeMillis();

        System.out.println(l1 - l);
    }
}


下面列举5次运行时间:

  1. 3450
  2. 3886
  3. 3969
  4. 3446
  5. 3789

将上面代码注释打开

package com.example.mashibing;

import java.util.concurrent.CountDownLatch;

public class Demo1 {

    static CountDownLatch countDownLatch = new CountDownLatch(2);

    // 内存空间局部性原理 优化
    public static class T {
        public long p1611 = 1;
        public long p161 = 1;
        public long p151 = 1;
        public long p141 = 1;
        public long p131 = 1;
        public long p121 = 1;
        public long p111 = 1;
        public volatile long p1 = 1;
    }

    public static void main(String[] args) throws InterruptedException {
        long l = System.currentTimeMillis();
        T t1 = new T();
        T t3 = new T();
        Thread t = new Thread(() -> {
            for (int i = 0; i < 1_0000_0000; i++) {
                t1.p1 ++;
            }
            countDownLatch.countDown();
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1_0000_0000; i++) {
                t3.p1 ++;
            }
            countDownLatch.countDown();
        });
        t.start();
        t2.start();
        countDownLatch.await();

        long l1 = System.currentTimeMillis();

        System.out.println(l1 - l);
    }

}


下面列举5次运行时间:

  1. 760
  2. 761
  3. 781
  4. 786
  5. 766

why

我们知道程序所需的数据运行时需要加载到内存中去,如下图,程序中的一个字段i为int类型,(这里以Java为例)我们知道一个int类型占4个字节,我们程序需要去对i进行一个加一操作时会做什么那?(这里我们说单线程情况)


  1. 从主存load i到L3
  2. 从L3load i到L2
  3. 从L2 load i到L1
  4. L1 iload到寄存器
  5. 运算器对i进行加一操作后写回到L1 L2 L3 主存
  6. 主存i =1

在这里插入图片描述

过于上述过程提2个问题


  1. i占4个字节,从主存读取i时只读4个字节吗?
  2. 多线程访问i时,如何解决并发(次要)

关于第一个问题 答案必然是否,那他一次读取多少个字节那?

看下这块https://lrita.github.io/2018/06/30/programmer-should-know-about-memory-1/#MathJax-Element-1-Frame 有兴趣的小伙伴可以看下全篇 很赞!不跑题,我们可以这里记一下一般是一次读取64个字节

第二个问题,这个不同的语言都有自己的解决方案,如这里我使用的Java的,我通过volatile来保障i字段具有内存可见性,不过这里由于i++不是原子操作,不能保障每次的修改都能同步到主存,不过在1E次的修改中必然是会有同步的操作的

现在来解答为什么最初的两个程序为什么效率相差很大


  1. 两个线程同时new一个对象时,他们均需要在主存申请内存空间,根据内存行读取一次大小为64字节和内存空间局部性原理,我们可以推测如果这个对象占用大小小于64字节,就会存在这样的情况,如下图,
  2. 即T1和T2的T对象申请内存时挨得很近由于自身占用内存过小,加到一块都不到64字节,这样程序运行时就会发生T1和T2进行i运算时读取的是同一块64byte的内存行到L3 L2 L1 通用寄存区又由于我加了volatile关键字,保障了内存可见性,导致当i被修改后会通知到数据总线导致该数据会被写回到主存,另一个线程同这种情况
  3. 这样子假设当第一次主存的i被T1修改后写会到主存,T2线程就会感知到同时从主存去拉取T1线程修改过的数据,而当T2同步后他要去修改数据并修改成功后也会同步到主存同时通知到T1,这样子就多了很多T1、T2与主存交互的操作,因此第一次的代码非常耗时
  4. 第二次代码加了7个long类型的字段,我们知道long类型占8个字节,加上那个volatile的字段,一共就有64个字节,这样子一个对象申请的内存空间一定是大于64字节的,就能够避免两个线程的T对象申请的内存会作为一个64字节的内存行被CPU去读取了,因为每一个T对象自身已经大于了64字节

在这里插入图片描述

感谢马士兵老师的公开课指导,同时萌新创作,也跪求大佬看完一个赞,嘤嘤嘤

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值