编程语言
首页 > 编程语言> > 多线程之程序的局部性原理和伪共享问题

多线程之程序的局部性原理和伪共享问题

作者:互联网

程序的局部性原理

存储结构

在说局部性原理的时候,先来了解一下计算机的存储结构,设计到计算机组成结构的一些知识,计算机的存储结构,主要分以下几层,其中的磁盘跟本文无关。就暂且不提

局部性原理

通过上面的简单介绍,可以知道,在内存和cpu之间有一道cache,cpu取值是先看cache中有没有,如果有就从cache中拿,没有再访问主存。并且cache读取数据是并不是一个一个数据来读的,而是将目标数据所在的内存区域的一个缓存行大小一起读,作为一个缓存行,然后复制存进缓存的。总结一下就是:缓存行是主存和cache数据交换的单位。为什么要这么设计呢。程序的局部性原理认为:

CPU访问存储器时,无论是存取指令还是存取数据,所访问的存储单元都趋于聚集在一个较小的连续区域中

时间局部性:程序有在一段时间内多次访问同一个数据块的倾向

空间局部性:程序往往有访问一个聚集空间的数据块的倾向

通俗来说呢就是,你对一个数据进行访问的时候,cpu认为,这个数据附近的数据部分也有非常有可能被访问,于是缓存就会直接把这个数据附近的数据也一起放到缓存中,节省cpu再到内存找的性能开销。举个例子:数组。数组的存储位置是连续的,但是我们一般在编程的时候,很少会单独的去访问数组的单个元素,就像我们平时写代码一样,数组都是用for循环去遍历,如果不把数组的的元素连续区域放进cache中,那cpu每循环一次,都要去内存中取值,效率较低。但是如果根据局部性原理,把这个数组的这块某块区域的大小都放进cache中,那cpu每次遍历只需要直接从高速缓存中取值。效率就会大大的提高。例如像这样

long a,b,c,d;

这个时候当cpu访问变量a的时候,如果a在缓存中没有,并且缓存行的大小为32字节,那么这个时候,abcd都会作为一个缓存行存进缓存中。因为cpu认为,bcd变量在后续的指令执行中非常有可能用到。

局部性原理并不是定理,而是一种经验的规律的总结,是计算机科学家在研究程序的运行过程发现的规律的总结,因此提出的提高效率的一种解决办法,目前很多的cpu都采用了这种方式。

伪共享

在多线程的环境中,伪共享的问题就是由于上面所说的程序的局部性原理导致的。从上面知道,cpu和主存之间隔着一个缓存,而缓存存值又是以缓存行作为单位的,cpu对值的修改也是以缓存行为单位的。由于程序的局部性原理,cpu从缓存中读取数据都是按缓存行来读的,一个缓存行可能是32个字节,也可能是64个字节等等,不同的cpu设计会有不同的大小。假设有x变量和y变量是在同一行被cpu读到,但是此时同时有两个线程,线程1对x进行操作,线程2对y进行操作。虽然他们读取的变量都不一样,由于这两个变量是在同一行。那么如果此时线程1对x的变量值进行了修改,由于缓存一致性协议,就要通知线程2 x的值被改了需要重新到主存中读取,这样就不是真正的共享。而这样反复的读取主存,就会无意中影响彼此的性能, 这就是线程间的伪共享问题。

如何解决伪共享问题

解决伪共享问题有一个很好的思路就是缓存对齐或者说缓存填充,例如有这样的一段代码,假设缓存行大小为64个字节。

 volatile long  value  = 0;
long p1,p2,p3,p4,p5,p6,p7;
long value2 = 2

只需要像这样,value和其他7个变量就会被放到同一个缓存行,value2又会被放到另一个缓存行中么,这样的话,对这个缓存行中的数据value和Value2读写就不会产生伪共享问题,因为他们是在不同的缓存行,这是一种用空间换时间的方式

在jdk1.8之后有一个注解,@sun.misc.centened.将这个注解放在变量上,会自动填充缓存行。但是这个注解要对jvm参数进行一些配置才会生效

标签:缓存,cache,线程,内存,共享,多线程,局部性,cpu
来源: https://www.cnblogs.com/blackmlik/p/12872458.html