9

众所周知,以一种跨步方式访问内存最有利于性能。

在以下情况下

  • 我必须访问一个内存区域才能阅读,
  • 我必须访问另一个区域进行写作,并且
  • 我只能以一种大步的方式访问这两个区域中的一个,

我应该更喜欢阅读步幅一还是写步幅一?

一个简单而具体的示例是类似 BLAS 的复制和置换操作,例如y := P x. 置换矩阵P完全由某个置换向量定义q(i)。它有一个对应的逆置换向量qinv(i)。可以将所需的循环编码为y[qinv(i)] = x[i]前者y[i]=x[q(i)]x步幅一读取而后者写入y步幅一的位置。

理想情况下,人们总是可以对这两种可能性进行编码,在有代表性的条件下对其进行分析,然后选择更快的版本。假设您只能编写一个版本 - 根据现代内存架构的行为,您总是期望哪种访问模式更快?在线程环境中工作会改变你的反应吗?

4

2 回答 2

7

访问模式,您将其命名为“writes stride one” ( y[i]=x[q(i)]),通常更快。

如果内存被缓存并且您的数据片段小于缓存行,则此访问模式需要更少的内存带宽。

现代处理器通常具有比存储单元更多的加载执行单元。下一个名为 Haswell 的英特尔架构仅支持 GATHER 指令,而 SCATTER 尚未在他们的计划中。这一切也有利于“写大步”的模式。

在线程环境中工作不会改变这一点。

于 2012-01-26T17:39:38.657 回答
2

我想分享我的简单基准测试的结果。假设我们有两个s的NxN方阵,A并且我们想要执行一个带有转置的复制:Bdouble

A = transpose(B)

算法:

  1. 两个嵌套循环,读取是连续的,写入是跨步的。
  2. 两个嵌套循环,读取是跨步的,写入是连续的。
  3. 顺序 MKL 的mkl_domatcopy.

没有转置的复制用作基线。的值N被认为是2^K + 1为了减轻缓存关联性影响。

带有 GCC 8.3.0 ( -O3 -m64 -march=native) 和英特尔 MKL 2019.0.1 的英特尔酷睿 i7-4770:

英特尔酷睿 i7-4770

带有 GCC 7.3.0 ( -O3 -m64 -march=native) 和英特尔 MKL 2017.0.1 的英特尔至强 E5-2650 v3:

英特尔至强 E5-2650 v3

数字和 C++ 源代码

于 2019-06-02T08:24:31.617 回答