太牛逼了!自从项目中用了Disruptor之后,性能提升了2.5倍
共 4488字,需浏览 9分钟
·
2021-02-26 00:08
一、CPU Cache
现在的 CPU 都是由多个处理器,每个处理器由多个核心构成。一个处理器对应一个物理插槽,不同的处理器间通过 QPI 总线相连。一个处理器间的多核共享 L3 Cache。一个核包含寄存器、L1 Cache、L2 Cache,下图是Intel Sandy Bridge CPU架构:
二、缓存行与伪共享
现在假设多线程情况下,线程 A 的执行者 CPU Core-1 读取 arr[1],首先查询缓存,缓存没有命中,缓存就会去内存中加载。从内存中读取 arr[1] 起的连续的 64 个字节地址到缓存中,组成缓存行。由于从arr[1] 起,arr 的长度不足够 64 个字节,只够 56 个字节。假设最后 8 个字节内存地址上存储的是对象 bar,那么对象 bar 也会被一起加载到缓存行中。
三、Disruptor 缓存行填充
public class Sequence extends RhsPadding {
private static final long VALUE_OFFSET;
static {
VALUE_OFFSET = UNSAFE.objectFieldOffset(Value.class.getDeclaredField("value"));
...
}
...
}
class RhsPadding extends Value {
protected long p9, p10, p11, p12, p13, p14, p15;
}
class Value extends LhsPadding {
protected volatile long value;
}
class LhsPadding {
protected long p1, p2, p3, p4, p5, p6, p7;
}
如下图所示,其中 V 就是 Value 类的 value,P 为 value 前后填充的无意义 long 型变量,U 为其它无关的变量。不论什么情况下,都能保证 V 不和其他无关的变量处于同一缓存行中,这样 V 就不会被其他无关的变量所影响。
此处以 Disruptor 的 RingBuffer 为例,最左边的 7 个 long 型变量被定义在顶级父类 RingBufferPad 中,最右边的 7 个 long 型变量被定义在 RingBuffer 的最后一行变量定义中,这样所有的需要独占的变量都被左右 long 型给包围,确保会独占缓存行。
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;
...
}
abstract class RingBufferFields<E> extends RingBufferPad
{
...
}
abstract class RingBufferPad {
protected long p1, p2, p3, p4, p5, p6, p7;
}
四、@Contended
// 类前加上代表整个类的每个变量都会在单独的cache line中
@sun.misc.Contended
public class ContendedData {
int value;
long modifyTime;
boolean flag;
long createTime;
char key;
}
// 同一 groupName 在同一缓存行
public class ContendedGroupData {
@sun.misc.Contended("group1")
int value;
@sun.misc.Contended("group1")
long modifyTime;
@sun.misc.Contended("group2")
boolean flag;
@sun.misc.Contended("group3")
long createTime;
@sun.misc.Contended("group3")
char key;
}
public class Thread implements Runnable {
...
// The following three initially uninitialized fields are exclusively
// managed by class java.util.concurrent.ThreadLocalRandom. These
// fields are used to build the high-performance PRNGs in the
// concurrent code, and we can not risk accidental false sharing.
// Hence, the fields are isolated with @Contended.
/** The current seed for a ThreadLocalRandom */
@sun.misc.Contended("tlr")
long threadLocalRandomSeed;
/** Probe hash value; nonzero if threadLocalRandomSeed initialized */
@sun.misc.Contended("tlr")
int threadLocalRandomProbe;
/** Secondary seed isolated from public ThreadLocalRandom sequence */
@sun.misc.Contended("tlr")
int threadLocalRandomSecondarySeed;
...
}
五、速度测试
来源:https://jitwxs.cn/13836b16.html
版权申明:内容来源网络,版权归原创者所有。除非无法确认,我们都会标明作者及出处,如有侵权烦请告知,我们会立即删除并表示歉意。谢谢!