【问题标题】:std::deque memory usage - Visual C++, and comparison to othersstd::deque 内存使用 - Visual C++,以及与其他人的比较
【发布时间】:2010-11-04 14:35:50
【问题描述】:

关注What the heque is going on with the memory overhead of std::deque?

Visual C++ 使用以下方法根据容器元素类型管理 deque 块:

#define _DEQUESIZ   (sizeof (value_type) <= 1 ? 16 \
    : sizeof (value_type) <= 2 ? 8 \
    : sizeof (value_type) <= 4 ? 4 \
    : sizeof (value_type) <= 8 ? 2 \
    : 1)    /* elements per block (a power of 2) */

这会导致小元素占用非常大的内存。通过将第一行中的 16 更改为 128,我能够大大减少大型 deque&lt;char&gt; 所需的占用空间。 Process Explorer Private Bytes 在 100m push_back(const char&amp; mychar) 调用后从 181MB -> 113MB 下降)。

  • 任何人都可以证明 那个#define
  • 其他的怎么办 编译器处理 deque 块 浆纱?
  • 他们的足迹是什么 (32位操作)为简单 测试 100m push_back 调用 deque&lt;char&gt;?
  • STL 是否允许 覆盖此块大小 编译时,无需修改 &lt;deque&gt; 代码?

【问题讨论】:

  • 你不能写一个自定义分配器来分配更大的块吗?
  • @Space_C0wb0y - 是的,尽管我认为这是不鼓励的(Meyers 等人)。
  • @Space_C0wb0y - 自定义分配器可能会有所帮助,但不能完全解决这个问题。尽管如此,每个双端队列块中都会有双端队列的变量,因此无论如何都会有开销。
  • @valdo - 不仅是每个块的信息,还有每个块的堆管理器信息。对于deque&lt;char&gt;,开销几乎与成员存储相同。他们为什么要这样做?
  • @Steve:“他们为什么要这样做?” - 我能想到的第一个答案是 MS/Dinkumware 并没有真正考虑在这方面优化 deque。从 GCC 源 @AProgrammer 引用中的评论来看,GNU 也没有,它只是拥有一个更适合大型 deques 的常量。创建 10m 双端队列,每个队列包含 10 个字符,突然间,Visual C++ 是天才,而 GCC 是拥有 5000% 开销的坏人。大概可以实现一个deque,从小块开始,然后增加块大小。

标签: c++ memory-management deque


【解决方案1】:

gcc 有

return __size < 512 ? size_t(512 / __size) : size_t(1);

有评论

/*  The '512' is
 *  tunable (and no other code needs to change), but no investigation has
 *  been done since inheriting the SGI code.
 */

【讨论】:

  • 你能扩展一下吗?即:这里__size是什么?
  • @Matthieu:它实际上是一个内联函数的参数,__deque_buf_size,但在任何地方调用它的参数都是sizeof(_Tp),乍一看似乎_Tp 始终是值双端队列的类型。
  • @Steve:谢谢,那么这意味着对于 GCC,块大小约为 512 字节(如果 sizeof(_Tp) &lt; 512 最多,则为对象大小)。有趣的是,这并没有随着缓存的变大而演变。当然,这个系统有更多潜在的浪费(即,如果你的双端队列中只有一个 char,你仍然有一个 512 字节块......)
【解决方案2】:

Dinkumware (MS) 实现希望将双端队列一次增加 16 个字节。难道这只是一个非常古老的实现(就像有史以来的第一个?),它针对内存很少的平台(按照今天的标准)进行了调整,以防止过度分配和耗尽内存(就像std::vector 一样)?

我必须在我正在开发的应用程序中实现自己的队列,因为 std::queue(使用 std::deque)的 2.5 倍内存占用是不可接受的。

互联网上似乎很少有证据表明人们遇到了这种低效率,这让我感到惊讶。我认为像队列(标准库,不少于)这样的基本数据结构将在野外非常普遍,并且将在性能/时间/空间关键应用程序中。但我们到了。

为了回答最后一个问题,C++ 标准没有定义修改块大小的接口。我很确定它不要求任何实现,只是两端插入/删除的复杂性要求。

【讨论】:

  • 一个有趣的猜想。 dinkumware.com/deque.txt 的 Dinkumware 代码实际上使用了它,并引用了古老的 VC++ 5.0。 #define _DEQUESIZ (4096
  • 有趣...好吧,我想这打破了我的理论,因为 current 实现已经使 _DEQUESIZ 变小了!
【解决方案3】:

STLPort

...seemsuse:

::: <stl/_alloc.h>
...
enum { _MAX_BYTES = 32 * sizeof(void*) };
...
::: <deque>
...
static size_t _S_buffer_size()
{
  const size_t blocksize = _MAX_BYTES;
  return (sizeof(_Tp) < blocksize ? (blocksize / sizeof(_Tp)) : 1);
}

这意味着 32 x 4 = 128 字节块大小在 32 位和 32 x 8 = 256 字节块大小在 64 位。

我的想法:从大小开销 POV 来看,我想任何实现都可以使用可变长度块进行操作,但我认为这很难满足deque 的恒定时间随机访问要求.

至于问题

STL 是否允许在编译时覆盖此块大小而不修改代码?

这里也不可能。

Apache

(似乎是 Rogue Wave STL 版本)显然使用:

static size_type _C_bufsize () {
    // deque only uses __rw_new_capacity to retrieve the minimum
    // allocation amount; this may be specialized to provide a
    // customized minimum amount
    typedef deque<_TypeT, _Allocator> _RWDeque;
    return _RWSTD_NEW_CAPACITY (_RWDeque, (const _RWDeque*)0, 0);
}

所以似乎有某种机制通过特化覆盖块大小,而 ... 的定义如下所示:

// returns a suggested new capacity for a container needing more space
template <class _Container>
inline _RWSTD_CONTAINER_SIZE_TYPE
__rw_new_capacity (_RWSTD_CONTAINER_SIZE_TYPE __size, const _Container*)
{
    typedef _RWSTD_CONTAINER_SIZE_TYPE _RWSizeT;

    const _RWSizeT __ratio = _RWSizeT (  (_RWSTD_NEW_CAPACITY_RATIO << 10)
                                       / _RWSTD_RATIO_DIVIDER);

    const _RWSizeT __cap =   (__size >> 10) * __ratio
                           + (((__size & 0x3ff) * __ratio) >> 10);

    return (__size += _RWSTD_MINIMUM_NEW_CAPACITY) > __cap ? __size : __cap;
}

所以我想说,嗯,很复杂。

(如果有人想进一步了解这一点,请随时直接编辑我的答案或发表评论。)

【讨论】:

    猜你喜欢
    • 2011-09-24
    • 2014-05-09
    • 1970-01-01
    • 2017-10-17
    • 1970-01-01
    • 2021-01-04
    • 1970-01-01
    • 2012-01-24
    • 2014-04-23
    相关资源
    最近更新 更多