缓存行是什么,伪共享是什么?

1 个回答

  • 柳馨儿
    生活给你苦难,其实是在铺垫浪漫。
    ### 一、缓存行(Cache Line) 缓存行(Cache Line)是CPU缓存(Cache)中数据存储的最小单位,通常为 **64字节**。当CPU从主存中读取数据时,会以缓存行为单位加载数据,而不是单独加载某个变量或某几个字节。 **缓存行的特点:** - **空间局部性**:CPU读取数据时,会顺带将周围的数据一起加载进缓存,以提高后续访问效率。 - **一致性协议**:缓存行是缓存一致性协议(如MESI协议)进行数据同步的基本单位。 - **固定大小**:主流CPU架构通常缓存行大小是64字节。 ### 二、伪共享(False Sharing) **伪共享**是指多个线程在各自处理不同变量时,这些变量正好位于**同一个缓存行**中,导致线程频繁争夺同一缓存行的控制权,使缓存行不断在各CPU核心间传递,严重影响程序性能。 伪共享的本质并非真正的共享数据,而是由于CPU缓存一致性协议引发的缓存行竞争。 ### 三、伪共享产生原因与过程 举个例子: 假设缓存行大小为64字节,有两个线程分别访问两个变量: ```java public class Counter { volatile long x = 0; volatile long y = 0; } ``` - 虽然线程A只访问 `x`,线程B只访问 `y`,看似没有共享数据; - 但由于 `x` 和 `y` 紧密相邻,很可能被放在同一个64字节的缓存行中; - 当线程A修改 `x` 时,缓存一致性协议会使线程B的缓存行失效,同理线程B修改 `y` 时也会导致线程A缓存失效; - 最终造成大量缓存行的无效、更新、传递,引发性能损耗。 ### 四、如何避免伪共享? 1. **填充缓存行(Padding):** 在变量之间添加额外的字节,使变量分布在不同缓存行中。 ```java public class CounterPadded { volatile long x = 0; long p1, p2, p3, p4, p5, p6, p7; // padding volatile long y = 0; } ``` 2. **使用@Contended注解(JDK8及以上):** ```java @jdk.internal.vm.annotation.Contended public class CounterContended { volatile long x = 0; volatile long y = 0; } ``` 使用时需添加JVM参数: ``` -XX:-RestrictContended ``` 3. **合理设计数据结构:** 尽量避免多线程频繁访问的数据被放入同一个结构体或类实例中,减少缓存行共享可能性。 ### 五、伪共享实际影响与性能表现 - 伪共享导致的性能损耗可以非常严重,有时可能使程序性能降低数倍; - 并发越高,线程数越多,伪共享的性能损耗越明显; - 大量时间耗费在缓存一致性协议上的缓存行同步操作。 ### 六、典型应用场景及解决方案示例 #### 典型场景: - 多线程计数器(如业务计数、访问次数统计); - 并发队列、并发数组访问。 #### 实际优化案例: 假设有一个高性能计数器: ```java public class PaddedAtomicLong { private volatile long value = 0; // Padding fields, each long is 8 bytes, 7 fields * 8 bytes = 56 bytes padding public long p1, p2, p3, p4, p5, p6, p7; public void increment() { value++; } public long get() { return value; } } ``` - 添加padding后,每个计数器占据独立缓存行,避免伪共享。 ### 总结: - **缓存行** 是CPU缓存操作的最小单位(通常为64字节)。 - **伪共享** 指变量未真正共享,但位于同一缓存行中,导致性能损耗。 - 通过缓存行填充(padding)和合理设计数据结构可有效避免伪共享,提高并发程序的性能。
    发布于 2025-03-20 17:14 •IP属地 山东
    举报
    评论
    收藏