使用C ++访问过度分配的内存

时间:2015-09-03 06:51:29

标签: c++ arrays

我有一棵巨大的树,可能需要几千兆字节。节点结构如下。您会注意到我将最后一个成员设置为大小为1的数组。原因是我可以过度分配具有灵活大小的Node。类似于C本身作为灵活阵列成员支持的内容。我可以改用std::unique_ptr<T[]>std::vector<T>,但问题是每个树节点都有双重动态分配,双重间接和额外的高速缓存未命中。在我的上一次测试中,这使我的程序花费了大约50%的时间,这对我的应用程序来说是不可接受的。

template<typename T>
class Node
{
public:
  Node<T> *parent;
  Node<T> *child;

  /* ... */

  T &operator[](int);
private;
  int size;
  T array[1];
};

实现operator[]的最简单方法就是这样做。

template<typename T>
T &Node::operator[](int n)
{
  return array[n];
}

它应该在大多数理智的C ++实现中正常工作。但是,由于C ++标准允许疯狂的实现进行数组边界检查,因为我知道这在技术上调用了未定义的行为。那我可以这样做吗?

template<typename T>
T &Node::operator[](int n)
{
  return (&array[0])[n];
}
我在这里有点困惑。原始类型的[]运算符只是*的语法糖。因此,(&array[0])[n]相当于(&*(array + 0))[n],我认为可以将其清除为array[n],使所有内容与第一个相同。好的,但我仍然可以这样做。

template<typename T>
T &Node::operator[](int n)
{
  return *(reinterpret_cast<T *>(reinterpret_cast<char *>(this) + offsetof(Node<T>, array)) + n);
}

我希望我现在摆脱可能未定义的行为。也许内联汇编会更好地展示我的意图。但我真的必须走这么远吗?有人可以向我澄清事情吗?

顺便说一句T始终是POD类型。整个Node也是POD。

3 个答案:

答案 0 :(得分:1)

“越界”数组访问的主要问题是没有任何对象存在。这不是导致问题的越界索引本身。 现在在你的情况下,可能是预定位置的原始内存。这意味着你实际上可以通过赋值在那里创建一个POD对象。任何后续的读取访问都将在那里找到对象。

根本原因是C实际上没有数组边界。 a[n]只是*(a+n)按照定义。所以前两个提议的表格已经相同了。

我会更担心T array[1]背后的任何填充,您将作为array[1]的一部分访问。

答案 1 :(得分:1)

首先,一个实现可以自由地重新排序所有但很简单的情况下的类成员。您的案例并不简单,因为它具有访问说明符。除非你让你的POD类,或者它在C ++ 11中调用的任何东西(平凡的布局?),你不能保证你的数组实际上是最后布局的。

当然,C ++中不存在灵活成员。

然而,一切都没有丢失。分配一大块内存足够容纳你的类和你的数组,然后在开头放置你的类,并解释对象后面的部分(以及任何paddibg以确保正确对齐)作为数组。

如果您有this,则可以使用

访问该阵列
reinterpret_cast<T*>(
 reinterpret_cast<char*>(this) +
 sizeof(*this) + padding))

选择adfing以使sizeof(T)除以sizeof(*this) + padding

获取灵感,请查看std :: make_shared`。它还将两个对象打包到一个已分配的内存块中。

答案 2 :(得分:0)

您还想知道是否有替代方法。鉴于您最近关于“无重新分配”的评论,我将数组数据存储为指向堆分配存储的指针,但是:

树具有可预测的访问模式,从根到儿童。因此,我有一个Node::operator new并确保子节点直接在其父节点之后分配。这为您在走树时提供了参考位置。其次,我有另一个数组数据的分配器,并为父数组和它们的第一个子进程返回连续的内存(当然后面是它的第一个孙子)。

结果是节点及其数组之间没有引用的局部性,而是获得树图和相关数组数据的引用局部性。

阵列数据分配器很可能是树的简单池分配器。只需一次分配256 KB块,并一次将它们分成几个整数。您需要跟踪的整个状态是您已经分配了多少256 kB。这比std::vector<T, std::allocator>快得多,因为只要树生存,它就无法知道记忆的存在。