我正在阅读维基百科上关于引用的地方性的文章,我忍不住发现对Equifare Locality的解释相当晦涩。
我真的无法理解它,我想知道是否有人能尝试用通俗的英语解释它?
等距位置:位于空间位置和分支机构所在地。考虑循环访问等距模式,即时空坐标中的路径空格是一条虚线。在这种情况下,一个简单的线性函数可以预测在不久的将来将访问哪个位置。
"以等距模式访问位置的循环"意味着什么?这些位置之间的距离是否相等?
"时空坐标空间是一条虚线。"的垃圾是什么?这对我来说毫无意义。
如果有人能澄清Equifare locality应该是什么意思,那就太好了!
我认为最好用例子来解释这一点。这些局部性原则经常被用来优化事物。现代CPU中一个可能的组件是内存预取器,它会尝试猜测你将使用哪个内存,并在你需要的时候将其放入缓存。这在很大程度上依赖于局部性原理。
以一个数组为例,如果你这样做(c++示例):
#include <iostream>
#include <vector>
int main()
{
std::vector<int> test = { 1, 2, 3, 4, 5};
for(int& i: test)
{
std::cout << i << std::endl;
}
}
在向量(或其他语言中的数组)中,元素以固定的步长打包在一个连续的块中。因此,如果test
的第一个元素在地址X
,那么第二个元素将在X+Y
,第三个元素在X+2Y
,...
。因此,向量本身是空间局部性的一个非常基本的例子,更好的是,局部性是非常可预测的。其次,元素是在一个紧密的循环中访问的,所以我们也有很好的时间空间性。因为元素也是按顺序访问的,所以我们在"时空"中具有等距的空间性。这意味着,一旦CPU在查找中识别出X+Y, X+2Y, X+3Y
模式,它就可以开始在缓存中引入未来的元素。
您可以将其与以下内容进行对比:
#include <iostream>
#include <list>
int main()
{
std::list<int> test = { 1, 2, 3, 4, 5};
for(int& i: test)
{
std::cout << i << std::endl;
}
}
在链表中,元素相互引用,单个元素可以位于内存中的任何位置,因此您将失去空间位置。然而,你在一个循环中访问元素,所以你仍然有你的时间空间性。像这样的东西很难检测和优化预取(但并非不可能)。
最后,作为为什么组合时空空间性很重要的指标,考虑一下这个(有点人为的)例子:
#include <iostream>
#include <vector>
#include <algorithm>
#include <random>
#include <iterator>
int main()
{
std::vector<int> test = { 1, 2, 3, 4, 5 };
std::vector<unsigned int> indices = { 0, 1, 2, 3, 4 };
std::random_device rd;
std::shuffle(std::begin(indices), std::end(indices), std::mt19937 { rd() });
for (unsigned int& i : indices)
{
std::cout << test[i] << std::endl;
}
}
如果您纯粹查看test
容器,它再次具有良好的空间局部性(如第一个示例中那样大步前进)。如果您查看循环中的test
访问,您会发现查找中存在时间局部性。然而,在"时间空间"中,当你从数组的一部分跳到另一部分时,查找不是等距的,访问不是顺序的,因此在空间和时间上都没有等距的空间性。这几乎是不可能优化的。
访问等距模式中位置的循环
这很神秘,但如果我必须猜测的话,这意味着循环的所有迭代都具有相同级别的空间/时间局部性:如果循环只是在一个数组中迭代,那么在每次迭代中,我们访问位于前一个元素旁边的元素(因此空间局部性与上次迭代中相同),它和上次迭代一样"最近"(所以时间局部性也没有变化)。
因此,等距局部性对于每次迭代都是相同的。