什么是缓存行对齐
高速缓存控制器是针对数据块,而不是字节进行操作的。从程序设计的角度讲,高速缓存其实就是一组称之为缓存行(cache line)的固定大小的数据块,其大小是以突发读或者突发写周期的大小
为基础的。
缓存基本上来说就是把后面的数据加载到离CPU自己进的地方,对于CPU来说,它是不会一个字节一个字节的加载的,因为这非常没有效率,一般来说都是要一块一块的加载的,在CPU的缓存技术中,这个术语叫“CacheLine”(有的中文编译成“缓存行”),一般来说,一个主流的CPU的CacheLine是64Bytes(也有的CPU用32Bytes和128Bytes),也就是16个32位的整型。也就是说,CPU从内存中捞数据上来的最小数据单位。
关于缓存行介绍详细可见:http://www.elecfans.com/d/1196867.html
验证CPU的缓存行对齐现象
由于一个缓存行是64字节,所以我们设计如下代码:
public class CacheLineTest {
private static class TClass {
private volatile long data = 0;
private volatile long a, b, c, d, e, f, g; // 测试此行加与不加时运行速度对比
}
private static volatile TClass[] tClasses = new TClass[2];
static {
tClasses[0] = new TClass();
tClasses[1] = new TClass();
}
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
for (long i = 0; i < 1000_0000L; i++) {
tClasses[0].data = i;
}
});
Thread thread2 = new Thread(() -> {
for (long i = 0; i < 1000_0000L; i++) {
tClasses[1].data = i;
}
});
final long start = System.nanoTime();
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println((System.nanoTime() - start) / 100_1000);
}
}
当上面代码内部类TClass不加上标记的那一行成员变量时,运行速度如下:
当加上时,运行速度如下:
原因分析
由于缓存行是64字节,而一个long类型为8字节,当不加那一行代码时,计算的总的字节数为8+8=16字节,所以这两个变量在一个缓存行中。
于是这个缓存行中就有数据1和数据2。
当两个线程分别执行时,由于加了volatile关键字,线程1改变了缓存行中的数据1,需要去通知线程2去更新缓存行中的数据1(即使数据1线程2不需要用到),因此就降低了CPU的执行速度。
但是上面代码如果加上
private volatile long a, b, c, d, e, f, g;
这时每个TClass有8个long类型的成员变量,即8 * 8 = 64个字节,每个TClass都单独在一个缓存行中。
因此两个线程分别执行时都不需要通知对方,CPU执行速度就会增加。