共计 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 个整数递增的数组——一种批改每个缓存行的便宜办法。当咱们达到最初一个值时,咱们循环回到结尾。咱们将尝试应用不同的阵列大小,并且应该看到阵列溢出一个缓存级别后,阵列大小的性能降落。
这是程序
未完待续