关于缓存:处理器缓存影响-翻译

58次阅读

共计 1320 个字符,预计需要花费 4 分钟才能阅读完成。

翻译自: http://igoro.com/archive/gall…

原文代码是 C# 的,我改成了 golang,原文有些不主观,我加了 sleep 进展一会,把缓存置出

我的电脑是 Mac Pro2018,

例 1. 内存拜访性能

func TestMem(t *testing.T) {var arr = []int{128 * 1024 * 1024}

    time.Sleep(time.Second)

    // Loop 1
    tt := time.Now()
    for i := 0; i < len(arr); i++ {arr[i] *= 3
    }
    fmt.Println("Loop 1", time.Now().Sub(tt))
    time.Sleep(time.Second)

    // Loop 2
    tt = time.Now()
    for i := 0; i < len(arr); i += 16 {arr[i] *= 3
    }
    fmt.Println("Loop 2", time.Now().Sub(tt))
}

运行后果

Loop 1 417ns
Loop 2 523ns
很神奇,第一次运行的工夫反而更少 

循环破费雷同工夫的起因与内存无关。这些循环的运行工夫取决于对数组的内存拜访,而不是整数乘法。而且,正如我将在示例 2 中解释的那样,硬件将为两个循环执行雷同的主内存拜访。

例 2: Impact of cache lines

让咱们更深刻地摸索这个例子。咱们将尝试其余步长值,而不仅仅是 1 和 16

以下是此循环针对不同步长(K)的运行工夫

请留神,尽管 step 在 1 到 16 的范畴内,但 for 循环的运行工夫简直没有变动。然而从 16 开始,每增加一倍,运行工夫就会减半

这背地的起因是明天的 CPU 不会逐字节拜访内存。相同,它们以(通常)64 字节的块(称为缓存行)获取内存。当您读取特定内存地位时,整个缓存即将从主内存中提取到缓存中。而且,从同一缓存行拜访其余值很便宜!

因为 16 个整数占用 64 字节(一个缓存行),步长在 1 到 16 之间的 for 循环必须接触雷同数量的缓存行:数组中的所有缓存行。然而一旦步长是 32,咱们将只接触大概每隔一个缓存行,而一旦它是 64,每四个。

理解缓存行对于某些类型的程序优化很重要。例如,数据的对齐能够确定操作是涉及一个还是两条高速缓存行。正如咱们在下面的例子中看到的,这很容易意味着在未对齐的状况下,操作会慢两倍

 注: 我本地跑了下,k 即便加到 8196, 运行工夫还是差不多 

例 3: L1 and L2 cache sizes

明天的计算机带有两级或三级缓存,通常称为 L1、L2 和 L3。如果您想晓得不同缓存的大小,能够应用 CoreInfo SysInternals 工具,或应用 GetLogicalProcessorInfo Windows API 调用。除了缓存大小外,这两种办法还会告诉您缓存行大小。

在我的机器上,CoreInfo 报告我有一个 32kB 的 L1 数据缓存、一个 32kB 的 L1 指令缓存和一个 4MB 的 L2 数据缓存。L1 缓存是每核的,L2 缓存在核查之间共享:

让咱们通过试验来验证这些数字。为此,咱们将遍历一个每 16 个整数递增的数组——一种批改每个缓存行的便宜办法。当咱们达到最初一个值时,咱们循环回到结尾。咱们将尝试应用不同的阵列大小,并且应该看到阵列溢出一个缓存级别后,阵列大小的性能降落。

这是程序

未完待续

正文完
 0