cache 在X86和ARM的性能比较 - 矩阵累加和分块矩阵乘法

有一段时间在x86和arm服务器下面做开发,需要平台之间的移植,然后经常发现同一段代码在不同平台下面的表现不一样,有一大部分原因是不同平台对cache处理方法不一样。

大部分参考资料上说,cache有时间和空间的局部性,空间的局部性是指未来要用到的信息会和当前的信息靠在一起,即a[i] 会和a[i+1]在同一条cache line上, 顺序存储。时间的局部性是指未来访问的信息很有可能是现在访问的信息, 累加或者循环结构。

就空间局部性来说,如果访问某个变量时,它在cache 里面,称cache hit, 否则cache miss. cache 一般分l1, l2, l3 三层,每层发生cache miss的时候, 大概要花几个cycles 到几百个cycles不等。

下面的例子就是来探讨x86/ARM cache.

例子A: 矩阵累加

sum_matrix: 先row 在column

sum_matrix1: 先column在row

Intel(R) Xeon(R) CPU E5-2680: 

icc -O3 test_sum.c -o test_sum

 差别很小

In Arm Cortex A72:

aarch64-linux-gnu-gcc -O3 test_sum_trail1.c -o test_sum

跑出来的结果:

 结果非常明显,顺序读的效果比跳读要快一倍。

例子二: 矩阵乘法 - 分块

 

Intel(R) Xeon(R) CPU E5-2680: 

icc -O3 test_block_matrix.c -o test_block_matrix

没有效果。修改一下程序,把block size作为一个输入

完全没效果^_^_^_^_^_^

In Arm Cortex A72:

aarch64-linux-gnu-gcc -O3 test_block_matrix.c -o test_block_matrix

太夸张了,5 倍的性能提升。

Cpu l1d 32 Kbytes, 把分块矩阵的大小设置为64 * 64, 那么所有的矩阵的大小 64 * 64 * 3(A, B, C)* 4 (sizeof(int)) = 48 K, 我们看看效果,

把分块矩阵的大小设置为8*8似乎是最合适的,并非分块矩阵占满整个l1 cache才算。

总结:

随着自己对cpu的认知越来越深,我也尝试着用一些自己的理解去解释上面的现象, 比如,我通过查手册,看到,arm cpu一次性可以load 6 个cache line, 那么我就想x86是不是多于这个呢,结果是4个(不太确定):

 也许和cpu的硬件预取功能有关,x86的预取效果好,正如x86的分支预测做的比arm的好。。。

或许x86的并行性做的比arm好。。。。。

Anyway, 对于x86的开发者,可以更加关注应用层,而非底层cpu的运行逻辑了,而对于arm开发者在开发软件时,一定要把cache的局部性放在心里。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您解答这个问题。 首先,我们需要明确什么是一级缓存和二级缓存。一级缓存是CPU内部的缓存,速度非常快但容量较小,一般为几十KB到几百KB;二级缓存则是位于CPU和内存之间的缓存,速度比内存快但比一级缓存慢,容量一般为几百KB到几十MB。 在使用openblas计算矩阵乘法时,我们可以通过调整矩阵的大小和分块的大小来利用一级缓存和二级缓存,从而优化计算性能。 具体来说,当矩阵较小时,可以将其完全存储在一级缓存中,以利用一级缓存的高速读写;当矩阵较大时,可以将其分块存储在二级缓存中,以利用二级缓存的高速读写和较大的容量。 下面是一个使用openblas计算矩阵乘法并划分一级缓存和二级缓存的示例代码: ```python import numpy as np import time # 定义矩阵大小和块大小 n = 2048 k = 512 # 生成随机矩阵 A = np.random.rand(n, k) B = np.random.rand(k, n) C = np.zeros((n, n)) # 利用一级缓存计算矩阵乘法 start = time.time() for i in range(n): for j in range(n): for l in range(k): C[i][j] += A[i][l] * B[l][j] end = time.time() print("Using L1 cache:", end - start) # 利用二级缓存计算矩阵乘法 C = np.zeros((n, n)) start = time.time() for i0 in range(0, n, k): for j0 in range(0, n, k): for l0 in range(0, k, k//2): i1 = min(i0+k, n) j1 = min(j0+k, n) l1 = min(l0+k//2, k) for i in range(i0, i1): for j in range(j0, j1): for l in range(l0, l1): C[i][j] += A[i][l] * B[l][j] end = time.time() print("Using L2 cache:", end - start) ``` 在上面的代码中,我们首先生成了两个随机矩阵A和B,然后分别使用一级缓存和二级缓存计算矩阵乘法,并计算两种方法的执行时间。在使用二级缓存计算矩阵乘法时,我们采用了分块的方法,将矩阵A和B分成多个块,每个块的大小为k,然后分别计算每个块之间的乘积。这样可以将矩阵的访问局部化,减少缓存的失效率,从而提高计算性能。 需要注意的是,矩阵的大小和块的大小需要根据具体的硬件环境和应用场景进行调整,以达到最优的计算性能

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值