jvm缓存行对齐

1、cpu缓存结构

  cpu内核的缓存一般分为一级缓存和二级缓存,三级缓存。cpu运行时,先从一级缓存读取数据,如果读取失败,则从二级缓存读取,读取失败,再从三级缓存和内存中读取数据。如下图
  cpu读取缓存时数据时,以缓存行形式读取数据,从内存进入到三级缓存,二级缓存和一级缓存,最终有寄存器再进行处理。缓存行大小基于cpu架构,目前常见的为intel的x64架构,缓存行大小为64字节。

2、伪共享问题

  由于缓存行这种特性存在,当多线程修改相互独立的数据时,如果这些数据同在一个缓存行,那么当数据在其中一个core中修改时,需要通知另外的数据进行刷新,这将影响了线程彼此间的性能,这就是所谓的伪共享。以64字节来算,在java中,long类型长度为8字节,因此一个缓存行支持8个long类型变量。当你声明长度为8的long类型变量时,访问其中一个,其他7个也会同时加载到缓存中,因此可以更快访问其他7个变量。
  cpu读取数据时,通过内存数据到L3,L2,L1,再对数据进行处理,例如声明了volatile的数据(非必须),当数据修改时由于会进行内存的回写,如果其他处理器的缓存行也刚好带了这个变量,这是会通过总线进行通知,刷新其他处理器的缓存数据,确保变量数据的一致性。这也造成了cpu的等待和总线的加锁,从而导致性能问题。
如图,core1和core2都会进行数据A和数据B的处理,因此会从内存一直加载到L1,当core1对A进行修改时,会回写到内存,core1通过总线通知core2,core2需重新从内存读取数据;反之也是如此。

3、缓存行对齐

  看下面的代码:

package com.chenys.test;
import java.util.concurrent.CountDownLatch;
/**
 * 缓存行未对齐
 */
public class CachelineNopadding {
    public static final long RUN_COUNT = 300000000L;
    private static class Data {
        // long 8字节
        private volatile long a = 0L;
    }
    public static Data[] data = new Data[2];
    static {
        data[0] = new Data();
        data[1] = new Data();
    }
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(2);
        Thread thread1 = new Thread(() -> {
            for (long i = 0; i < RUN_COUNT; i++) {
                data[0].a = i;
            }
            latch.countDown();
        });
        Thread thread2 = new Thread(() -> {
            for (long i = 0; i < RUN_COUNT; i++) {
                data[1].a = i;
            }
            latch.countDown();
        });
        long startTime = System.nanoTime();
        thread1.start();
        thread2.start();
        latch.await();
        System.out.println((System.nanoTime()-startTime)/1000000);
    }
}

  其运行耗时如下:

总耗时: 13815

  如果改造成缓存行对齐,如下:

package com.chenys.test;
import java.util.concurrent.CountDownLatch;
/**
 * 缓存行对齐
 */
public class Cachelinepadding {
    public static final long RUN_COUNT = 300000000L;
    private static class Data {
        private volatile long f1,f2,f3,f4,f5,f6,f7;
        // long 8字节
        private volatile long a = 0L;
        private volatile long f9,f10,f11,f12,f13,f14,f15;
    }
    public static Data[] data = new Data[2];
    static {
        data[0] = new Data();
        data[1] = new Data();
    }
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(2);
        Thread thread1 = new Thread(() -> {
            for (long i = 0; i < RUN_COUNT; i++) {
                data[0].a = i;
            }
            latch.countDown();
        });
        Thread thread2 = new Thread(() -> {
            for (long i = 0; i < RUN_COUNT; i++) {
                data[1].a = i;
            }
            latch.countDown();
        });
        long startTime = System.nanoTime();
        thread1.start();
        thread2.start();
        latch.await();
        System.out.println((System.nanoTime()-startTime)/1000000);
    }
}

  其总耗时如下:

总耗时: 3943

  从上面所示,缓存行对齐后,总耗时也大大减少。如果前后多少位取8个变量,其都是是的变量a都在同一个缓存行。

4、Disruptor框架

  Disruptor框架中,也使用了该缓存行。Disruptor是一个高性能环形队列框架。

abstract class RingBufferPad
{
    protected byte
        p10, p11, p12, p13, p14, p15, p16, p17,
        p20, p21, p22, p23, p24, p25, p26, p27,
        p30, p31, p32, p33, p34, p35, p36, p37,
        p40, p41, p42, p43, p44, p45, p46, p47,
        p50, p51, p52, p53, p54, p55, p56, p57,
        p60, p61, p62, p63, p64, p65, p66, p67,
        p70, p71, p72, p73, p74, p75, p76, p77;
}
/**
 * Ring based store of reusable entries containing the data representing
 * an event being exchanged between event producer and {@link EventProcessor}s.
 *
 * @param <E> implementation storing the data for sharing during exchange or parallel coordination of an event.
 */
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 byte
        p10, p11, p12, p13, p14, p15, p16, p17,
        p20, p21, p22, p23, p24, p25, p26, p27,
        p30, p31, p32, p33, p34, p35, p36, p37,
        p40, p41, p42, p43, p44, p45, p46, p47,
        p50, p51, p52, p53, p54, p55, p56, p57,
        p60, p61, p62, p63, p64, p65, p66, p67,
        p70, p71, p72, p73, p74, p75, p76, p77;
关注公众号

关注公众号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值