博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
CPU 缓存行(Cache Line)影响性能效果演示
阅读量:2492 次
发布时间:2019-05-11

本文共 3512 字,大约阅读时间需要 11 分钟。

CPU的多级缓存

CPU缓存通常分为大小不等的三级缓存

来自百度百科对三级缓存分类的介绍:

  • 一级缓存都内置在CPU内部并与CPU同速运行,可以有效的提高CPU的运行效率。一级缓存越大,CPU的运行效率越高,但受到CPU内部结构的限制,一级缓存的容量都很小。

  • 二级缓存,它是为了协调一级缓存和内存之间的速度。cpu调用缓存首先是一级缓存,当处理器的速度逐渐提升,会导致一级缓存就供不应求,这样就得提升到二级缓存了。二级缓存它比一级缓存的速度相对来说会慢,但是它比一级缓存的空间容量要大。主要就是做一级缓存和内存之间数据临时交换的地方用。

  • 三级缓存是为读取二级缓存后未命中的数据设计的—种缓存,在拥有三级缓存的CPU中,只有约5%的数据需要从内存中调用,这进一步提高了CPU的效率。其运作原理在于使用较快速的储存装置保留一份从慢速储存装置中所读取数据并进行拷贝,当有需要再从较慢的储存体中读写数据时,缓存(cache)能够使得读写的动作先在快速的装置上完成,如此会使系统的响应较为快速。

效果演示

1、按行写入

public class CacheLine {
public static void main(String[] args) {
int[][] arr = new int[10000][10000]; long s = System.currentTimeMillis(); for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
//按行写入 arr[i][j] = 0; } } long e = System.currentTimeMillis(); System.out.println(e-s); }}

2、按列写入

public class CacheLine {
public static void main(String[] args) {
int[][] arr = new int[10000][10000]; long s = System.currentTimeMillis(); for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
//调整顺序,按列写入 arr[j][i] = 0; } } long e = System.currentTimeMillis(); System.out.println(e-s); }}

上面两个案例都能完成二维数组的遍历写入,但是按行写入的效率要高于按列写入,读者可以自行测试,这就是CPU缓存行带来的影响。

原因分析

CPU的缓存是由多个缓存行组成的,以缓存行为基本单位,一个缓存行的大小一般为64字节,二维数组在内存中保存时,实际上是以按行遍历的方式进行保存,比如:

arr[0][0],arr[0][1],arr[1][0],arr[1][1],arr[2][0],arr[2][1]...

所以当按行访问时,是按照内存存储的顺序进行访问,那么CPU缓存后面的元素就可以利用到,而如果是按列访问,那么CPU的缓存是没有用的。

缓存行对齐

public class CacheLinePadding {
private static class Padding {
//一个long是8个字节,一共7个long //public volatile long p1, p2, p3, p4, p5, p6, p7; } private static class T extends Padding {
//x变量8个字节,加上Padding中的变量,刚好64个字节,独占一个缓存行。 public volatile long x = 0L; } public static T[] arr = new T[2]; static {
arr[0] = new T(); arr[1] = new T(); } public static void main(String[] args) throws Exception {
Thread t1 = new Thread(() -> {
for (long i = 0; i < 10000000; i++) {
arr[0].x = i; } }); Thread t2 = new Thread(() -> {
for (long i = 0; i < 10000000; i++) {
arr[1].x = i; } }); final long start = System.nanoTime(); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println((System.nanoTime() - start) / 100000); }}

上述代码,可以自行测试,当有如下变量时,执行的速度会比没有时更快。

public volatile long p1, p2, p3, p4, p5, p6, p7;

JDK8提供的解决方式

JDK8版本之后,提供了解决伪共享的注解,需要在启动时添加如下配置-XX:-RestrictContended,然后使用sun.misc.Contended注解即可。

public class CacheLinePadding {
@Contended private static class T {
public volatile long x = 0L; } public static T[] arr = new T[2]; static {
arr[0] = new T(); arr[1] = new T(); } public static void main(String[] args) throws Exception {
Thread t1 = new Thread(() -> {
for (long i = 0; i < 10000000; i++) {
arr[0].x = i; } }); Thread t2 = new Thread(() -> {
for (long i = 0; i < 10000000; i++) {
arr[1].x = i; } }); final long start = System.nanoTime(); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println((System.nanoTime() - start) / 100000); }}

转载地址:http://belrb.baihongyu.com/

你可能感兴趣的文章
Linux(SUSE 12)安装jboss4并实现远程访问
查看>>
Neutron在给虚拟机分配网络时,底层是如何实现的?
查看>>
netfilter/iptables全攻略
查看>>
Overlay之VXLAN架构
查看>>
Eclipse : An error occurred while filtering resources(Maven错误提示)
查看>>
在eclipse上用tomcat部署项目404解决方案
查看>>
web.xml 配置中classpath: 与classpath*:的区别
查看>>
suse如何修改ssh端口为2222?
查看>>
详细理解“>/dev/null 2>&1”
查看>>
suse如何创建定时任务?
查看>>
suse搭建ftp服务器方法
查看>>
centos虚拟机设置共享文件夹并通过我的电脑访问[增加smbd端口修改]
查看>>
文件拷贝(IFileOperation::CopyItem)
查看>>
MapReduce的 Speculative Execution机制
查看>>
大数据学习之路------借助HDP SANDBOX开始学习
查看>>
Hadoop基础学习:基于Hortonworks HDP
查看>>
为什么linux安装程序 都要放到/usr/local目录下
查看>>
Hive安装前扫盲之Derby和Metastore
查看>>
永久修改PATH环境变量的几种办法
查看>>
大数据学习之HDP SANDBOX开始学习
查看>>