分配/访问2d阵列,使得2d子块是连续的

Allocating/accessing a 2d array such that 2d sub-blocks are contiguous

本文关键字:2d 连续 访问 阵列 分配 使得      更新时间:2023-10-16

我有一个2d数组SomeData* data[4096][4096]。这里,数据沿着最后一个坐标是连续的,因此由于内存的局部性,在y坐标上迭代比在x坐标上迭代更快。然而,我的访问模式是,我查看一个条目,然后在两个坐标中查看附近的邻居,即数据[x][y]以及数据[x-1][y-1]、数据[x+1][y+1]等。

如果我可以分配这个数组,使小的2d子块在内存中是连续的,这将加快速度。

我说分配,但我怀疑这里的解决方案是正常分配一个2d数组,然后用索引做一些技巧,这样我就可以连续访问2d块。换句话说,我想要一些转换坐标的查找函数,但我不能立即看到它应该是什么

SomeData* data[4096][4096];
SomeData* lookup(size_t x, size_t y) {
//??? Some logic to translate x,y such that 2d blocks are accessed contiguously.
}

保证数据数组的两个维度都是二的幂。

假设我们有一个nxm网格。我们想将网格细分为bxb块。n%b=0和m%b=0是必要的。

让我们调用总体索引I=0,。。。,n-1和J=0,。。。,m-1和块i=0、…、…中的索引,。。。,b-1和j=0,。。。,b-1.

我试着在这里勾画布局。

给定I,块的列索引为(I/b(,块内索引I=I%b。块的行索引为(J/b(,块内索引J=J%b。

每个完整的块包含b*b元素。因此,整行块包含(n/b(bb=n*b个元素。

把元素(I,J(的线性指数加在一起就是:

  • (I%b([元素前面块中的列]
  • +(J%b(*b[元素前面的块中的行]
  • +(I/b(*b*b[元素块前面的块列]
  • +(J/b(*n*b[元素块之前的块行]

运行时大小的阻塞网格类的粗略草图:

template <typename T>
class Block_Grid
{
public:
Block_Grid(std::size_t n, std::size_t m, std::size_t b)
: _n(n), _m(m), _b(b), _elements(n * m)
{ 
assert(n % b == 0);
assert(m % b == 0);
}
T & operator()(std::size_t i, std::size_t j)
{
return _elements[index(i, j)];
}
protected:
private:
std::size_t index(int i, int j) const
{
return (i % b) 
+ (j % b) * b
+ (i / b) * b * b
+ (j / b) * n * b;
}
std::size_t _n;
std::size_t _m;
std::size_t _b;
std::vector<T> _elements;
};

或编译时大小的类

template <typename T, std::size_t N, std::size_t M, std::size_t B>
class Block_Grid
{
static_assert(N % B == 0);
static_assert(M % B == 0);
public:
Block_Grid() = default;
T & operator()(std::size_t i, std::size_t j)
{
return _elements[index(i, j)];
}
protected:
private:
std::size_t index(std::size_t i, std::size_t j) const
{
return (i % B) + (j % B) * B + (i / B) * B * B + (j / B) * N * B;
}
std::array<T, N * M> _elements;
};