伪共享问题

原理

程序在执行时呈现出局部性规律,即在一段时间内,整个程序的执行仅限于程序中的某一部分。执行所访问的存储空间也局限于某个内存区域。这就是程序的局部,因为这一规律,所以在设计计算机系统时会使用该原理来优化程序执行。

同时,在设计整个计算机系统时,为了解决磁盘-内存、内存-寄存器速度不匹配的问题,加入了缓存的概念,今天要分享的问题出现在内存-寄存器之间缓存上面。在传统的64位CPU中,存在一个缓存行的概念,即程序会将连续的64字节内容load到CPU高速缓存中,提升CPU执行数据处理的速度。

但是,现在主流的CPU都是多核心(超核)的处理器,每一个核心可以同时执行一个线程,那么计算机在同一时刻就可以执行多条线程(中的指令),由于程序的局部性原理,当多个线程在执行时需要处理物理位置相近的数据时,就会同时将位于同一个缓存行的数据load到缓冲中,多条线程处理了数据时会发现缓存失效的问题,重新从内存中load数据,造成多个线程相互影响,形成伪共享。

Java 代码

下面给出Java代码重现伪共享问题的实例:

package com.zjw.cache_line;

/**
 * @author zjwblog <cn.zjwblog@gmail.com>
 * @version 1.0
 */
public class MainTest {

  private static final long COUNT = 1000_0000;

  private static class Super {
    // 进行缓存行填充,让程序load的数据在不同的缓存中
    public volatile long p1, p2, p3, p4, p5, p6, p7;
  }

  private static class Test1 {

    public volatile long x = 0;
  }

  private static class Test2 extends Super {

    public volatile long x = 0;
  }

  public static Test1[] arr1 = new Test1[2];
  public static Test2[] arr2 = new Test2[2];

  static {
    arr1[0] = new Test1();
    arr1[1] = new Test1();
    arr2[0] = new Test2();
    arr2[1] = new Test2();
  }

  public static void main(String[] args) throws Exception {
    for (int i = 0; i < 100; i++) {
      test1();
      test2();
    }
  }

  public static final void test1() throws Exception {
    Thread t1 = new Thread(() -> {
      for (int i = 0; i < COUNT; i++) {
        arr1[0].x++;
      }
    });

    Thread t2 = new Thread(() -> {
      for (int i = 0; i < COUNT; i++) {
        arr1[1].x++;
      }
    });
    final long startTime = System.nanoTime();
    t1.start();
    t2.start();
    t1.join();
    t2.join();
    final long stopTime = System.nanoTime();
    System.out.println("Example 1 run [" + ((stopTime - startTime) / 1000 / 1000) + "] ms");
  }

  public static final void test2() throws Exception {
    Thread t1 = new Thread(() -> {
      for (int i = 0; i < 1000_0000; i++) {
        arr2[0].x++;
      }
    });

    Thread t2 = new Thread(() -> {
      for (int i = 0; i < 1000_0000; i++) {
        arr2[1].x++;
      }
    });
    final long startTime = System.nanoTime();
    t1.start();
    t2.start();
    t1.join();
    t2.join();
    final long stopTime = System.nanoTime();
    System.out.println("Example 2 run [" + ((stopTime - startTime) / 1000 / 1000) + "] ms");
  }
}

执行结果:

Example 1 run [342] ms
Example 2 run [79] ms
Example 1 run [333] ms
Example 2 run [79] ms
Example 1 run [335] ms
Example 2 run [77] ms
Example 1 run [342] ms
Example 2 run [74] ms
...

Disruptor使用到了该理论

Disruptor是英国外汇交易公司LMAX开发的一个高性能队列,里面有一个环形队列,使用到了该理论:

abstract class RingBufferPad {
    protected long p1, p2, p3, p4, p5, p6, p7;
}

以及RingBuffer内部定义了缓存对齐的代码

public final class RingBuffer<E> extends RingBufferFields<E> implements Cursored, EventSequencer<E>, EventSink<E> {
    public static final long INITIAL_CURSOR_VALUE = Sequence.INITIAL_VALUE;
    protected long p1, p2, p3, p4, p5, p6, p7;

    /**
     * Construct a RingBuffer with the full option set.
     *
     * @param eventFactory to newInstance entries for filling the RingBuffer
     * @param sequencer    sequencer to handle the ordering of events moving through the RingBuffer.
     * @throws IllegalArgumentException if bufferSize is less than 1 or not a power of 2
     */
    RingBuffer(
        EventFactory<E> eventFactory,
        Sequencer sequencer)
    {
        super(eventFactory, sequencer);
    }
    // ... 一些额外的方法
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值