C ++中的可变长度数组开销?

Posted

技术标签:

【中文标题】C ++中的可变长度数组开销?【英文标题】:Variable Length Array overhead in C++? 【发布时间】:2011-05-19 15:14:03 【问题描述】:

看到这个问题:Why does a C/C++ compiler need know the size of an array at compile time ? 我想到编译器实现者现在应该有一些时间来涉足(这是 C99 标准的一部分,那是 10 年前的事)并提供高效的实现。

但是,它似乎(从答案中)仍然被认为是昂贵的。

这让我有些吃惊。

当然,我知道静态偏移在性能方面比动态偏移要好得多,并且与一个建议不同,我实际上不会让编译器执行数组的堆分配,因为这可能会花费更多 [这还没有被测量;)]

但我仍然对假定的成本感到惊讶:

据我所知,如果函数中没有 VLA,那么就不会产生任何成本。 如果只有一个 VLA,则可以将它放在所有变量之前或之后,因此可以为大多数堆栈帧获得静态偏移量(或者在我看来,但我并不精通在堆栈管理中)

当然会出现多个 VLA 的问题,我想知道有一个专用的 VLA 堆栈是否可行。这意味着 VLA 将由一个计数和一个指针(因此具有已知大小)表示,并且在辅助堆栈中占用的实际内存仅用于此目的(因此实际上也是一个堆栈)。

[改写]

在 gcc / VC++ 中 VLA 是如何实现的?

成本真的那么高吗?

[结束改写]

在我看来,它只能比使用vector 更好,即使是目前的实现,因为您不会产生动态分配的成本(以不可调整大小为代价)。

编辑:

有部分回复here,但是将 VLA 与传统数组进行比较似乎不公平。如果我们事先知道尺寸,那么我们就不需要 VLA。在同一个问题中,AndreyT 给出了一些关于实现的指示,但并不像我想要的那么精确。

【问题讨论】:

@Matthieu M. 已删除。我一定在想别的事。 @Matthieu:你的想法对我来说似乎是合理的...... VLA 仅在超过 1 个时才建议开销(只需将其放在“已知大小的元素之后”,然后可以是已知偏移堆栈中的指针或额外调整,以指示后续 VLA 从何处开始。但根本看不到第二个堆栈有帮助。 @Tony:我想知道堆栈是如何实现的,如果实现意味着只有当前堆栈的顶部是已知的,那么您似乎有一个动态偏移计算,除非您使用第二个堆栈来存储 VLA 元素。如果您知道当前帧的顶部和底部,那么对于单个 VLA 元素来说很容易。无论如何,我只想知道它是如何完成的(目前)以及“成本”是多少。 问“为什么是功能 A 而不是功能 B?”在设计特定语言时通常没有用处。答案通常是主观的和不知情的。关于如何在 C 中实现 VLA 的问题将是客观的,并且可以得到令人满意的回答。 据我所知,Visual C++ 不支持 VLA。 【参考方案1】:

VLA 在 gcc / VC++ 中是如何实现的?

AFAIK VC++ 没有实现 VLA。它是一个 C++ 编译器,仅支持 C89(无 VLA,无限制)。我不知道 gcc 如何实现 VLA,但最快的方法是将指向 VLA 的指针及其大小存储在堆栈帧的静态部分中。这样,您可以访问具有恒定大小数组性能的 VLA 之一(如果堆栈像 x86 中那样向下增长,则它是最后一个 VLA(取消引用 [堆栈指针 + 索引 * 元素大小 + 最后临时推送的大小]),以及第一个 VLA(如果它向上增长)(取消引用 [stackframe 指针 + 与 stackframe 的偏移量 + 索引*元素大小]))。所有其他 VLA 将需要再进行一次间接操作才能从堆栈的静态部分获取它们的基地址。

[ 编辑: 同样,当使用 VLA 时,编译器不能省略 stack-frame-base 指针,否则这是多余的,因为可以在编译时计算堆栈指针的所有偏移量。这样您就少了一个免费注册机。 — 结束编辑 ]

成本真的那么高吗?

不是真的。而且,如果你不使用它,你就不用付钱。

[ 编辑: 可能更正确的答案是:与什么相比?与堆分配的向量相比,访问时间相同,但分配和释放会更快。 — 结束编辑 ]

【讨论】:

MSVC++ 确实支持限制(尽管拼写为__restrict)。 谢谢,我以为 VC++ 支持它们,我的错。对于提议的实现,似乎访问局部变量也会产生成本(即偏移的动态计算),还是我弄错了? @Matthieu:当然有,它会花费你一个间接性:[stack-frame-base-pointer + local-variable-offset]。但它没有连接到 VLA。关键是对于 VLA,您需要 两个 间接。 我应该澄清一下,对不起,我对访问 VLA 元素的成本并不感兴趣,我猜它介于静态数组和向量之间。由于存在 VLA,我对访问其他局部变量的成本增加更感兴趣。那么问题是:使用不可调整大小的向量是否会更快,因为其他局部变量具有“固定”位置?还是这种退化的情况,如果我有选择,我最好使用 VLA(这将是在编译器中实现它们的一个参数)。 @Matthieu:不,因为编译器可以将它们放在堆栈帧的静态部分。【参考方案2】:

如果要在 VC++ 中实现,我会假设编译器团队会使用 _alloca(size) 的一些变体。而且我觉得代价相当于在栈上使用大于8字节对齐的变量(比如__m128);编译器必须将原始堆栈指针存储在某处,对齐堆栈需要一个额外的寄存器来存储未对齐的堆栈。

因此开销基本上是额外的间接(您必须将 VLA 的地址存储在某处)和由于在某处存储原始堆栈范围而导致的寄存器压力。

【讨论】:

以上是关于C ++中的可变长度数组开销?的主要内容,如果未能解决你的问题,请参考以下文章

C 和 C++ 中的可变长度数组 (VLA)

C语言中,对于不知道长度的数组怎样输入。。。

C语言中,对于不知道长度的数组怎样输入。。。

派生类型中的可变长度数组

JavaScript中的可变长度数组数组

数组的长度(c写法)