false sharing

false sharing我们一般说的是多核的问题,这个问题主要出现在CPU的缓存上,我们知道CPU有多级缓存,而CPU缓存单单位是行(主流一个缓存行是64Byte, 也就是8个int64) CPU加载缓存 当我们要操作一个变量A时会把A附近的64个字节都加载到缓存行中(空间局部性原理),这样在CPU的缓存里操作时要比在内存中要快 多核CPU缓存问题 在多核心中每个CPU核心都有自己的缓存,如果A和B变量是挨着的, 当CPU1要写A变量, CPU2读变量B, 因为AB挨着,CPU1和CPU2都把它们加载到自己的缓存中,并且AB在同一个缓存行中,CPU1改了A导致了CPU2中B缓存失效,CPUB就得重新从内存中加载缓存 这种情况就是"假共享"/“伪共享”/“false sharing” 说白了就是,CPU各自都有一份,因为邻近变量修改导致了其他核心缓存失效 例子 CASE1 type FS struct { X int64 Y int64 } func share() { var a FS wg := sync.WaitGroup{} wg.Add(2) start := time.Now() go func() { for i := 0; i < 100000000; i++ { a.X++ } wg.Done() }() go func() { for i := 0; i < 100000000; i++ { a.Y++ } wg.Done() }() wg.Wait() fmt.Println(time.Since(start)) } 这种就创造了两个变量挨着在同一缓存行的情况,这个代码在我本机运行时间为305.462012ms CASE 2 我们把上面结构体改一下,X和Y间隔56个字节, 使他们不在一个缓存行, 其他部分不变 type FS struct { X int64 _ [7]int64 Y int64 } 本地运行时间变为201.209402ms, 节省了100ms 注意区分false sharing和data race false sharing讲的是多个核心数据共享缓存失效的问题, 只是影响程序性能,并不影响程序正确性(解决这个问题可以使用内存填充,就像CASE2那样) data race讲的是并发访问同一个变量问题, 会导致意外的情况, 程序不能正确执行(没有安全的数据竞争,发生数据竞争要使用同步原语解决)

<span title='2022-06-22 15:04:38 +0800 +0800'>六月 22, 2022</span>