使用可变长度数组有任何开销吗?

发布于 2024-08-17 11:39:45 字数 65 浏览 8 评论 0原文

使用可变长度数组有一些开销吗?数组的大小可以在运行时通过命令行参数传递吗?与自动和动态分配数组相比,为什么要引入它?

Is there some overhead of using variable-length arrays? Could the size of array be passed via command line argument at run time? Why is it introduced, compared to automatic and dynamically allocating an array?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

败给现实 2024-08-24 11:39:45

VLA 确实有一些开销(与“普通”命名的编译时大小的数组相比)。

首先,它具有运行时长度,而且该语言为您提供了在运行时获取数组实际大小的方法(使用 sizeof)。这立即意味着数组的实际大小必须存储在某个地方。这会导致每个阵列的内存开销微不足道。然而,由于 VLA 只能声明为自动对象,因此任何人都不会注意到这种内存开销。这就像声明一个额外的整型局部变量一样。

其次,VLA通常分配在堆栈上,但由于其大小可变,一般情况下在编译时无法知道其在内存中的确切位置。因此,底层实现通常必须将其实现为指向内存块的指针。这引入了一些额外的内存开销(对于指针),由于上述原因,这也是完全微不足道的。这也带来了轻微的性能开销,因为我们必须读取指针值才能找到实际的数组。这与访问 malloc 编辑的数组时获得的开销相同(并且不会使用命名的编译时大小的数组)。

由于 VLA 的大小是运行时整数值,因此它当然可以作为命令行参数传递。 VLA 并不关心它的大小从何而来。

VLA 是作为运行时大小的数组引入的,具有较低的分配/释放成本。它们介于“普通”命名的编译时大小的数组(分配-释放成本几乎为零,但大小固定)和 malloc 数组(具有运行时大小,但相对较高)之间。分配-解除分配成本)。

VLA [几乎]遵循与自动(即本地)对象相同的范围相关生命周期规则,这意味着在一般情况下它们不能替换 malloc-ed 数组。它们的适用性仅限于需要具有典型自动生命周期的快速运行时大小的数组的情况。

VLA does have some overhead (compared to "ordinary" named compile-time-sized array).

Firstly, it has run-time length and yet the language provides you with means to obtain the actual size of the array at run-time (using sizeof). This immediately means that the actual size of the array has to be stored somewhere. This results in some insignificant per-array memory overhead. However, since VLAs can only be declared as automatic objects, this memory overhead is not something anyone would ever notice. It is just like declaring an extra local variable of integral type.

Secondly, VLA is normally allocated on stack, but because of its variable size, in general case its exact location in memory is not known at compile time. For this reason the underlying implementation usually has to implement it as a pointer to a memory block. This introduces some additional memory overhead (for the pointer), which is again completely insignificant for the reasons described above. This also introduces slight performance overhead, since we have to read the pointer value in order to find the actual array. This is the same overhead you get when accessing malloc-ed arrays (and don't get with the named compile-time-sized arrays).

Since the size of the VLA is a run-time integer value, it can, of course, be passed as a command-line argument. VLA doesn't care where its size comes from.

VLA were introduced as run-time-sized arrays with low allocation/deallocation cost. They fit between "ordinary" named compile-time-sized arrays (which have virtually zero allocation-deallocation cost, but fixed size) and malloc-ed arrays (which have run-time size, but relatively high allocation-deallocation cost).

VLA obey [almost] the same scope-dependent lifetime rules as automatic (i.e local) objects, which means that in general case they can't replace malloc-ed arrays. They applicability is limited to situations when you need a quick run-time sized array with a typical automatic lifetime.

可爱咩 2024-08-24 11:39:45

可变长度数组会产生一些运行时开销,但您必须非常努力地测量它。请注意,如果 vla 是可变长度数组,则 sizeof(vla) 不是编译时常量。

数组的大小可以在运行时传递给函数。如果您选择从命令行参数获取大小并将其转换为整数并在运行时将其传递给函数,那就这样吧——它会起作用。

使用可变长度数组是因为变量会自动分配到正确的大小,并在函数退出时自动释放。这可以避免过度分配空间(当您主要使用最小大小时,为最大可能大小分配足够的空间),并避免内存清理问题。

此外,对于多维数组,AFAIK 它的行为更像 Fortran - 您可以动态配置所有维度,而不是为除数组的前导维度之外的所有维度坚持固定大小。


VLA 的一些运行时开销的具体证据 - 至少对于 SPARC (Solaris 10) 上的 GCC 4.4.2 而言。

考虑下面的两个文件:

vla.c - 使用可变长度数组

#include <assert.h>
#include <stddef.h>
extern size_t identity_matrix(int n, int m);

size_t identity_matrix(int n, int m)
{
    int vla[n][m];
    int i, j;
    assert(n > 0 && n <= 32);
    assert(m > 0 && m <= 32);
    for (i = 0; i < n; i++)
    {
        for (j = 0; j < m; j++)
        {
            vla[i][j] = 0;
        }
        vla[i][i] = 1;
    }
    return(sizeof(vla));
}

fla.c - 使用固定长度数组

#include <assert.h>
#include <stddef.h>
extern size_t identity_matrix(int n, int m);

size_t identity_matrix(int n, int m)
{
    int fla[32][32];
    int i, j;
    assert(n > 0 && n <= 32);
    assert(m > 0 && m <= 32);
    for (i = 0; i < n; i++)
    {
        for (j = 0; j < m; j++)
        {
            fla[i][j] = 0;
        }
        fla[i][i] = 1;
    }
    return(sizeof(fla));
}

编译和目标文件大小

出于比较目的,本地数组的名称不同(vla 与 fla),并且数组的尺寸在声明时不同 - 否则,文件是相同的。

我编译使用:

$ gcc -O2 -c -std=c99 fla.c vla.c

目标文件大小有些不同 - 通过“ls”和“size”来测量:

$ ls -l fla.o vla.o
-rw-r--r--   1 jleffler rd          1036 Jan  9 12:13 fla.o
-rw-r--r--   1 jleffler rd          1176 Jan  9 12:13 vla.o
$ size fla.o vla.o
fla.o: 530 + 0 + 0 = 530
vla.o: 670 + 0 + 0 = 670

我没有进行广泛的测试来查看有多少开销是固定的,有多少是可变的,但是有使用 VLA 的开销。

There is some run-time overhead with variable-length arrays, but you would have to be working fairly hard to measure it. Note that sizeof(vla) is not a compile-time constant if vla is a variable-length array.

The size of the array can be passed to a function at run-time. If you choose to take the size from a command line argument and convert that into an integer and pass that to the function at run-time, so be it -- it will work.

Variable-length arrays are used because the variables are automatically allocated to the correct size and automatically freed on exit from the function. This avoids over-allocating space (allocating enough space for the maximum possible size when you mostly work with minimal sizes), and avoids problems with memory clean up.

Additionally, with multi-dimensional arrays, AFAIK it behaves more like Fortran - you can dynamically configure all the dimensions, rather than being stuck with fixed sizes for all but the leading dimension of the array.


Concrete evidence of some run-time overhead for VLA - at least with GCC 4.4.2 on SPARC (Solaris 10).

Consider the two files below:

vla.c - using a variable-length array

#include <assert.h>
#include <stddef.h>
extern size_t identity_matrix(int n, int m);

size_t identity_matrix(int n, int m)
{
    int vla[n][m];
    int i, j;
    assert(n > 0 && n <= 32);
    assert(m > 0 && m <= 32);
    for (i = 0; i < n; i++)
    {
        for (j = 0; j < m; j++)
        {
            vla[i][j] = 0;
        }
        vla[i][i] = 1;
    }
    return(sizeof(vla));
}

fla.c - using a fixed-length array

#include <assert.h>
#include <stddef.h>
extern size_t identity_matrix(int n, int m);

size_t identity_matrix(int n, int m)
{
    int fla[32][32];
    int i, j;
    assert(n > 0 && n <= 32);
    assert(m > 0 && m <= 32);
    for (i = 0; i < n; i++)
    {
        for (j = 0; j < m; j++)
        {
            fla[i][j] = 0;
        }
        fla[i][i] = 1;
    }
    return(sizeof(fla));
}

Compilation and object file sizes

For comparison purposes, the names of the local array are different (vla vs fla), and the dimensions on the array are different when it is declared - otherwise, the files are the same.

I compiled using:

$ gcc -O2 -c -std=c99 fla.c vla.c

The object file sizes are somewhat different - as measured both by 'ls' and by 'size':

$ ls -l fla.o vla.o
-rw-r--r--   1 jleffler rd          1036 Jan  9 12:13 fla.o
-rw-r--r--   1 jleffler rd          1176 Jan  9 12:13 vla.o
$ size fla.o vla.o
fla.o: 530 + 0 + 0 = 530
vla.o: 670 + 0 + 0 = 670

I've not done extensive testing to see how much of the overhead is fixed and how much is variable, but there is overhead in using a VLA.

南街九尾狐 2024-08-24 11:39:45

我只是想知道使用可变长度数组是否有一些开销?

没有

数组的大小可以在运行时通过命令行参数传递吗?

是的。

与自动和动态分配数组相比,为什么要引入它?

自动分配仅允许在编译时已知的固定大小。

动态分配(malloc)会将数组存储在上,堆内存空间较大,但访问速度较慢。

VLA 的工作原理是将数组放入堆栈中。这使得分配和访问速度非常快,但是堆栈通常很小(几KB),当VLA溢出堆栈时,它与无限递归没有区别。

I just wonder if there is some overhead of using variable-length arrays?

Nope

Can the size of array could be passed via command line argument at run time?

Yes.

Why is it introduced, compared to automatic and dynamically allocating an array?

Automatic allocated only allows a fixed size known at compile time.

Dynamically allocating (malloc) will store the array on the heap, which has a large memory space, but is slower to access.

VLA works by placing the array in the stack. This makes allocation and access extremely fast, but the stack is usually small (of a few KB), and when the VLA overflowed the stack, it's indistinguishable from an infinite recursion.

煞人兵器 2024-08-24 11:39:45

VLA 的开销应该很小(最多应该导致堆栈指针的增加)。动态分配需要手动内存管理,并且比 VLA 的基于堆栈的分配慢,并且数组的“自动”声明需要数组大小的编译时表达式。但是,请记住,如果发生堆栈溢出,将导致未定义的行为,因此请保持 VLA 相对较小。

您可以通过命令行参数传递数组的大小,但您必须自己编写代码来处理它。

There should be very little overhead for VLAs (At most it should result in an addition to the stack pointer). Dynamic allocation requires manual memory management and is slower than stack-based allocation of a VLA, and "automatic" declaration of an array requires a compile-time expression for the array size. However, keep in mind that if a stack overflow occurs, it will cause undefined behavior, so keep VLAs relatively small.

You could pass the size of an array via a command-line argument, but you would have to write the code to handle that yourself.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文